From b3de011f54064048722c67d9fa1a35e4141cf4bc Mon Sep 17 00:00:00 2001 From: swcompiler Date: Fri, 6 Jun 2025 11:11:53 +0800 Subject: [PATCH] add sw64 support --- add-sw64-support-nodejs-v18.20.2.patch | 36085 +++++++++++++++++++++++ nodejs.spec | 6 +- 2 files changed, 36090 insertions(+), 1 deletion(-) create mode 100644 add-sw64-support-nodejs-v18.20.2.patch diff --git a/add-sw64-support-nodejs-v18.20.2.patch b/add-sw64-support-nodejs-v18.20.2.patch new file mode 100644 index 0000000..2ae168f --- /dev/null +++ b/add-sw64-support-nodejs-v18.20.2.patch @@ -0,0 +1,36085 @@ +From 604d7b9044ca9ad9b9b90fdddd39504172dc0349 Mon Sep 17 00:00:00 2001 +From: chenmengxue +Date: Tue, 27 May 2025 10:33:46 +0800 +Subject: [PATCH 2/2] [sw64] add sw branch and files + +--- + configure.py | 30 +- + .../source/i18n/double-conversion-utils.h | 2 +- + deps/v8/BUILD.gn | 44 + + deps/v8/include/v8-unwinder-state.h | 2 +- + deps/v8/infra/mb/mb_config.pyl | 30 + + deps/v8/src/base/build_config.h | 14 +- + deps/v8/src/base/cpu.cc | 20 +- + deps/v8/src/base/cpu.h | 6 + + deps/v8/src/base/platform/platform-posix.cc | 5 + + deps/v8/src/baseline/baseline-assembler-inl.h | 2 + + deps/v8/src/baseline/baseline-compiler.cc | 2 + + .../sw64/baseline-assembler-sw64-inl.h | 525 ++ + .../sw64/baseline-compiler-sw64-inl.h | 78 + + .../builtins-sharedarraybuffer-gen.cc | 10 +- + deps/v8/src/builtins/builtins.cc | 2 +- + deps/v8/src/builtins/sw64/builtins-sw64.cc | 3783 +++++++++++ + deps/v8/src/codegen/assembler-arch.h | 2 + + deps/v8/src/codegen/assembler-inl.h | 2 + + deps/v8/src/codegen/constants-arch.h | 2 + + deps/v8/src/codegen/cpu-features.h | 7 + + deps/v8/src/codegen/external-reference.cc | 97 + + deps/v8/src/codegen/external-reference.h | 8 + + .../src/codegen/interface-descriptors-inl.h | 8 +- + deps/v8/src/codegen/macro-assembler.h | 3 + + deps/v8/src/codegen/register-arch.h | 2 + + deps/v8/src/codegen/register-configuration.cc | 2 + + deps/v8/src/codegen/reglist.h | 2 + + deps/v8/src/codegen/reloc-info.cc | 2 +- + deps/v8/src/codegen/sw64/assembler-sw64-inl.h | 291 + + deps/v8/src/codegen/sw64/assembler-sw64.cc | 3881 ++++++++++++ + deps/v8/src/codegen/sw64/assembler-sw64.h | 1519 +++++ + deps/v8/src/codegen/sw64/constants-sw64.cc | 143 + + deps/v8/src/codegen/sw64/constants-sw64.h | 1625 +++++ + deps/v8/src/codegen/sw64/cpu-sw64.cc | 40 + + .../sw64/interface-descriptors-sw64-inl.h | 320 + + .../src/codegen/sw64/macro-assembler-sw64.cc | 5575 +++++++++++++++++ + .../src/codegen/sw64/macro-assembler-sw64.h | 1248 ++++ + deps/v8/src/codegen/sw64/register-sw64.h | 302 + + deps/v8/src/codegen/sw64/reglist-sw64.h | 47 + + deps/v8/src/common/globals.h | 3 + + .../src/compiler/backend/instruction-codes.h | 2 + + .../compiler/backend/instruction-selector.cc | 8 +- + .../backend/sw64/code-generator-sw64.cc | 2918 +++++++++ + .../backend/sw64/instruction-codes-sw64.h | 420 ++ + .../sw64/instruction-scheduler-sw64.cc | 1269 ++++ + .../backend/sw64/instruction-selector-sw64.cc | 3241 ++++++++++ + deps/v8/src/compiler/c-linkage.cc | 8 + + deps/v8/src/debug/debug-evaluate.cc | 2 +- + .../src/deoptimizer/sw64/deoptimizer-sw64.cc | 34 + + deps/v8/src/diagnostics/gdb-jit.cc | 2 + + deps/v8/src/diagnostics/perf-jit.h | 3 + + deps/v8/src/diagnostics/sw64/disasm-sw64.cc | 1598 +++++ + deps/v8/src/diagnostics/sw64/unwinder-sw64.cc | 14 + + deps/v8/src/execution/clobber-registers.cc | 5 + + deps/v8/src/execution/frame-constants.h | 2 + + deps/v8/src/execution/simulator-base.h | 2 +- + deps/v8/src/execution/simulator.h | 2 + + .../execution/sw64/frame-constants-sw64.cc | 34 + + .../src/execution/sw64/frame-constants-sw64.h | 83 + + deps/v8/src/execution/sw64/simulator-sw64.cc | 11 + + deps/v8/src/execution/sw64/simulator-sw64.h | 39 + + deps/v8/src/flags/flag-definitions.h | 39 +- + .../heap/base/asm/sw64/push_registers_asm.cc | 47 + + .../src/interpreter/interpreter-assembler.cc | 4 +- + deps/v8/src/libsampler/sampler.cc | 4 + + deps/v8/src/logging/log.cc | 2 + + deps/v8/src/objects/code.cc | 3 +- + deps/v8/src/objects/code.h | 2 + + .../src/regexp/regexp-macro-assembler-arch.h | 2 + + deps/v8/src/regexp/regexp-macro-assembler.h | 1 + + deps/v8/src/regexp/regexp.cc | 3 + + .../sw64/regexp-macro-assembler-sw64.cc | 1383 ++++ + .../regexp/sw64/regexp-macro-assembler-sw64.h | 233 + + deps/v8/src/runtime/runtime-atomics.cc | 4 +- + deps/v8/src/runtime/runtime-utils.h | 13 + + deps/v8/src/snapshot/deserializer.h | 2 +- + .../v8/src/snapshot/embedded/embedded-data.cc | 2 +- + .../platform-embedded-file-writer-generic.cc | 2 +- + .../wasm/baseline/liftoff-assembler-defs.h | 14 + + deps/v8/src/wasm/baseline/liftoff-assembler.h | 2 + + deps/v8/src/wasm/baseline/liftoff-compiler.cc | 2 +- + .../baseline/sw64/liftoff-assembler-sw64.h | 3590 +++++++++++ + deps/v8/src/wasm/jump-table-assembler.cc | 35 + + deps/v8/src/wasm/jump-table-assembler.h | 5 + + deps/v8/src/wasm/wasm-debug.cc | 4 + + deps/v8/src/wasm/wasm-linkage.h | 9 + + tools/v8_gypfiles/toolchain.gypi | 12 + + tools/v8_gypfiles/v8.gyp | 15 + + 88 files changed, 34783 insertions(+), 34 deletions(-) + create mode 100644 deps/v8/src/baseline/sw64/baseline-assembler-sw64-inl.h + create mode 100644 deps/v8/src/baseline/sw64/baseline-compiler-sw64-inl.h + create mode 100644 deps/v8/src/builtins/sw64/builtins-sw64.cc + create mode 100644 deps/v8/src/codegen/sw64/assembler-sw64-inl.h + create mode 100644 deps/v8/src/codegen/sw64/assembler-sw64.cc + create mode 100644 deps/v8/src/codegen/sw64/assembler-sw64.h + create mode 100644 deps/v8/src/codegen/sw64/constants-sw64.cc + create mode 100644 deps/v8/src/codegen/sw64/constants-sw64.h + create mode 100644 deps/v8/src/codegen/sw64/cpu-sw64.cc + create mode 100644 deps/v8/src/codegen/sw64/interface-descriptors-sw64-inl.h + create mode 100644 deps/v8/src/codegen/sw64/macro-assembler-sw64.cc + create mode 100644 deps/v8/src/codegen/sw64/macro-assembler-sw64.h + create mode 100644 deps/v8/src/codegen/sw64/register-sw64.h + create mode 100644 deps/v8/src/codegen/sw64/reglist-sw64.h + create mode 100644 deps/v8/src/compiler/backend/sw64/code-generator-sw64.cc + create mode 100644 deps/v8/src/compiler/backend/sw64/instruction-codes-sw64.h + create mode 100644 deps/v8/src/compiler/backend/sw64/instruction-scheduler-sw64.cc + create mode 100755 deps/v8/src/compiler/backend/sw64/instruction-selector-sw64.cc + create mode 100644 deps/v8/src/deoptimizer/sw64/deoptimizer-sw64.cc + create mode 100644 deps/v8/src/diagnostics/sw64/disasm-sw64.cc + create mode 100644 deps/v8/src/diagnostics/sw64/unwinder-sw64.cc + create mode 100644 deps/v8/src/execution/sw64/frame-constants-sw64.cc + create mode 100644 deps/v8/src/execution/sw64/frame-constants-sw64.h + create mode 100644 deps/v8/src/execution/sw64/simulator-sw64.cc + create mode 100644 deps/v8/src/execution/sw64/simulator-sw64.h + create mode 100644 deps/v8/src/heap/base/asm/sw64/push_registers_asm.cc + create mode 100644 deps/v8/src/regexp/sw64/regexp-macro-assembler-sw64.cc + create mode 100644 deps/v8/src/regexp/sw64/regexp-macro-assembler-sw64.h + create mode 100644 deps/v8/src/wasm/baseline/sw64/liftoff-assembler-sw64.h + +diff --git a/configure.py b/configure.py +index 82916748..eda2755c 100644 +--- a/configure.py ++++ b/configure.py +@@ -48,10 +48,11 @@ parser = argparse.ArgumentParser() + + valid_os = ('win', 'mac', 'solaris', 'freebsd', 'openbsd', 'linux', + 'android', 'aix', 'cloudabi', 'os400', 'ios') +-valid_arch = ('arm', 'arm64', 'ia32', 'mips', 'mipsel', 'mips64el', 'ppc', ++valid_arch = ('arm', 'arm64', 'ia32', 'mips', 'mipsel', 'mips64el', 'sw_64', 'ppc', + 'ppc64', 'x64', 'x86', 'x86_64', 's390x', 'riscv64', 'loong64') + valid_arm_float_abi = ('soft', 'softfp', 'hard') + valid_arm_fpu = ('vfp', 'vfpv3', 'vfpv3-d16', 'neon') ++valid_sw64_arch = ('r2', 'r3') + valid_mips_arch = ('loongson', 'r1', 'r2', 'r6', 'rx') + valid_mips_fpu = ('fp32', 'fp64', 'fpxx') + valid_mips_float_abi = ('soft', 'hard') +@@ -471,6 +472,11 @@ parser.add_argument('--tag', + dest='tag', + help='custom build tag') + ++parser.add_argument('--sw-tag', ++ action='store', ++ dest='sw_tag', ++ help='sw64 build tag') ++ + parser.add_argument('--release-urlbase', + action='store', + dest='release_urlbase', +@@ -532,6 +538,13 @@ parser.add_argument('--with-mips-arch-variant', + choices=valid_mips_arch, + help=f"MIPS arch variant ({', '.join(valid_mips_arch)}) [default: %(default)s]") + ++parser.add_argument('--with-sw64-arch-variant', ++ action='store', ++ dest='sw64_arch_variant', ++ default='r2', ++ choices=valid_sw64_arch, ++ help=f"SW64 arch variant ({', '.join(valid_sw64_arch)}) [default: %(default)s]") ++ + parser.add_argument('--with-mips-fpu-mode', + action='store', + dest='mips_fpu_mode', +@@ -1153,6 +1166,7 @@ def host_arch_cc(): + '__s390x__' : 's390x', + '__riscv' : 'riscv', + '__loongarch64': 'loong64', ++ '__sw_64__' : 'sw_64', + } + + rtn = 'ia32' # default +@@ -1216,6 +1230,8 @@ def configure_arm(o): + + o['variables']['arm_fpu'] = options.arm_fpu or arm_fpu + ++def configure_sw64(o): ++ o['variables']['sw64_arch_variant'] = options.sw64_arch_variant + + def configure_mips(o, target_arch): + can_use_fpu_instructions = options.mips_float_abi != 'soft' +@@ -1321,6 +1337,8 @@ def configure_node(o): + + if target_arch == 'arm': + configure_arm(o) ++ elif target_arch == 'sw_64': ++ configure_sw64(o) + elif target_arch in ('mips', 'mipsel', 'mips64el'): + configure_mips(o, target_arch) + elif sys.platform == 'zos': +@@ -1420,10 +1438,18 @@ def configure_node(o): + raise Exception('Link Time Code Generation is only supported on Windows.') + + if options.tag: +- o['variables']['node_tag'] = '-' + options.tag ++ if target_arch == 'sw_64': ++ o['variables']['node_tag'] = ' ' + options.tag ++ else: ++ o['variables']['node_tag'] = '-' + options.tag + else: + o['variables']['node_tag'] = '' + ++ if options.sw_tag: ++ o['variables']['node_sw_tag'] = options.sw_tag ++ else: ++ o['variables']['node_sw_tag'] = '' ++ + o['variables']['node_release_urlbase'] = options.release_urlbase or '' + + if options.v8_options: +diff --git a/deps/icu-small/source/i18n/double-conversion-utils.h b/deps/icu-small/source/i18n/double-conversion-utils.h +index 303668f9..f3f7b9d9 100644 +--- a/deps/icu-small/source/i18n/double-conversion-utils.h ++++ b/deps/icu-small/source/i18n/double-conversion-utils.h +@@ -148,7 +148,7 @@ int main(int argc, char** argv) { + #if defined(_M_X64) || defined(__x86_64__) || \ + defined(__ARMEL__) || defined(__avr32__) || defined(_M_ARM) || defined(_M_ARM64) || \ + defined(__hppa__) || defined(__ia64__) || \ +- defined(__mips__) || \ ++ defined(__mips__) || defined(__sw_64__) || \ + defined(__loongarch__) || \ + defined(__nios2__) || defined(__ghs) || \ + defined(__powerpc__) || defined(__ppc__) || defined(__ppc64__) || \ +diff --git a/deps/v8/BUILD.gn b/deps/v8/BUILD.gn +index f6860561..89234a5a 100644 +--- a/deps/v8/BUILD.gn ++++ b/deps/v8/BUILD.gn +@@ -1071,6 +1071,10 @@ config("toolchain") { + } + } + ++ if (v8_current_cpu == "sw_64") { ++ defines += [ "V8_TARGET_ARCH_SW64", "SW64" ] ++ } ++ + # Mips64el/mipsel simulators. + if (target_is_simulator && + (v8_current_cpu == "mipsel" || v8_current_cpu == "mips64el")) { +@@ -2456,6 +2460,11 @@ v8_source_set("v8_initializers") { + ### gcmole(arch:loong64) ### + "src/builtins/loong64/builtins-loong64.cc", + ] ++ } else if(v8_current_cpu == "sw_64") { ++ sources += [ ++ ### gcmole(arch:sw64) ### ++ "src/builtins/sw64/builtins-sw64.cc", ++ ] + } else if (v8_current_cpu == "ppc") { + sources += [ + ### gcmole(arch:ppc) ### +@@ -3790,6 +3799,22 @@ v8_header_set("v8_internal_headers") { + "src/regexp/mips64/regexp-macro-assembler-mips64.h", + "src/wasm/baseline/mips64/liftoff-assembler-mips64.h", + ] ++ } else if (v8_current_cpu == "sw_64" || v8_current_cpu == "sw_64") { ++ sources += [ ### gcmole(arch:sw_64) ### ++ "src/baseline/sw64/baseline-assembler-sw64-inl.h", ++ "src/baseline/sw64/baseline-compiler-sw64-inl.h", ++ "src/codegen/sw64/assembler-sw64-inl.h", ++ "src/codegen/sw64/assembler-sw64.h", ++ "src/codegen/sw64/constants-sw64.h", ++ "src/codegen/sw64/macro-assembler-sw64.h", ++ "src/codegen/sw64/register-sw64.h", ++ "src/codegen/sw64/reglist-sw64.h", ++ "src/compiler/backend/sw64/instruction-codes-sw64.h", ++ "src/execution/sw64/frame-constants-sw64.h", ++ "src/execution/sw64/simulator-sw64.h", ++ "src/regexp/sw64/regexp-macro-assembler-sw64.h", ++ "src/wasm/baseline/sw64/liftoff-assembler-sw64.h", ++ ] + } else if (v8_current_cpu == "loong64") { + sources += [ ### gcmole(arch:loong64) ### + "src/baseline/loong64/baseline-assembler-loong64-inl.h", +@@ -4794,6 +4819,23 @@ v8_source_set("v8_base_without_compiler") { + "src/execution/loong64/simulator-loong64.cc", + "src/regexp/loong64/regexp-macro-assembler-loong64.cc", + ] ++ } else if(v8_current_cpu == "sw_64") { ++ sources += [ ### gcmole(arch:sw_64) ### ++ "src/codegen/sw64/assembler-sw64.cc", ++ "src/codegen/sw64/constants-sw64.cc", ++ "src/codegen/sw64/cpu-sw64.cc", ++ "src/codegen/sw64/interface-descriptors-sw64-inl.h", ++ "src/codegen/sw64/macro-assembler-sw64.cc", ++ "src/compiler/backend/sw64/code-generator-sw64.cc", ++ "src/compiler/backend/sw64/instruction-scheduler-sw64.cc", ++ "src/compiler/backend/sw64/instruction-selector-sw64.cc", ++ "src/deoptimizer/sw64/deoptimizer-sw64.cc", ++ "src/diagnostics/sw64/disasm-sw64.cc", ++ "src/diagnostics/sw64/unwinder-sw64.cc", ++ "src/execution/sw64/frame-constants-sw64.cc", ++ "src/execution/sw64/simulator-sw64.cc", ++ "src/regexp/sw64/regexp-macro-assembler-sw64.cc", ++ ] + } else if (v8_current_cpu == "ppc") { + sources += [ ### gcmole(arch:ppc) ### + "src/codegen/ppc/assembler-ppc.cc", +@@ -5535,6 +5577,8 @@ v8_source_set("v8_heap_base") { + sources += [ "src/heap/base/asm/loong64/push_registers_asm.cc" ] + } else if (current_cpu == "riscv64") { + sources += [ "src/heap/base/asm/riscv64/push_registers_asm.cc" ] ++ } else if (current_cpu == "sw_64") { ++ sources += [ "src/heap/base/asm/sw64/push_registers_asm.cc" ] + } + } else if (is_win) { + if (current_cpu == "x64") { +diff --git a/deps/v8/include/v8-unwinder-state.h b/deps/v8/include/v8-unwinder-state.h +index a30f7325..0c5e846b 100644 +--- a/deps/v8/include/v8-unwinder-state.h ++++ b/deps/v8/include/v8-unwinder-state.h +@@ -20,7 +20,7 @@ struct CalleeSavedRegisters { + #elif V8_TARGET_ARCH_X64 || V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_ARM64 || \ + V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_PPC || \ + V8_TARGET_ARCH_PPC64 || V8_TARGET_ARCH_RISCV64 || V8_TARGET_ARCH_S390 || \ +- V8_TARGET_ARCH_LOONG64 ++ V8_TARGET_ARCH_LOONG64 || V8_TARGET_ARCH_SW64 + struct CalleeSavedRegisters {}; + #else + #error Target architecture was not detected as supported by v8 +diff --git a/deps/v8/infra/mb/mb_config.pyl b/deps/v8/infra/mb/mb_config.pyl +index 76958218..0ff9edb3 100644 +--- a/deps/v8/infra/mb/mb_config.pyl ++++ b/deps/v8/infra/mb/mb_config.pyl +@@ -40,6 +40,12 @@ + 'riscv64.debug.sim': 'default_debug_riscv64_sim', + 'riscv64.optdebug.sim': 'default_optdebug_riscv64_sim', + 'riscv64.release.sim': 'default_release_riscv64_sim', ++ 'sw_64.debug': 'default_debug_sw64', ++ 'sw_64.optdebug': 'default_optdebug_sw64', ++ 'sw_64.release': 'default_release_sw64', ++ 'sw_64.debug.sim': 'default_debug_sw64_sim', ++ 'sw_64.optdebug.sim': 'default_optdebug_sw64_sim', ++ 'sw_64.release.sim': 'default_release_sw64_sim', + 's390x.debug': 'default_debug_s390x', + 's390x.optdebug': 'default_optdebug_s390x', + 's390x.release': 'default_release_s390x', +@@ -198,6 +204,8 @@ + 'V8 Linux - s390x - sim - builder': 'release_simulate_s390x', + # RISC-V + 'V8 Linux - riscv64 - sim - builder': 'release_simulate_riscv64', ++ # SW64 ++ 'V8 Linux - sw_64 - sim - builder': 'release_simulate_sw64', + # Loongson + 'V8 Linux - loong64 - sim - builder': 'release_simulate_loong64', + }, +@@ -248,6 +256,7 @@ + 'v8_linux64_loong64_rel_ng': 'release_simulate_loong64', + 'v8_linux64_msan_rel_ng': 'release_simulate_arm64_msan_minimal_symbols', + 'v8_linux64_riscv64_rel_ng': 'release_simulate_riscv64', ++ 'v8_linux64_sw64_rel_ng': 'release_simulate_sw64', + 'v8_linux64_tsan_rel_ng': 'release_x64_tsan_minimal_symbols', + 'v8_linux64_tsan_no_cm_rel_ng': 'release_x64_tsan_no_cm', + 'v8_linux64_tsan_isolates_rel_ng': +@@ -348,6 +357,18 @@ + 'debug', 'simulate_riscv64', 'v8_enable_slow_dchecks'], + 'default_release_riscv64_sim': [ + 'release', 'simulate_riscv64'], ++ 'default_debug_sw64': [ ++ 'debug', 'sw_64', 'gcc', 'v8_enable_slow_dchecks', 'v8_full_debug'], ++ 'default_optdebug_sw64': [ ++ 'debug', 'sw_64', 'gcc', 'v8_enable_slow_dchecks'], ++ 'default_release_sw64': [ ++ 'release', 'sw_64', 'gcc'], ++ 'default_debug_sw64_sim': [ ++ 'debug', 'simulate_sw64', 'gcc', 'v8_enable_slow_dchecks', 'v8_full_debug'], ++ 'default_optdebug_sw64_sim': [ ++ 'debug', 'simulate_sw64', 'gcc', 'v8_enable_slow_dchecks'], ++ 'default_release_sw64_sim': [ ++ 'release', 'simulate_sw64', 'gcc'], + 'default_debug_ppc64': [ + 'debug', 'ppc64', 'gcc', 'v8_enable_slow_dchecks', 'v8_full_debug'], + 'default_optdebug_ppc64': [ +@@ -439,6 +460,8 @@ + 'release_bot', 'simulate_ppc64'], + 'release_simulate_riscv64': [ + 'release_bot', 'simulate_riscv64'], ++ 'release_simulate_sw64': [ ++ 'release_bot', 'simulate_sw64'], + 'release_simulate_s390x': [ + 'release_bot', 'simulate_s390x'], + +@@ -872,6 +895,10 @@ + 'gn_args': 'target_cpu="x64" v8_target_cpu="riscv64"', + }, + ++ 'simulate_sw64': { ++ 'gn_args': 'target_cpu="x64" v8_target_cpu="sw_64"', ++ }, ++ + 'simulate_s390x': { + 'gn_args': 'target_cpu="x64" v8_target_cpu="s390x"', + }, +@@ -1009,6 +1036,9 @@ + 'gn_args': 'target_cpu="riscv64" use_custom_libcxx=false', + }, + ++ 'sw_64': { ++ 'gn_args': 'target_cpu="sw_64" use_custom_libcxx=false', ++ }, + 'x64': { + 'gn_args': 'target_cpu="x64"', + }, +diff --git a/deps/v8/src/base/build_config.h b/deps/v8/src/base/build_config.h +index 3befde51..5c2e3b99 100644 +--- a/deps/v8/src/base/build_config.h ++++ b/deps/v8/src/base/build_config.h +@@ -30,6 +30,9 @@ + #elif defined(__mips64) + #define V8_HOST_ARCH_MIPS64 1 + #define V8_HOST_ARCH_64_BIT 1 ++#elif defined(__sw_64__) ++#define V8_HOST_ARCH_SW64 1 ++#define V8_HOST_ARCH_64_BIT 1 + #elif defined(__MIPSEB__) || defined(__MIPSEL__) + #define V8_HOST_ARCH_MIPS 1 + #define V8_HOST_ARCH_32_BIT 1 +@@ -86,7 +89,7 @@ + #if !V8_TARGET_ARCH_X64 && !V8_TARGET_ARCH_IA32 && !V8_TARGET_ARCH_ARM && \ + !V8_TARGET_ARCH_ARM64 && !V8_TARGET_ARCH_MIPS && !V8_TARGET_ARCH_MIPS64 && \ + !V8_TARGET_ARCH_PPC && !V8_TARGET_ARCH_PPC64 && !V8_TARGET_ARCH_S390 && \ +- !V8_TARGET_ARCH_RISCV64 && !V8_TARGET_ARCH_LOONG64 ++ !V8_TARGET_ARCH_RISCV64 && !V8_TARGET_ARCH_LOONG64 && !V8_TARGET_ARCH_SW64 + #if defined(_M_X64) || defined(__x86_64__) + #define V8_TARGET_ARCH_X64 1 + #elif defined(_M_IX86) || defined(__i386__) +@@ -97,6 +100,8 @@ + #define V8_TARGET_ARCH_ARM 1 + #elif defined(__mips64) + #define V8_TARGET_ARCH_MIPS64 1 ++#elif defined(__sw_64__) ++#define V8_TARGET_ARCH_SW64 1 + #elif defined(__MIPSEB__) || defined(__MIPSEL__) + #define V8_TARGET_ARCH_MIPS 1 + #elif defined(_ARCH_PPC64) +@@ -133,6 +138,8 @@ + #define V8_TARGET_ARCH_64_BIT 1 + #elif V8_TARGET_ARCH_LOONG64 + #define V8_TARGET_ARCH_64_BIT 1 ++#elif V8_TARGET_ARCH_SW64 ++#define V8_TARGET_ARCH_64_BIT 1 + #elif V8_TARGET_ARCH_PPC + #define V8_TARGET_ARCH_32_BIT 1 + #elif V8_TARGET_ARCH_PPC64 +@@ -178,6 +185,9 @@ + #endif + #if (V8_TARGET_ARCH_LOONG64 && !(V8_HOST_ARCH_X64 || V8_HOST_ARCH_LOONG64)) + #error Target architecture loong64 is only supported on loong64 and x64 host ++#if (V8_TARGET_ARCH_SW64 && !(V8_HOST_ARCH_X64 || V8_HOST_ARCH_SW64)) ++#error Target architecture sw64 is only supported on sw64 and x64 host ++#endif + #endif + + // Determine architecture endianness. +@@ -191,6 +201,8 @@ + #define V8_TARGET_LITTLE_ENDIAN 1 + #elif V8_TARGET_ARCH_LOONG64 + #define V8_TARGET_LITTLE_ENDIAN 1 ++#elif V8_TARGET_ARCH_SW64 ++#define V8_TARGET_LITTLE_ENDIAN 1 + #elif V8_TARGET_ARCH_MIPS + #if defined(__MIPSEB__) + #define V8_TARGET_BIG_ENDIAN 1 +diff --git a/deps/v8/src/base/cpu.cc b/deps/v8/src/base/cpu.cc +index 571a1b6a..286b5e3a 100644 +--- a/deps/v8/src/base/cpu.cc ++++ b/deps/v8/src/base/cpu.cc +@@ -87,7 +87,7 @@ static V8_INLINE void __cpuid(int cpu_info[4], int info_type) { + #endif // !V8_LIBC_MSVCRT + + #elif V8_HOST_ARCH_ARM || V8_HOST_ARCH_ARM64 || V8_HOST_ARCH_MIPS || \ +- V8_HOST_ARCH_MIPS64 || V8_HOST_ARCH_RISCV64 ++ V8_HOST_ARCH_MIPS64 || V8_HOST_ARCH_RISCV64 || V8_HOST_ARCH_SW64 + + #if V8_OS_LINUX + +@@ -447,6 +447,7 @@ CPU::CPU() + has_non_stop_time_stamp_counter_(false), + is_running_in_vm_(false), + has_msa_(false), ++ has_swsa_(false), + has_rvv_(false) { + memcpy(vendor_, "Unknown", 8); + +@@ -739,7 +740,7 @@ CPU::CPU() + + #endif // V8_OS_LINUX + +-#elif V8_HOST_ARCH_MIPS || V8_HOST_ARCH_MIPS64 ++#elif V8_HOST_ARCH_MIPS || V8_HOST_ARCH_MIPS64 + + // Simple detection of FPU at runtime for Linux. + // It is based on /proc/cpuinfo, which reveals hardware configuration +@@ -758,6 +759,21 @@ CPU::CPU() + architecture_ = __detect_mips_arch_revision(); + #endif + ++#elif V8_HOST_ARCH_SW64 ++ ++ // Simple detection of FPU at runtime for Linux. ++ // It is based on /proc/cpuinfo, which reveals hardware configuration ++ // to user-space applications. ++ CPUInfo cpu_info; ++ char* cpu_model = cpu_info.ExtractField("model name"); ++ has_fpu_ = HasListItem(cpu_model, "FPU"); ++ char* sw_model_name = cpu_info.ExtractField("model name"); ++ use_sw8a_ = HasListItem(sw_model_name, "WX-H8000"); ++ char* ASEs = cpu_info.ExtractField("ASEs implemented"); ++ has_swsa_ = HasListItem(ASEs, "swsa"); ++ delete[] cpu_model; ++ delete[] ASEs; ++ + #elif V8_HOST_ARCH_ARM64 + #ifdef V8_OS_WIN + // Windows makes high-resolution thread timing information available in +diff --git a/deps/v8/src/base/cpu.h b/deps/v8/src/base/cpu.h +index 3050f2c4..6406ff5d 100644 +--- a/deps/v8/src/base/cpu.h ++++ b/deps/v8/src/base/cpu.h +@@ -127,10 +127,14 @@ class V8_BASE_EXPORT CPU final { + // mips features + bool is_fp64_mode() const { return is_fp64_mode_; } + bool has_msa() const { return has_msa_; } ++ bool has_swsa() const { return has_swsa_; } + + // riscv features + bool has_rvv() const { return has_rvv_; } + ++ // SW64 features ++ bool use_sw8a() const { return use_sw8a_; } ++ + private: + #if defined(V8_OS_STARBOARD) + bool StarboardDetectCPU(); +@@ -180,7 +184,9 @@ class V8_BASE_EXPORT CPU final { + bool has_non_stop_time_stamp_counter_; + bool is_running_in_vm_; + bool has_msa_; ++ bool has_swsa_; + bool has_rvv_; ++ bool use_sw8a_; + }; + + } // namespace base +diff --git a/deps/v8/src/base/platform/platform-posix.cc b/deps/v8/src/base/platform/platform-posix.cc +index 517cbfcc..0d786e2a 100644 +--- a/deps/v8/src/base/platform/platform-posix.cc ++++ b/deps/v8/src/base/platform/platform-posix.cc +@@ -364,6 +364,9 @@ void* OS::GetRandomMmapAddr() { + // 42 bits of virtual addressing. Truncate to 40 bits to allow kernel chance + // to fulfill request. + raw_addr &= uint64_t{0xFFFFFF0000}; ++#elif V8_TARGET_ARCH_SW64 ++ raw_addr &= uint64_t{0x07FF0000}; ++ raw_addr += 0x20000000000; + #else + raw_addr &= 0x3FFFF000; + +@@ -639,6 +642,8 @@ void OS::DebugBreak() { + asm("break"); + #elif V8_HOST_ARCH_LOONG64 + asm("break 0"); ++#elif V8_HOST_ARCH_SW64 ++ asm("sys_call 0x80"); + #elif V8_HOST_ARCH_PPC || V8_HOST_ARCH_PPC64 + asm("twge 2,2"); + #elif V8_HOST_ARCH_IA32 +diff --git a/deps/v8/src/baseline/baseline-assembler-inl.h b/deps/v8/src/baseline/baseline-assembler-inl.h +index 5a81dd55..b6a83a9e 100644 +--- a/deps/v8/src/baseline/baseline-assembler-inl.h ++++ b/deps/v8/src/baseline/baseline-assembler-inl.h +@@ -40,6 +40,8 @@ + #include "src/baseline/mips/baseline-assembler-mips-inl.h" + #elif V8_TARGET_ARCH_LOONG64 + #include "src/baseline/loong64/baseline-assembler-loong64-inl.h" ++#elif V8_TARGET_ARCH_SW64 ++#include "src/baseline/sw64/baseline-assembler-sw64-inl.h" + #else + #error Unsupported target architecture. + #endif +diff --git a/deps/v8/src/baseline/baseline-compiler.cc b/deps/v8/src/baseline/baseline-compiler.cc +index 9d4d480d..8a795974 100644 +--- a/deps/v8/src/baseline/baseline-compiler.cc ++++ b/deps/v8/src/baseline/baseline-compiler.cc +@@ -55,6 +55,8 @@ + #include "src/baseline/mips/baseline-compiler-mips-inl.h" + #elif V8_TARGET_ARCH_LOONG64 + #include "src/baseline/loong64/baseline-compiler-loong64-inl.h" ++#elif V8_TARGET_ARCH_SW64 ++#include "src/baseline/sw64/baseline-compiler-sw64-inl.h" + #else + #error Unsupported target architecture. + #endif +diff --git a/deps/v8/src/baseline/sw64/baseline-assembler-sw64-inl.h b/deps/v8/src/baseline/sw64/baseline-assembler-sw64-inl.h +new file mode 100644 +index 00000000..47c99074 +--- /dev/null ++++ b/deps/v8/src/baseline/sw64/baseline-assembler-sw64-inl.h +@@ -0,0 +1,525 @@ ++// 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 V8_BASELINE_SW64_BASELINE_ASSEMBLER_SW64_INL_H_ ++#define V8_BASELINE_SW64_BASELINE_ASSEMBLER_SW64_INL_H_ ++ ++#include "src/baseline/baseline-assembler.h" ++#include "src/codegen/interface-descriptors.h" ++#include "src/codegen/sw64/assembler-sw64-inl.h" ++#include "src/objects/literal-objects-inl.h" ++ ++namespace v8 { ++namespace internal { ++namespace baseline { ++ ++class BaselineAssembler::ScratchRegisterScope { ++ public: ++ explicit ScratchRegisterScope(BaselineAssembler* assembler) ++ : assembler_(assembler), ++ prev_scope_(assembler->scratch_register_scope_), ++ wrapped_scope_(assembler->masm()) { ++ if (!assembler_->scratch_register_scope_) { ++ // If we haven't opened a scratch scope yet, for the first one add a ++ // couple of extra registers. ++ wrapped_scope_.Include({t0, t1, t2, t3}); ++ } ++ assembler_->scratch_register_scope_ = this; ++ } ++ ~ScratchRegisterScope() { assembler_->scratch_register_scope_ = prev_scope_; } ++ ++ Register AcquireScratch() { return wrapped_scope_.Acquire(); } ++ ++ private: ++ BaselineAssembler* assembler_; ++ ScratchRegisterScope* prev_scope_; ++ UseScratchRegisterScope wrapped_scope_; ++}; ++ ++enum class Condition : uint32_t { ++ kEqual = eq, ++ kNotEqual = ne, ++ ++ kLessThan = lt, ++ kGreaterThan = gt, ++ kLessThanEqual = le, ++ kGreaterThanEqual = ge, ++ ++ kUnsignedLessThan = Uless, ++ kUnsignedGreaterThan = Ugreater, ++ kUnsignedLessThanEqual = Uless_equal, ++ kUnsignedGreaterThanEqual = Ugreater_equal, ++ ++ kOverflow = overflow, ++ kNoOverflow = no_overflow, ++ ++ kZero = eq, ++ kNotZero = ne, ++}; ++ ++inline internal::Condition AsMasmCondition(Condition cond) { ++ STATIC_ASSERT(sizeof(internal::Condition) == sizeof(Condition)); ++ return static_cast(cond); ++} ++ ++namespace detail { ++ ++#ifdef DEBUG ++inline bool Clobbers(Register target, MemOperand op) { ++ return op.is_reg() && op.rm() == target; ++} ++#endif ++ ++} // namespace detail ++ ++#define __ masm_-> ++ ++MemOperand BaselineAssembler::RegisterFrameOperand( ++ interpreter::Register interpreter_register) { ++ return MemOperand(fp, interpreter_register.ToOperand() * kSystemPointerSize); ++} ++void BaselineAssembler::RegisterFrameAddress( ++ interpreter::Register interpreter_register, Register rscratch) { ++ return __ Addl(rscratch, fp, ++ interpreter_register.ToOperand() * kSystemPointerSize); ++} ++MemOperand BaselineAssembler::FeedbackVectorOperand() { ++ return MemOperand(fp, BaselineFrameConstants::kFeedbackVectorFromFp); ++} ++ ++void BaselineAssembler::Bind(Label* label) { __ bind(label); } ++ ++void BaselineAssembler::BindWithoutJumpTarget(Label* label) { __ bind(label); } ++ ++void BaselineAssembler::JumpTarget() { ++ // NOP. ++} ++void BaselineAssembler::Jump(Label* target, Label::Distance distance) { ++ __ Branch(target); ++} ++void BaselineAssembler::JumpIfRoot(Register value, RootIndex index, ++ Label* target, Label::Distance) { ++ __ JumpIfRoot(value, index, target); ++} ++void BaselineAssembler::JumpIfNotRoot(Register value, RootIndex index, ++ Label* target, Label::Distance) { ++ __ JumpIfNotRoot(value, index, target); ++} ++void BaselineAssembler::JumpIfSmi(Register value, Label* target, ++ Label::Distance) { ++ __ JumpIfSmi(value, target); ++} ++void BaselineAssembler::JumpIfNotSmi(Register value, Label* target, ++ Label::Distance) { ++ __ JumpIfNotSmi(value, target); ++} ++void BaselineAssembler::JumpIfImmediate(Condition cc, Register left, int right, ++ Label* target, ++ Label::Distance distance) { ++ JumpIf(cc, left, Operand(right), target, distance); ++} ++ ++void BaselineAssembler::CallBuiltin(Builtin builtin) { ++ ASM_CODE_COMMENT_STRING(masm_, ++ __ CommentForOffHeapTrampoline("call", builtin)); ++ Register temp = t9; ++ __ LoadEntryFromBuiltin(builtin, temp); ++ __ Call(temp); ++} ++ ++void BaselineAssembler::TailCallBuiltin(Builtin builtin) { ++ ASM_CODE_COMMENT_STRING(masm_, ++ __ CommentForOffHeapTrampoline("tail call", builtin)); ++ Register temp = t9; ++ __ LoadEntryFromBuiltin(builtin, temp); ++ __ Jump(temp); ++} ++ ++void BaselineAssembler::TestAndBranch(Register value, int mask, Condition cc, ++ Label* target, Label::Distance) { ++ ScratchRegisterScope temps(this); ++ Register scratch = temps.AcquireScratch(); ++ __ And(scratch, value, Operand(mask)); ++ __ Branch(target, AsMasmCondition(cc), scratch, Operand(zero_reg)); ++} ++ ++void BaselineAssembler::JumpIf(Condition cc, Register lhs, const Operand& rhs, ++ Label* target, Label::Distance) { ++ __ Branch(target, AsMasmCondition(cc), lhs, Operand(rhs)); ++} ++void BaselineAssembler::JumpIfObjectType(Condition cc, Register object, ++ InstanceType instance_type, ++ Register map, Label* target, ++ Label::Distance) { ++ ScratchRegisterScope temps(this); ++ Register type = temps.AcquireScratch(); ++ __ GetObjectType(object, map, type); ++ __ Branch(target, AsMasmCondition(cc), type, Operand(instance_type)); ++} ++void BaselineAssembler::JumpIfInstanceType(Condition cc, Register map, ++ InstanceType instance_type, ++ Label* target, Label::Distance) { ++ ScratchRegisterScope temps(this); ++ Register type = temps.AcquireScratch(); ++ if (FLAG_debug_code) { ++ __ AssertNotSmi(map); ++ __ GetObjectType(map, type, type); ++ __ Assert(eq, AbortReason::kUnexpectedValue, type, Operand(MAP_TYPE)); ++ } ++ __ Ldl(type, FieldMemOperand(map, Map::kInstanceTypeOffset)); ++ __ Branch(target, AsMasmCondition(cc), type, Operand(instance_type)); ++} ++void BaselineAssembler::JumpIfPointer(Condition cc, Register value, ++ MemOperand operand, Label* target, ++ Label::Distance) { ++ ScratchRegisterScope temps(this); ++ Register scratch = temps.AcquireScratch(); ++ __ Ldl(scratch, operand); ++ __ Branch(target, AsMasmCondition(cc), value, Operand(scratch)); ++} ++void BaselineAssembler::JumpIfSmi(Condition cc, Register value, Smi smi, ++ Label* target, Label::Distance) { ++ ScratchRegisterScope temps(this); ++ Register scratch = temps.AcquireScratch(); ++ __ li(scratch, Operand(smi)); ++ __ SmiUntag(scratch); ++ __ Branch(target, AsMasmCondition(cc), value, Operand(scratch)); ++} ++void BaselineAssembler::JumpIfSmi(Condition cc, Register lhs, Register rhs, ++ Label* target, Label::Distance) { ++ __ AssertSmi(lhs); ++ __ AssertSmi(rhs); ++ __ Branch(target, AsMasmCondition(cc), lhs, Operand(rhs)); ++} ++void BaselineAssembler::JumpIfTagged(Condition cc, Register value, ++ MemOperand operand, Label* target, ++ Label::Distance) { ++ ScratchRegisterScope temps(this); ++ Register scratch = temps.AcquireScratch(); ++ __ Ldl(scratch, operand); ++ __ Branch(target, AsMasmCondition(cc), value, Operand(scratch)); ++} ++void BaselineAssembler::JumpIfTagged(Condition cc, MemOperand operand, ++ Register value, Label* target, ++ Label::Distance) { ++ ScratchRegisterScope temps(this); ++ Register scratch = temps.AcquireScratch(); ++ __ Ldl(scratch, operand); ++ __ Branch(target, AsMasmCondition(cc), scratch, Operand(value)); ++} ++void BaselineAssembler::JumpIfByte(Condition cc, Register value, int32_t byte, ++ Label* target, Label::Distance) { ++ __ Branch(target, AsMasmCondition(cc), value, Operand(byte)); ++} ++ ++void BaselineAssembler::Move(interpreter::Register output, Register source) { ++ Move(RegisterFrameOperand(output), source); ++} ++void BaselineAssembler::Move(Register output, TaggedIndex value) { ++ __ li(output, Operand(value.ptr())); ++} ++void BaselineAssembler::Move(MemOperand output, Register source) { ++ __ Stl(source, output); ++} ++void BaselineAssembler::Move(Register output, ExternalReference reference) { ++ __ li(output, Operand(reference)); ++} ++void BaselineAssembler::Move(Register output, Handle value) { ++ __ li(output, Operand(value)); ++} ++void BaselineAssembler::Move(Register output, int32_t value) { ++ __ li(output, Operand(value)); ++} ++void BaselineAssembler::MoveMaybeSmi(Register output, Register source) { ++ __ Move(output, source); ++} ++void BaselineAssembler::MoveSmi(Register output, Register source) { ++ __ Move(output, source); ++} ++ ++namespace detail { ++ ++template ++inline Register ToRegister(BaselineAssembler* basm, ++ BaselineAssembler::ScratchRegisterScope* scope, ++ Arg arg) { ++ Register reg = scope->AcquireScratch(); ++ basm->Move(reg, arg); ++ return reg; ++} ++inline Register ToRegister(BaselineAssembler* basm, ++ BaselineAssembler::ScratchRegisterScope* scope, ++ Register reg) { ++ return reg; ++} ++ ++template ++struct PushAllHelper; ++template <> ++struct PushAllHelper<> { ++ static int Push(BaselineAssembler* basm) { return 0; } ++ static int PushReverse(BaselineAssembler* basm) { return 0; } ++}; ++// TODO(ishell): try to pack sequence of pushes into one instruction by ++// looking at regiser codes. For example, Push(r1, r2, r5, r0, r3, r4) ++// could be generated as two pushes: Push(r1, r2, r5) and Push(r0, r3, r4). ++template ++struct PushAllHelper { ++ static int Push(BaselineAssembler* basm, Arg arg) { ++ BaselineAssembler::ScratchRegisterScope scope(basm); ++ basm->masm()->Push(ToRegister(basm, &scope, arg)); ++ return 1; ++ } ++ static int PushReverse(BaselineAssembler* basm, Arg arg) { ++ return Push(basm, arg); ++ } ++}; ++// TODO(ishell): try to pack sequence of pushes into one instruction by ++// looking at regiser codes. For example, Push(r1, r2, r5, r0, r3, r4) ++// could be generated as two pushes: Push(r1, r2, r5) and Push(r0, r3, r4). ++template ++struct PushAllHelper { ++ static int Push(BaselineAssembler* basm, Arg arg, Args... args) { ++ PushAllHelper::Push(basm, arg); ++ return 1 + PushAllHelper::Push(basm, args...); ++ } ++ static int PushReverse(BaselineAssembler* basm, Arg arg, Args... args) { ++ int nargs = PushAllHelper::PushReverse(basm, args...); ++ PushAllHelper::Push(basm, arg); ++ return nargs + 1; ++ } ++}; ++template <> ++struct PushAllHelper { ++ static int Push(BaselineAssembler* basm, interpreter::RegisterList list) { ++ for (int reg_index = 0; reg_index < list.register_count(); ++reg_index) { ++ PushAllHelper::Push(basm, list[reg_index]); ++ } ++ return list.register_count(); ++ } ++ static int PushReverse(BaselineAssembler* basm, ++ interpreter::RegisterList list) { ++ for (int reg_index = list.register_count() - 1; reg_index >= 0; ++ --reg_index) { ++ PushAllHelper::Push(basm, list[reg_index]); ++ } ++ return list.register_count(); ++ } ++}; ++ ++template ++struct PopAllHelper; ++template <> ++struct PopAllHelper<> { ++ static void Pop(BaselineAssembler* basm) {} ++}; ++// TODO(ishell): try to pack sequence of pops into one instruction by ++// looking at regiser codes. For example, Pop(r1, r2, r5, r0, r3, r4) ++// could be generated as two pops: Pop(r1, r2, r5) and Pop(r0, r3, r4). ++template <> ++struct PopAllHelper { ++ static void Pop(BaselineAssembler* basm, Register reg) { ++ basm->masm()->Pop(reg); ++ } ++}; ++template ++struct PopAllHelper { ++ static void Pop(BaselineAssembler* basm, Register reg, T... tail) { ++ PopAllHelper::Pop(basm, reg); ++ PopAllHelper::Pop(basm, tail...); ++ } ++}; ++ ++} // namespace detail ++ ++template ++int BaselineAssembler::Push(T... vals) { ++ return detail::PushAllHelper::Push(this, vals...); ++} ++ ++template ++void BaselineAssembler::PushReverse(T... vals) { ++ detail::PushAllHelper::PushReverse(this, vals...); ++} ++ ++template ++void BaselineAssembler::Pop(T... registers) { ++ detail::PopAllHelper::Pop(this, registers...); ++} ++ ++void BaselineAssembler::LoadTaggedPointerField(Register output, Register source, ++ int offset) { ++ __ Ldl(output, FieldMemOperand(source, offset)); ++} ++void BaselineAssembler::LoadTaggedSignedField(Register output, Register source, ++ int offset) { ++ __ Ldl(output, FieldMemOperand(source, offset)); ++} ++void BaselineAssembler::LoadTaggedAnyField(Register output, Register source, ++ int offset) { ++ __ Ldl(output, FieldMemOperand(source, offset)); ++} ++void BaselineAssembler::LoadWord16FieldZeroExtend(Register output, ++ Register source, int offset) { ++ __ Ldhu(output, FieldMemOperand(source, offset)); ++} ++void BaselineAssembler::LoadWord8Field(Register output, Register source, ++ int offset) { ++ __ Ldb(output, FieldMemOperand(source, offset)); ++} ++void BaselineAssembler::StoreTaggedSignedField(Register target, int offset, ++ Smi value) { ++ ASM_CODE_COMMENT(masm_); ++ ScratchRegisterScope temps(this); ++ Register scratch = temps.AcquireScratch(); ++ __ li(scratch, Operand(value)); ++ __ Stl(scratch, FieldMemOperand(target, offset)); ++} ++void BaselineAssembler::StoreTaggedFieldWithWriteBarrier(Register target, ++ int offset, ++ Register value) { ++ ASM_CODE_COMMENT(masm_); ++ __ Stl(value, FieldMemOperand(target, offset)); ++ ScratchRegisterScope temps(this); ++ Register scratch = temps.AcquireScratch(); ++ __ RecordWriteField(target, offset, value, scratch, kRAHasNotBeenSaved, ++ SaveFPRegsMode::kIgnore); ++} ++void BaselineAssembler::StoreTaggedFieldNoWriteBarrier(Register target, ++ int offset, ++ Register value) { ++ __ Stl(value, FieldMemOperand(target, offset)); ++} ++ ++void BaselineAssembler::AddToInterruptBudgetAndJumpIfNotExceeded( ++ int32_t weight, Label* skip_interrupt_label) { ++ ASM_CODE_COMMENT(masm_); ++ ScratchRegisterScope scratch_scope(this); ++ Register feedback_cell = scratch_scope.AcquireScratch(); ++ LoadFunction(feedback_cell); ++ LoadTaggedPointerField(feedback_cell, feedback_cell, ++ JSFunction::kFeedbackCellOffset); ++ ++ Register interrupt_budget = scratch_scope.AcquireScratch(); ++ __ Ldw(interrupt_budget, ++ FieldMemOperand(feedback_cell, FeedbackCell::kInterruptBudgetOffset)); ++ __ Addw(interrupt_budget, interrupt_budget, weight); ++ __ Stw(interrupt_budget, ++ FieldMemOperand(feedback_cell, FeedbackCell::kInterruptBudgetOffset)); ++ if (skip_interrupt_label) { ++ DCHECK_LT(weight, 0); ++ __ Branch(skip_interrupt_label, ge, interrupt_budget, Operand(zero_reg)); ++ } ++} ++void BaselineAssembler::AddToInterruptBudgetAndJumpIfNotExceeded( ++ Register weight, Label* skip_interrupt_label) { ++ ASM_CODE_COMMENT(masm_); ++ ScratchRegisterScope scratch_scope(this); ++ Register feedback_cell = scratch_scope.AcquireScratch(); ++ LoadFunction(feedback_cell); ++ LoadTaggedPointerField(feedback_cell, feedback_cell, ++ JSFunction::kFeedbackCellOffset); ++ ++ Register interrupt_budget = scratch_scope.AcquireScratch(); ++ __ Ldw(interrupt_budget, ++ FieldMemOperand(feedback_cell, FeedbackCell::kInterruptBudgetOffset)); ++ __ Addw(interrupt_budget, interrupt_budget, weight); ++ __ Stw(interrupt_budget, ++ FieldMemOperand(feedback_cell, FeedbackCell::kInterruptBudgetOffset)); ++ if (skip_interrupt_label) ++ __ Branch(skip_interrupt_label, ge, interrupt_budget, Operand(zero_reg)); ++} ++ ++void BaselineAssembler::AddSmi(Register lhs, Smi rhs) { ++ __ Addl(lhs, lhs, Operand(rhs)); ++} ++ ++void BaselineAssembler::Word32And(Register output, Register lhs, int rhs) { ++ __ And(output, lhs, Operand(rhs)); ++} ++ ++void BaselineAssembler::Switch(Register reg, int case_value_base, ++ Label** labels, int num_labels) { ++ ASM_CODE_COMMENT(masm_); ++ Label fallthrough; ++ if (case_value_base != 0) { ++ __ Subl(reg, reg, Operand(case_value_base)); ++ } ++ ++ __ Branch(&fallthrough, AsMasmCondition(Condition::kUnsignedGreaterThanEqual), ++ reg, Operand(num_labels)); ++ ++ __ GenerateSwitchTable(reg, num_labels, ++ [labels](size_t i) { return labels[i]; }); ++ ++ __ bind(&fallthrough); ++} ++ ++#undef __ ++ ++#define __ basm. ++ ++void BaselineAssembler::EmitReturn(MacroAssembler* masm) { ++ ASM_CODE_COMMENT(masm); ++ BaselineAssembler basm(masm); ++ ++ Register weight = BaselineLeaveFrameDescriptor::WeightRegister(); ++ Register params_size = BaselineLeaveFrameDescriptor::ParamsSizeRegister(); ++ ++ { ++ ASM_CODE_COMMENT_STRING(masm, "Update Interrupt Budget"); ++ ++ Label skip_interrupt_label; ++ __ AddToInterruptBudgetAndJumpIfNotExceeded(weight, &skip_interrupt_label); ++ __ masm()->SmiTag(params_size); ++ __ masm()->Push(params_size, kInterpreterAccumulatorRegister); ++ ++ __ LoadContext(kContextRegister); ++ __ LoadFunction(kJSFunctionRegister); ++ __ masm()->Push(kJSFunctionRegister); ++ __ CallRuntime(Runtime::kBytecodeBudgetInterrupt, 1); ++ ++ __ masm()->Pop(params_size, kInterpreterAccumulatorRegister); ++ __ masm()->SmiUntag(params_size); ++ ++ __ Bind(&skip_interrupt_label); ++ } ++ ++ BaselineAssembler::ScratchRegisterScope temps(&basm); ++ Register actual_params_size = temps.AcquireScratch(); ++ // Compute the size of the actual parameters + receiver (in bytes). ++ __ Move(actual_params_size, ++ MemOperand(fp, StandardFrameConstants::kArgCOffset)); ++ ++ // If actual is bigger than formal, then we should use it to free up the stack ++ // arguments. ++ Label corrected_args_count; ++ __ masm()->Branch(&corrected_args_count, ge, params_size, ++ Operand(actual_params_size)); ++ __ masm()->Move(params_size, actual_params_size); ++ __ Bind(&corrected_args_count); ++ ++ // Leave the frame (also dropping the register file). ++ __ masm()->LeaveFrame(StackFrame::BASELINE); ++ ++ // Drop receiver + arguments. ++ __ masm()->DropArguments(params_size, TurboAssembler::kCountIsInteger, ++ TurboAssembler::kCountIncludesReceiver); ++ ++ __ masm()->Ret(); ++} ++ ++#undef __ ++ ++inline void EnsureAccumulatorPreservedScope::AssertEqualToAccumulator( ++ Register reg) { ++ assembler_->masm()->Assert(eq, AbortReason::kUnexpectedValue, reg, ++ Operand(kInterpreterAccumulatorRegister)); ++} ++ ++} // namespace baseline ++} // namespace internal ++} // namespace v8 ++ ++#endif // V8_BASELINE_SW64_BASELINE_ASSEMBLER_SW64_INL_H_ +diff --git a/deps/v8/src/baseline/sw64/baseline-compiler-sw64-inl.h b/deps/v8/src/baseline/sw64/baseline-compiler-sw64-inl.h +new file mode 100644 +index 00000000..7e8219dd +--- /dev/null ++++ b/deps/v8/src/baseline/sw64/baseline-compiler-sw64-inl.h +@@ -0,0 +1,78 @@ ++// 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 V8_BASELINE_SW64_BASELINE_COMPILER_SW64_INL_H_ ++#define V8_BASELINE_SW64_BASELINE_COMPILER_SW64_INL_H_ ++ ++#include "src/base/logging.h" ++#include "src/baseline/baseline-compiler.h" ++ ++namespace v8 { ++namespace internal { ++namespace baseline { ++ ++#define __ basm_. ++ ++void BaselineCompiler::Prologue() { ++ ASM_CODE_COMMENT(&masm_); ++ __ masm()->EnterFrame(StackFrame::BASELINE); ++ DCHECK_EQ(kJSFunctionRegister, kJavaScriptCallTargetRegister); ++ int max_frame_size = ++ bytecode_->frame_size() + max_call_args_ * kSystemPointerSize; ++ CallBuiltin( ++ kContextRegister, kJSFunctionRegister, kJavaScriptCallArgCountRegister, ++ max_frame_size, kJavaScriptCallNewTargetRegister, bytecode_); ++ ++ PrologueFillFrame(); ++} ++ ++void BaselineCompiler::PrologueFillFrame() { ++ ASM_CODE_COMMENT(&masm_); ++ // Inlined register frame fill ++ interpreter::Register new_target_or_generator_register = ++ bytecode_->incoming_new_target_or_generator_register(); ++ __ LoadRoot(kInterpreterAccumulatorRegister, RootIndex::kUndefinedValue); ++ int register_count = bytecode_->register_count(); ++ // Magic value ++ const int kLoopUnrollSize = 8; ++ const int new_target_index = new_target_or_generator_register.index(); ++ const bool has_new_target = new_target_index != kMaxInt; ++ if (has_new_target) { ++ DCHECK_LE(new_target_index, register_count); ++ __ masm()->Addl(sp, sp, Operand(-(kPointerSize * new_target_index))); ++ for (int i = 0; i < new_target_index; i++) { ++ __ masm()->Stl(kInterpreterAccumulatorRegister, MemOperand(sp, i * 8)); ++ } ++ // Push new_target_or_generator. ++ __ Push(kJavaScriptCallNewTargetRegister); ++ register_count -= new_target_index + 1; ++ } ++ if (register_count < 2 * kLoopUnrollSize) { ++ // If the frame is small enough, just unroll the frame fill completely. ++ __ masm()->Addl(sp, sp, Operand(-(kPointerSize * register_count))); ++ for (int i = 0; i < register_count; ++i) { ++ __ masm()->Stl(kInterpreterAccumulatorRegister, MemOperand(sp, i * 8)); ++ } ++ } else { ++ __ masm()->Addl(sp, sp, Operand(-(kPointerSize * register_count))); ++ for (int i = 0; i < register_count; ++i) { ++ __ masm()->Stl(kInterpreterAccumulatorRegister, MemOperand(sp, i * 8)); ++ } ++ } ++} ++ ++void BaselineCompiler::VerifyFrameSize() { ++ ASM_CODE_COMMENT(&masm_); ++ __ masm()->Addl(kScratchReg, sp, ++ Operand(InterpreterFrameConstants::kFixedFrameSizeFromFp + ++ bytecode_->frame_size())); ++ __ masm()->Assert(eq, AbortReason::kUnexpectedStackPointer, kScratchReg, ++ Operand(fp)); ++} ++ ++} // namespace baseline ++} // namespace internal ++} // namespace v8 ++ ++#endif // V8_BASELINE_SW64_BASELINE_COMPILER_SW64_INL_H_ +diff --git a/deps/v8/src/builtins/builtins-sharedarraybuffer-gen.cc b/deps/v8/src/builtins/builtins-sharedarraybuffer-gen.cc +index 37530e7a..b43166f5 100644 +--- a/deps/v8/src/builtins/builtins-sharedarraybuffer-gen.cc ++++ b/deps/v8/src/builtins/builtins-sharedarraybuffer-gen.cc +@@ -118,8 +118,8 @@ TNode SharedArrayBufferBuiltinsAssembler::ValidateAtomicAccess( + // 3. Let accessIndex be ? ToIndex(requestIndex). + TNode index_uintptr = ToIndex(context, index, &range_error); + +- // 4. Assert: accessIndex ≥ 0. +- // 5. If accessIndex ≥ length, throw a RangeError exception. ++ // 4. Assert: accessIndex â‰?0. ++ // 5. If accessIndex â‰?length, throw a RangeError exception. + Branch(UintPtrLessThan(index_uintptr, array_length), &done, &range_error); + + BIND(&range_error); +@@ -410,7 +410,7 @@ TF_BUILTIN(AtomicsExchange, SharedArrayBufferBuiltinsAssembler) { + TNode index_word = + ValidateAtomicAccess(array, index_or_field_name, context); + +-#if V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_RISCV64 ++#if V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_RISCV64 || V8_TARGET_ARCH_SW64 + USE(array_buffer); + TNode index_number = ChangeUintPtrToTagged(index_word); + Return(CallRuntime(Runtime::kAtomicsExchange, context, array, index_number, +@@ -549,7 +549,7 @@ TF_BUILTIN(AtomicsCompareExchange, SharedArrayBufferBuiltinsAssembler) { + + #if V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_PPC64 || \ + V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_S390 || V8_TARGET_ARCH_S390X || \ +- V8_TARGET_ARCH_RISCV64 ++ V8_TARGET_ARCH_RISCV64 || V8_TARGET_ARCH_SW64 + USE(array_buffer); + TNode index_number = ChangeUintPtrToTagged(index_word); + Return(CallRuntime(Runtime::kAtomicsCompareExchange, context, array, +@@ -724,7 +724,7 @@ void SharedArrayBufferBuiltinsAssembler::AtomicBinopBuiltinCommon( + + #if V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_PPC64 || \ + V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_S390 || V8_TARGET_ARCH_S390X || \ +- V8_TARGET_ARCH_RISCV64 ++ V8_TARGET_ARCH_RISCV64 || V8_TARGET_ARCH_SW64 + USE(array_buffer); + TNode index_number = ChangeUintPtrToTagged(index_word); + Return(CallRuntime(runtime_function, context, array, index_number, value)); +diff --git a/deps/v8/src/builtins/builtins.cc b/deps/v8/src/builtins/builtins.cc +index cb0ad9f2..c3987e3e 100644 +--- a/deps/v8/src/builtins/builtins.cc ++++ b/deps/v8/src/builtins/builtins.cc +@@ -530,7 +530,7 @@ bool Builtins::CodeObjectIsExecutable(Builtin builtin) { + case Builtin::kCEntry_Return1_DontSaveFPRegs_ArgvOnStack_NoBuiltinExit: + return true; + default: +-#if V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 ++#if V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_SW64 + // TODO(Loongson): Move non-JS linkage builtins code objects into RO_SPACE + // caused MIPS platform to crash, and we need some time to handle it. Now + // disable this change temporarily on MIPS platform. +diff --git a/deps/v8/src/builtins/sw64/builtins-sw64.cc b/deps/v8/src/builtins/sw64/builtins-sw64.cc +new file mode 100644 +index 00000000..0fbb98e4 +--- /dev/null ++++ b/deps/v8/src/builtins/sw64/builtins-sw64.cc +@@ -0,0 +1,3783 @@ ++// Copyright 2012 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#if V8_TARGET_ARCH_SW64 ++ ++#include "src/api/api-arguments.h" ++#include "src/codegen/code-factory.h" ++#include "src/codegen/interface-descriptors-inl.h" ++#include "src/debug/debug.h" ++#include "src/deoptimizer/deoptimizer.h" ++#include "src/execution/frame-constants.h" ++#include "src/execution/frames.h" ++#include "src/logging/counters.h" ++// For interpreter_entry_return_pc_offset. TODO(jkummerow): Drop. ++#include "src/codegen/macro-assembler-inl.h" ++#include "src/codegen/register-configuration.h" ++#include "src/codegen/sw64/constants-sw64.h" ++#include "src/heap/heap-inl.h" ++#include "src/objects/cell.h" ++#include "src/objects/foreign.h" ++#include "src/objects/heap-number.h" ++#include "src/objects/js-generator.h" ++#include "src/objects/objects-inl.h" ++#include "src/objects/smi.h" ++#include "src/runtime/runtime.h" ++ ++#if V8_ENABLE_WEBASSEMBLY ++#include "src/wasm/wasm-linkage.h" ++#include "src/wasm/wasm-objects.h" ++#endif // V8_ENABLE_WEBASSEMBLY ++ ++namespace v8 { ++namespace internal { ++ ++#define __ ACCESS_MASM(masm) ++ ++void Builtins::Generate_Adaptor(MacroAssembler* masm, Address address) { ++ __ li(kJavaScriptCallExtraArg1Register, ExternalReference::Create(address)); ++ __ Jump(BUILTIN_CODE(masm->isolate(), AdaptorWithBuiltinExitFrame), ++ RelocInfo::CODE_TARGET); ++} ++ ++static void GenerateTailCallToReturnedCode(MacroAssembler* masm, ++ Runtime::FunctionId function_id) { ++ // ----------- S t a t e ------------- ++ // -- a0 : actual argument count ++ // -- a1 : target function (preserved for callee) ++ // -- a3 : new target (preserved for callee) ++ // ----------------------------------- ++ { ++ FrameScope scope(masm, StackFrame::INTERNAL); ++ // Push a copy of the target function, the new target and the actual ++ // argument count. ++ // Push function as parameter to the runtime call. ++ __ SmiTag(kJavaScriptCallArgCountRegister); ++ __ Push(kJavaScriptCallTargetRegister, kJavaScriptCallNewTargetRegister, ++ kJavaScriptCallArgCountRegister, kJavaScriptCallTargetRegister); ++ ++ __ CallRuntime(function_id, 1); ++ // Restore target function, new target and actual argument count. ++ __ Pop(kJavaScriptCallTargetRegister, kJavaScriptCallNewTargetRegister, ++ kJavaScriptCallArgCountRegister); ++ __ SmiUntag(kJavaScriptCallArgCountRegister); ++ } ++ ++ static_assert(kJavaScriptCallCodeStartRegister == a2, "ABI mismatch"); ++ __ Addl(a2, v0, Operand(Code::kHeaderSize - kHeapObjectTag)); ++ __ Jump(a2); ++} ++ ++namespace { ++ ++enum class ArgumentsElementType { ++ kRaw, // Push arguments as they are. ++ kHandle // Dereference arguments before pushing. ++}; ++ ++void Generate_PushArguments(MacroAssembler* masm, Register array, Register argc, ++ Register scratch, Register scratch2, ++ ArgumentsElementType element_type) { ++ DCHECK(!AreAliased(array, argc, scratch)); ++ Label loop, entry; ++ __ Subl(scratch, argc, Operand(kJSArgcReceiverSlots)); ++ __ Branch(&entry); ++ __ bind(&loop); ++ __ Lsal(scratch2, array, scratch, kSystemPointerSizeLog2); ++ __ Ldl(scratch2, MemOperand(scratch2)); ++ if (element_type == ArgumentsElementType::kHandle) { ++ __ Ldl(scratch2, MemOperand(scratch2)); ++ } ++ __ Push(scratch2); ++ __ bind(&entry); ++ __ Addl(scratch, scratch, Operand(-1)); ++ __ Branch(&loop, greater_equal, scratch, Operand(zero_reg)); ++} ++ ++void Generate_JSBuiltinsConstructStubHelper(MacroAssembler* masm) { ++ // ----------- S t a t e ------------- ++ // -- a0 : number of arguments ++ // -- a1 : constructor function ++ // -- a3 : new target ++ // -- cp : context ++ // -- ra : return address ++ // -- sp[...]: constructor arguments ++ // ----------------------------------- ++ ++ // Enter a construct frame. ++ { ++ FrameScope scope(masm, StackFrame::CONSTRUCT); ++ ++ // Preserve the incoming parameters on the stack. ++ __ SmiTag(a0); ++ __ Push(cp, a0); ++ __ SmiUntag(a0); ++ ++ // Set up pointer to first argument (skip receiver). ++ __ Addl( ++ t2, fp, ++ Operand(StandardFrameConstants::kCallerSPOffset + kSystemPointerSize)); ++ // Copy arguments and receiver to the expression stack. ++ // t2: Pointer to start of arguments. ++ // a0: Number of arguments. ++ Generate_PushArguments(masm, t2, a0, t3, t0, ArgumentsElementType::kRaw); ++ // The receiver for the builtin/api call. ++ __ PushRoot(RootIndex::kTheHoleValue); ++ ++ // Call the function. ++ // a0: number of arguments (untagged) ++ // a1: constructor function ++ // a3: new target ++ __ InvokeFunctionWithNewTarget(a1, a3, a0, InvokeType::kCall); ++ ++ // Restore context from the frame. ++ __ Ldl(cp, MemOperand(fp, ConstructFrameConstants::kContextOffset)); ++ // Restore smi-tagged arguments count from the frame. ++ __ Ldl(t3, MemOperand(fp, ConstructFrameConstants::kLengthOffset)); ++ // Leave construct frame. ++ } ++ ++ // Remove caller arguments from the stack and return. ++ __ DropArguments(t3, TurboAssembler::kCountIsSmi, ++ TurboAssembler::kCountIncludesReceiver, t3); ++ __ Ret(); ++} ++ ++} // namespace ++ ++// The construct stub for ES5 constructor functions and ES6 class constructors. ++void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) { ++ // ----------- S t a t e ------------- ++ // -- a0: number of arguments (untagged) ++ // -- a1: constructor function ++ // -- a3: new target ++ // -- cp: context ++ // -- ra: return address ++ // -- sp[...]: constructor arguments ++ // ----------------------------------- ++ ++ // Enter a construct frame. ++ FrameScope scope(masm, StackFrame::MANUAL); ++ Label post_instantiation_deopt_entry, not_create_implicit_receiver; ++ __ EnterFrame(StackFrame::CONSTRUCT); ++ ++ // Preserve the incoming parameters on the stack. ++ __ SmiTag(a0); ++ __ Push(cp, a0, a1); ++ __ PushRoot(RootIndex::kUndefinedValue); ++ __ Push(a3); ++ ++ // ----------- S t a t e ------------- ++ // -- sp[0*kPointerSize]: new target ++ // -- sp[1*kPointerSize]: padding ++ // -- a1 and sp[2*kPointerSize]: constructor function ++ // -- sp[3*kPointerSize]: number of arguments (tagged) ++ // -- sp[4*kPointerSize]: context ++ // ----------------------------------- ++ ++ __ Ldl(t2, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset)); ++ __ Ldwu(t2, FieldMemOperand(t2, SharedFunctionInfo::kFlagsOffset)); ++ __ DecodeField(t2); ++ __ JumpIfIsInRange( ++ t2, static_cast(FunctionKind::kDefaultDerivedConstructor), ++ static_cast(FunctionKind::kDerivedConstructor), ++ ¬_create_implicit_receiver); ++ ++ // If not derived class constructor: Allocate the new receiver object. ++ __ IncrementCounter(masm->isolate()->counters()->constructed_objects(), 1, ++ t2, t3); ++ __ Call(BUILTIN_CODE(masm->isolate(), FastNewObject), ++ RelocInfo::CODE_TARGET); ++ __ Branch(&post_instantiation_deopt_entry); ++ ++ // Else: use TheHoleValue as receiver for constructor call ++ __ bind(¬_create_implicit_receiver); ++ __ LoadRoot(v0, RootIndex::kTheHoleValue); ++ ++ // ----------- S t a t e ------------- ++ // -- v0: receiver ++ // -- Slot 4 / sp[0*kPointerSize]: new target ++ // -- Slot 3 / sp[1*kPointerSize]: padding ++ // -- Slot 2 / sp[2*kPointerSize]: constructor function ++ // -- Slot 1 / sp[3*kPointerSize]: number of arguments (tagged) ++ // -- Slot 0 / sp[4*kPointerSize]: context ++ // ----------------------------------- ++ // Deoptimizer enters here. ++ masm->isolate()->heap()->SetConstructStubCreateDeoptPCOffset( ++ masm->pc_offset()); ++ __ bind(&post_instantiation_deopt_entry); ++ ++ // Restore new target. ++ __ Pop(a3); ++ ++ // Push the allocated receiver to the stack. ++ __ Push(v0); ++ ++ // We need two copies because we may have to return the original one ++ // and the calling conventions dictate that the called function pops the ++ // receiver. The second copy is pushed after the arguments, we saved in t9 ++ // since v0 will store the return value of callRuntime. ++ __ mov(t9, v0); ++ ++ // Set up pointer to last argument. ++ __ Addl( ++ t2, fp, ++ Operand(StandardFrameConstants::kCallerSPOffset + kSystemPointerSize)); ++ ++ // ----------- S t a t e ------------- ++ // -- a3: new target ++ // -- sp[0*kPointerSize]: implicit receiver ++ // -- sp[1*kPointerSize]: implicit receiver ++ // -- sp[2*kPointerSize]: padding ++ // -- sp[3*kPointerSize]: constructor function ++ // -- sp[4*kPointerSize]: number of arguments (tagged) ++ // -- sp[5*kPointerSize]: context ++ // ----------------------------------- ++ ++ // Restore constructor function and argument count. ++ __ Ldl(a1, MemOperand(fp, ConstructFrameConstants::kConstructorOffset)); ++ __ Ldl(a0, MemOperand(fp, ConstructFrameConstants::kLengthOffset)); ++ __ SmiUntag(a0); ++ ++ Label stack_overflow; ++ __ StackOverflowCheck(a0, t0, t1, &stack_overflow); ++ ++ // TODO(victorgomes): When the arguments adaptor is completely removed, we ++ // should get the formal parameter count and copy the arguments in its ++ // correct position (including any undefined), instead of delaying this to ++ // InvokeFunction. ++ ++ // Copy arguments and receiver to the expression stack. ++ // t2: Pointer to start of argument. ++ // a0: Number of arguments. ++ Generate_PushArguments(masm, t2, a0, t0, t1, ArgumentsElementType::kRaw); ++ // We need two copies because we may have to return the original one ++ // and the calling conventions dictate that the called function pops the ++ // receiver. The second copy is pushed after the arguments, ++ __ Push(t9); ++ ++ // Call the function. ++ __ InvokeFunctionWithNewTarget(a1, a3, a0, InvokeType::kCall); ++ ++ // ----------- S t a t e ------------- ++ // -- v0: constructor result ++ // -- sp[0*kPointerSize]: implicit receiver ++ // -- sp[1*kPointerSize]: padding ++ // -- sp[2*kPointerSize]: constructor function ++ // -- sp[3*kPointerSize]: number of arguments ++ // -- sp[4*kPointerSize]: context ++ // ----------------------------------- ++ ++ // Store offset of return address for deoptimizer. ++ masm->isolate()->heap()->SetConstructStubInvokeDeoptPCOffset( ++ masm->pc_offset()); ++ ++ // If the result is an object (in the ECMA sense), we should get rid ++ // of the receiver and use the result; see ECMA-262 section 13.2.2-7 ++ // on page 74. ++ Label use_receiver, do_throw, leave_and_return, check_receiver; ++ ++ // If the result is undefined, we jump out to using the implicit receiver. ++ __ JumpIfNotRoot(v0, RootIndex::kUndefinedValue, &check_receiver); ++ ++ // Otherwise we do a smi check and fall through to check if the return value ++ // is a valid receiver. ++ ++ // Throw away the result of the constructor invocation and use the ++ // on-stack receiver as the result. ++ __ bind(&use_receiver); ++ __ Ldl(v0, MemOperand(sp, 0 * kPointerSize)); ++ __ JumpIfRoot(v0, RootIndex::kTheHoleValue, &do_throw); ++ ++ __ bind(&leave_and_return); ++ // Restore smi-tagged arguments count from the frame. ++ __ Ldl(a1, MemOperand(fp, ConstructFrameConstants::kLengthOffset)); ++ // Leave construct frame. ++ __ LeaveFrame(StackFrame::CONSTRUCT); ++ ++ // Remove caller arguments from the stack and return. ++ __ DropArguments(a1, TurboAssembler::kCountIsSmi, ++ TurboAssembler::kCountIncludesReceiver, a4); ++ __ Ret(); ++ ++ __ bind(&check_receiver); ++ __ JumpIfSmi(v0, &use_receiver); ++ ++ // If the type of the result (stored in its map) is less than ++ // FIRST_JS_RECEIVER_TYPE, it is not an object in the ECMA sense. ++ __ GetObjectType(v0, t2, t2); ++ STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE); ++ __ Branch(&leave_and_return, greater_equal, t2, ++ Operand(FIRST_JS_RECEIVER_TYPE)); ++ __ Branch(&use_receiver); ++ ++ __ bind(&do_throw); ++ // Restore the context from the frame. ++ __ Ldl(cp, MemOperand(fp, ConstructFrameConstants::kContextOffset)); ++ __ CallRuntime(Runtime::kThrowConstructorReturnedNonObject); ++ __ sys_call(0x80); ++ ++ __ bind(&stack_overflow); ++ // Restore the context from the frame. ++ __ Ldl(cp, MemOperand(fp, ConstructFrameConstants::kContextOffset)); ++ __ CallRuntime(Runtime::kThrowStackOverflow); ++ __ sys_call(0x80); ++} ++ ++void Builtins::Generate_JSBuiltinsConstructStub(MacroAssembler* masm) { ++ Generate_JSBuiltinsConstructStubHelper(masm); ++} ++ ++static void AssertCodeIsBaseline(MacroAssembler* masm, Register code, ++ Register scratch) { ++ DCHECK(!AreAliased(code, scratch)); ++ // Verify that the code kind is baseline code via the CodeKind. ++ __ Ldl(scratch, FieldMemOperand(code, Code::kFlagsOffset)); ++ __ DecodeField(scratch); ++ __ Assert(eq, AbortReason::kExpectedBaselineData, scratch, ++ Operand(static_cast(CodeKind::BASELINE))); ++} ++ ++// TODO(v8:11429): Add a path for "not_compiled" and unify the two uses under ++// the more general dispatch. ++static void GetSharedFunctionInfoBytecodeOrBaseline(MacroAssembler* masm, ++ Register sfi_data, ++ Register scratch1, ++ Label* is_baseline) { ++ Label done; ++ ++ __ GetObjectType(sfi_data, scratch1, scratch1); ++ if (FLAG_debug_code) { ++ Label not_baseline; ++ __ Branch(¬_baseline, ne, scratch1, Operand(CODET_TYPE)); ++ AssertCodeIsBaseline(masm, sfi_data, scratch1); ++ __ Branch(is_baseline); ++ __ bind(¬_baseline); ++ } else { ++ __ Branch(is_baseline, eq, scratch1, Operand(CODET_TYPE)); ++ } ++ __ Branch(&done, ne, scratch1, Operand(INTERPRETER_DATA_TYPE)); ++ __ Ldl(sfi_data, ++ FieldMemOperand(sfi_data, InterpreterData::kBytecodeArrayOffset)); ++ __ bind(&done); ++} ++ ++// static ++void Builtins::Generate_ResumeGeneratorTrampoline(MacroAssembler* masm) { ++ // ----------- S t a t e ------------- ++ // -- v0 : the value to pass to the generator ++ // -- a1 : the JSGeneratorObject to resume ++ // -- ra : return address ++ // ----------------------------------- ++ // Store input value into generator object. ++ __ Stl(v0, FieldMemOperand(a1, JSGeneratorObject::kInputOrDebugPosOffset)); ++ __ RecordWriteField(a1, JSGeneratorObject::kInputOrDebugPosOffset, v0, a3, ++ kRAHasNotBeenSaved, SaveFPRegsMode::kIgnore); ++ // Check that a1 is still valid, RecordWrite might have clobbered it. ++ __ AssertGeneratorObject(a1); ++ ++ // Load suspended function and context. ++ __ Ldl(a4, FieldMemOperand(a1, JSGeneratorObject::kFunctionOffset)); ++ __ Ldl(cp, FieldMemOperand(a4, JSFunction::kContextOffset)); ++ ++ // Flood function if we are stepping. ++ Label prepare_step_in_if_stepping, prepare_step_in_suspended_generator; ++ Label stepping_prepared; ++ ExternalReference debug_hook = ++ ExternalReference::debug_hook_on_function_call_address(masm->isolate()); ++ __ li(a5, debug_hook); ++ __ Ldb(a5, MemOperand(a5)); ++ __ Branch(&prepare_step_in_if_stepping, ne, a5, Operand(zero_reg)); ++ ++ // Flood function if we need to continue stepping in the suspended generator. ++ ExternalReference debug_suspended_generator = ++ ExternalReference::debug_suspended_generator_address(masm->isolate()); ++ __ li(a5, debug_suspended_generator); ++ __ Ldl(a5, MemOperand(a5)); ++ __ Branch(&prepare_step_in_suspended_generator, eq, a1, Operand(a5)); ++ __ bind(&stepping_prepared); ++ ++ // Check the stack for overflow. We are not trying to catch interruptions ++ // (i.e. debug break and preemption) here, so check the "real stack limit". ++ Label stack_overflow; ++ __ LoadStackLimit(kScratchReg, ++ MacroAssembler::StackLimitKind::kRealStackLimit); ++ __ Branch(&stack_overflow, lo, sp, Operand(kScratchReg)); ++ ++ // ----------- S t a t e ------------- ++ // -- a1 : the JSGeneratorObject to resume ++ // -- a4 : generator function ++ // -- cp : generator context ++ // -- ra : return address ++ // ----------------------------------- ++ ++ // Push holes for arguments to generator function. Since the parser forced ++ // context allocation for any variables in generators, the actual argument ++ // values have already been copied into the context and these dummy values ++ // will never be used. ++ __ Ldl(a3, FieldMemOperand(a4, JSFunction::kSharedFunctionInfoOffset)); ++ __ Ldhu(a3, ++ FieldMemOperand(a3, SharedFunctionInfo::kFormalParameterCountOffset)); ++ __ Subl(a3, a3, Operand(kJSArgcReceiverSlots)); ++ __ Ldl(t1, ++ FieldMemOperand(a1, JSGeneratorObject::kParametersAndRegistersOffset)); ++ { ++ Label done_loop, loop; ++ __ bind(&loop); ++ __ Subl(a3, a3, Operand(1)); ++ __ Branch(&done_loop, lt, a3, Operand(zero_reg)); ++ __ s8addl(a3, t1, kScratchReg); ++ DCHECK_EQ(kSystemPointerSizeLog2, 3); ++ __ Ldl(kScratchReg, FieldMemOperand(kScratchReg, FixedArray::kHeaderSize)); ++ __ Push(kScratchReg); ++ __ Branch(&loop); ++ __ bind(&done_loop); ++ // Push receiver. ++ __ Ldl(kScratchReg, ++ FieldMemOperand(a1, JSGeneratorObject::kReceiverOffset)); ++ __ Push(kScratchReg); ++ } ++ ++ // Underlying function needs to have bytecode available. ++ if (FLAG_debug_code) { ++ Label is_baseline; ++ __ Ldl(a3, FieldMemOperand(a4, JSFunction::kSharedFunctionInfoOffset)); ++ __ Ldl(a3, FieldMemOperand(a3, SharedFunctionInfo::kFunctionDataOffset)); ++ GetSharedFunctionInfoBytecodeOrBaseline(masm, a3, a0, &is_baseline); ++ __ GetObjectType(a3, a3, a3); ++ __ Assert(eq, AbortReason::kMissingBytecodeArray, a3, ++ Operand(BYTECODE_ARRAY_TYPE)); ++ __ bind(&is_baseline); ++ } ++ ++ // Resume (Ignition/TurboFan) generator object. ++ { ++ __ Ldl(a0, FieldMemOperand(a4, JSFunction::kSharedFunctionInfoOffset)); ++ __ Ldhu(a0, FieldMemOperand( ++ a0, SharedFunctionInfo::kFormalParameterCountOffset)); ++ // We abuse new.target both to indicate that this is a resume call and to ++ // pass in the generator object. In ordinary calls, new.target is always ++ // undefined because generator functions are non-constructable. ++ __ Move(a3, a1); ++ __ Move(a1, a4); ++ static_assert(kJavaScriptCallCodeStartRegister == a2, "ABI mismatch"); ++ __ Ldl(a2, FieldMemOperand(a1, JSFunction::kCodeOffset)); ++ __ Addl(a2, a2, Operand(Code::kHeaderSize - kHeapObjectTag)); ++ __ Jump(a2); ++ } ++ ++ __ bind(&prepare_step_in_if_stepping); ++ { ++ FrameScope scope(masm, StackFrame::INTERNAL); ++ __ Push(a1, a4); ++ // Push hole as receiver since we do not use it for stepping. ++ __ PushRoot(RootIndex::kTheHoleValue); ++ __ CallRuntime(Runtime::kDebugOnFunctionCall); ++ __ Pop(a1); ++ } ++ __ Ldl(a4, FieldMemOperand(a1, JSGeneratorObject::kFunctionOffset)); ++ __ Branch(&stepping_prepared); ++ ++ __ bind(&prepare_step_in_suspended_generator); ++ { ++ FrameScope scope(masm, StackFrame::INTERNAL); ++ __ Push(a1); ++ __ CallRuntime(Runtime::kDebugPrepareStepInSuspendedGenerator); ++ __ Pop(a1); ++ } ++ __ Ldl(a4, FieldMemOperand(a1, JSGeneratorObject::kFunctionOffset)); ++ __ Branch(&stepping_prepared); ++ ++ __ bind(&stack_overflow); ++ { ++ FrameScope scope(masm, StackFrame::INTERNAL); ++ __ CallRuntime(Runtime::kThrowStackOverflow); ++ __ sys_call(0x80); // This should be unreachable. ++ } ++} ++ ++void Builtins::Generate_ConstructedNonConstructable(MacroAssembler* masm) { ++ FrameScope scope(masm, StackFrame::INTERNAL); ++ __ Push(a1); ++ __ CallRuntime(Runtime::kThrowConstructedNonConstructable); ++} ++ ++// Clobbers scratch1 and scratch2; preserves all other registers. ++static void Generate_CheckStackOverflow(MacroAssembler* masm, Register argc, ++ Register scratch1, Register scratch2) { ++ // Check the stack for overflow. We are not trying to catch ++ // interruptions (e.g. debug break and preemption) here, so the "real stack ++ // limit" is checked. ++ Label okay; ++ __ LoadStackLimit(scratch1, MacroAssembler::StackLimitKind::kRealStackLimit); ++ // Make a2 the space we have left. The stack might already be overflowed ++ // here which will cause r2 to become negative. ++ __ Subl(scratch1, sp, scratch1); ++ // Check if the arguments will overflow the stack. ++ __ slll(argc, kPointerSizeLog2, scratch2); ++ __ Branch(&okay, gt, scratch1, Operand(scratch2)); // Signed comparison. ++ ++ // Out of stack space. ++ __ CallRuntime(Runtime::kThrowStackOverflow); ++ ++ __ bind(&okay); ++} ++ ++namespace { ++ ++// Called with the native C calling convention. The corresponding function ++// signature is either: ++// ++// using JSEntryFunction = GeneratedCode; ++// or ++// using JSEntryFunction = GeneratedCode; ++void Generate_JSEntryVariant(MacroAssembler* masm, StackFrame::Type type, ++ Builtin entry_trampoline) { ++ Label invoke, handler_entry, exit; ++ ++ { ++ NoRootArrayScope no_root_array(masm); ++ ++ // TODO(plind): unify the ABI description here. ++ // Registers: ++ // either ++ // a0: root register value ++ // a1: entry address ++ // a2: function ++ // a3: receiver ++ // a4: argc ++ // a5: argv ++ // or ++ // a0: root register value ++ // a1: microtask_queue ++ // ++ // Stack: ++ // 0 arg slots on sw64 ++ ++ // Save callee saved registers on the stack. ++ __ MultiPush(kCalleeSaved | ra); ++ ++ // Save callee-saved FPU registers. ++ __ MultiPushFPU(kCalleeSavedFPU); ++ // Set up the reserved register for 0.0. ++ //__ Move(kDoubleRegZero, 0.0); ++ ++ // Initialize the root register. ++ // C calling convention. The first argument is passed in a0. ++ __ mov(kRootRegister, a0); ++ } ++ ++ // a1: entry address ++ // a2: function ++ // a3: receiver ++ // a4: argc ++ // a5: argv ++ ++ // We build an EntryFrame. ++ __ li(s1, Operand(-1)); // Push a bad frame pointer to fail if it is used. ++ __ li(s2, Operand(StackFrame::TypeToMarker(type))); ++ __ li(t7, Operand(StackFrame::TypeToMarker(type))); ++ ExternalReference c_entry_fp = ExternalReference::Create( ++ IsolateAddressId::kCEntryFPAddress, masm->isolate()); ++ __ li(s3, c_entry_fp); ++ __ Ldl(t8, MemOperand(s3)); ++ __ Push(s1, s2, s3, t8); ++ ++ // Clear c_entry_fp, now we've pushed its previous value to the stack. ++ // If the c_entry_fp is not already zero and we don't clear it, the ++ // SafeStackFrameIterator will assume we are executing C++ and miss the JS ++ // frames on top. ++ __ Stl(zero_reg, MemOperand(s3)); ++ ++ // Set up frame pointer for the frame to be pushed. ++ __ Addl(fp, sp, -EntryFrameConstants::kCallerFPOffset); ++ ++ // Registers: ++ // either ++ // a1: entry address ++ // a2: function ++ // a3: receiver ++ // a4: argc ++ // a5: argv ++ // or ++ // a1: microtask_queue ++ // ++ // Stack: ++ // caller fp | ++ // function slot | entry frame ++ // context slot | ++ // bad fp (0xFF...F) | ++ // callee saved registers + ra ++ // [ O32: 4 args slots] ++ // args ++ ++ // If this is the outermost JS call, set js_entry_sp value. ++ Label non_outermost_js; ++ ExternalReference js_entry_sp = ExternalReference::Create( ++ IsolateAddressId::kJSEntrySPAddress, masm->isolate()); ++ __ li(s1, js_entry_sp); ++ __ Ldl(s2, MemOperand(s1)); ++ __ Branch(&non_outermost_js, ne, s2, Operand(zero_reg)); ++ __ Stl(fp, MemOperand(s1)); ++ __ li(s3, Operand(StackFrame::OUTERMOST_JSENTRY_FRAME)); ++ Label cont; ++ __ br(&cont); ++ //__ nop(); // Branch delay slot nop. ++ __ bind(&non_outermost_js); ++ __ li(s3, Operand(StackFrame::INNER_JSENTRY_FRAME)); ++ __ bind(&cont); ++ __ Push(s3); ++ ++ // Jump to a faked try block that does the invoke, with a faked catch ++ // block that sets the pending exception. ++ __ jmp(&invoke); ++ __ bind(&handler_entry); ++ ++ // Store the current pc as the handler offset. It's used later to create the ++ // handler table. ++ masm->isolate()->builtins()->SetJSEntryHandlerOffset(handler_entry.pos()); ++ ++ // Caught exception: Store result (exception) in the pending exception ++ // field in the JSEnv and return a failure sentinel. Coming in here the ++ // fp will be invalid because the PushStackHandler below sets it to 0 to ++ // signal the existence of the JSEntry frame. ++ __ li(s1, ExternalReference::Create( ++ IsolateAddressId::kPendingExceptionAddress, masm->isolate())); ++ __ Stl(v0, MemOperand(s1)); // We come back from 'invoke'. result is in v0. ++ __ LoadRoot(v0, RootIndex::kException); ++ __ br(&exit); // b exposes branch delay slot. ++ //__ nop(); // Branch delay slot nop. ++ ++ // Invoke: Link this frame into the handler chain. ++ __ bind(&invoke); ++ __ PushStackHandler(); ++ // If an exception not caught by another handler occurs, this handler ++ // returns control to the code after the bal(&invoke) above, which ++ // restores all kCalleeSaved registers (including cp and fp) to their ++ // saved values before returning a failure to C. ++ // ++ // Registers: ++ // either ++ // a0: root register value ++ // a1: entry address ++ // a2: function ++ // a3: receiver ++ // a4: argc ++ // a5: argv ++ // or ++ // a0: root register value ++ // a1: microtask_queue ++ // ++ // Stack: ++ // handler frame ++ // entry frame ++ // callee saved registers + ra ++ // [ O32: 4 args slots] ++ // args ++ // ++ // Invoke the function by calling through JS entry trampoline builtin and ++ // pop the faked function when we return. ++ ++ Handle trampoline_code = ++ masm->isolate()->builtins()->code_handle(entry_trampoline); ++ __ Call(trampoline_code, RelocInfo::CODE_TARGET); ++ ++ // Unlink this frame from the handler chain. ++ __ PopStackHandler(); ++ ++ __ bind(&exit); // v0 holds result ++ // Check if the current stack frame is marked as the outermost JS frame. ++ Label non_outermost_js_2; ++ __ pop(a5); ++ __ Branch(&non_outermost_js_2, ne, a5, ++ Operand(StackFrame::OUTERMOST_JSENTRY_FRAME)); ++ __ li(a5, js_entry_sp); ++ __ Stl(zero_reg, MemOperand(a5)); ++ __ bind(&non_outermost_js_2); ++ ++ // Restore the top frame descriptors from the stack. ++ __ pop(a5); ++ __ li(a4, ExternalReference::Create(IsolateAddressId::kCEntryFPAddress, ++ masm->isolate())); ++ __ Stl(a5, MemOperand(a4)); ++ ++ // Reset the stack to the callee saved registers. ++ __ addl(sp, -EntryFrameConstants::kCallerFPOffset, ++ sp); // negative imm ++ ++ // Restore callee-saved fpu registers. ++ __ MultiPopFPU(kCalleeSavedFPU); ++ ++ // Restore callee saved registers from the stack. ++ __ MultiPop(kCalleeSaved | ra); ++ // Return. ++ __ Jump(ra); ++} ++ ++} // namespace ++ ++void Builtins::Generate_JSEntry(MacroAssembler* masm) { ++ Generate_JSEntryVariant(masm, StackFrame::ENTRY, Builtin::kJSEntryTrampoline); ++} ++ ++void Builtins::Generate_JSConstructEntry(MacroAssembler* masm) { ++ Generate_JSEntryVariant(masm, StackFrame::CONSTRUCT_ENTRY, ++ Builtin::kJSConstructEntryTrampoline); ++} ++ ++void Builtins::Generate_JSRunMicrotasksEntry(MacroAssembler* masm) { ++ Generate_JSEntryVariant(masm, StackFrame::ENTRY, ++ Builtin::kRunMicrotasksTrampoline); ++} ++ ++static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm, ++ bool is_construct) { ++ // ----------- S t a t e ------------- ++ // -- a1: new.target ++ // -- a2: function ++ // -- a3: receiver_pointer ++ // -- a4: argc ++ // -- a5: argv ++ // ----------------------------------- ++ ++ // Enter an internal frame. ++ { ++ FrameScope scope(masm, StackFrame::INTERNAL); ++ ++ // Setup the context (we need to use the caller context from the isolate). ++ ExternalReference context_address = ExternalReference::Create( ++ IsolateAddressId::kContextAddress, masm->isolate()); ++ __ li(cp, context_address); ++ __ Ldl(cp, MemOperand(cp)); ++ ++ // Push the function onto the stack. ++ __ Push(a2); ++ ++ // Check if we have enough stack space to push all arguments. ++ __ mov(t9, a4); ++ Generate_CheckStackOverflow(masm, t9, a0, s2); ++ ++ // Copy arguments to the stack in a loop. ++ // a4: argc ++ // a5: argv, i.e. points to first arg ++ Generate_PushArguments(masm, a5, a4, s1, s2, ArgumentsElementType::kHandle); ++ ++ // Push the receive. ++ __ Push(a3); ++ ++ // a0: argc ++ // a1: function ++ // a3: new.target ++ __ mov(a3, a1); ++ __ mov(a1, a2); ++ __ mov(a0, a4); ++ ++ // Initialize all JavaScript callee-saved registers, since they will be seen ++ // by the garbage collector as part of handlers. ++ __ LoadRoot(a4, RootIndex::kUndefinedValue); ++ __ mov(a5, a4); ++ __ mov(s1, a4); ++ __ mov(s2, a4); ++ __ mov(s3, a4); // OK. ++ // s4 holds the root address. Do not clobber. ++ // s5 is cp. Do not init. ++ ++ // Invoke the code. ++ Handle builtin = is_construct ++ ? BUILTIN_CODE(masm->isolate(), Construct) ++ : masm->isolate()->builtins()->Call(); ++ __ Call(builtin, RelocInfo::CODE_TARGET); ++ ++ // Leave internal frame. ++ } ++ __ Jump(ra); ++} ++ ++void Builtins::Generate_JSEntryTrampoline(MacroAssembler* masm) { ++ Generate_JSEntryTrampolineHelper(masm, false); ++} ++ ++void Builtins::Generate_JSConstructEntryTrampoline(MacroAssembler* masm) { ++ Generate_JSEntryTrampolineHelper(masm, true); ++} ++ ++void Builtins::Generate_RunMicrotasksTrampoline(MacroAssembler* masm) { ++ // a1: microtask_queue ++ __ mov(RunMicrotasksDescriptor::MicrotaskQueueRegister(), a1); ++ __ Jump(BUILTIN_CODE(masm->isolate(), RunMicrotasks), RelocInfo::CODE_TARGET); ++} ++ ++static void ReplaceClosureCodeWithOptimizedCode(MacroAssembler* masm, ++ Register optimized_code, ++ Register closure, ++ Register scratch1, ++ Register scratch2) { ++ DCHECK(!AreAliased(optimized_code, closure, scratch1, scratch2)); ++ // Store code entry in the closure. ++ __ Stl(optimized_code, FieldMemOperand(closure, JSFunction::kCodeOffset)); ++ __ mov(scratch1, optimized_code); // Write barrier clobbers scratch1 below. ++ __ RecordWriteField(closure, JSFunction::kCodeOffset, scratch1, scratch2, ++ kRAHasNotBeenSaved, SaveFPRegsMode::kIgnore, ++ RememberedSetAction::kOmit, SmiCheck::kOmit); ++} ++ ++static void LeaveInterpreterFrame(MacroAssembler* masm, Register scratch1, ++ Register scratch2) { ++ Register params_size = scratch1; ++ ++ // Get the size of the formal parameters + receiver (in bytes). ++ __ Ldl(params_size, ++ MemOperand(fp, InterpreterFrameConstants::kBytecodeArrayFromFp)); ++ __ Ldw(params_size, ++ FieldMemOperand(params_size, BytecodeArray::kParameterSizeOffset)); ++ ++ Register actual_params_size = scratch2; ++ // Compute the size of the actual parameters + receiver (in bytes). ++ __ Ldl(actual_params_size, ++ MemOperand(fp, StandardFrameConstants::kArgCOffset)); ++ __ slll(actual_params_size, kPointerSizeLog2, actual_params_size); ++ ++ // If actual is bigger than formal, then we should use it to free up the stack ++ // arguments. ++ __ cmplt(params_size, actual_params_size, t2); ++ __ Selne(params_size, actual_params_size, t2); ++ ++ // Leave the frame (also dropping the register file). ++ __ LeaveFrame(StackFrame::INTERPRETED); ++ ++ // Drop receiver + arguments. ++ __ DropArguments(params_size, TurboAssembler::kCountIsBytes, ++ TurboAssembler::kCountIncludesReceiver); ++} ++ ++// Tail-call |function_id| if |actual_state| == |expected_state| ++static void TailCallRuntimeIfStateEquals(MacroAssembler* masm, ++ Register actual_state, ++ TieringState expected_state, ++ Runtime::FunctionId function_id) { ++ Label no_match; ++ __ Branch(&no_match, ne, actual_state, ++ Operand(static_cast(expected_state))); ++ GenerateTailCallToReturnedCode(masm, function_id); ++ __ bind(&no_match); ++} ++ ++static void TailCallOptimizedCodeSlot(MacroAssembler* masm, ++ Register optimized_code_entry, ++ Register scratch1, Register scratch2) { ++ // ----------- S t a t e ------------- ++ // -- a0 : actual argument count ++ // -- a3 : new target (preserved for callee if needed, and caller) ++ // -- a1 : target function (preserved for callee if needed, and caller) ++ // ----------------------------------- ++ DCHECK(!AreAliased(optimized_code_entry, a1, a3, scratch1, scratch2)); ++ ++ Register closure = a1; ++ Label heal_optimized_code_slot; ++ ++ // If the optimized code is cleared, go to runtime to update the optimization ++ // marker field. ++ __ LoadWeakValue(optimized_code_entry, optimized_code_entry, ++ &heal_optimized_code_slot); ++ ++ // Check if the optimized code is marked for deopt. If it is, call the ++ // runtime to clear it. ++ __ Ldl(scratch1, ++ FieldMemOperand(optimized_code_entry, Code::kCodeDataContainerOffset)); ++ __ Ldw(scratch1, ++ FieldMemOperand(scratch1, CodeDataContainer::kKindSpecificFlagsOffset)); ++ __ And(scratch1, scratch1, Operand(1 << Code::kMarkedForDeoptimizationBit)); ++ __ Branch(&heal_optimized_code_slot, ne, scratch1, Operand(zero_reg)); ++ ++ // Optimized code is good, get it into the closure and link the closure into ++ // the optimized functions list, then tail call the optimized code. ++ // The feedback vector is no longer used, so re-use it as a scratch ++ // register. ++ ReplaceClosureCodeWithOptimizedCode(masm, optimized_code_entry, closure, ++ scratch1, scratch2); ++ ++ static_assert(kJavaScriptCallCodeStartRegister == a2, "ABI mismatch"); ++ __ Addl(a2, optimized_code_entry, ++ Operand(Code::kHeaderSize - kHeapObjectTag)); ++ __ Jump(a2); ++ ++ // Optimized code slot contains deoptimized code or code is cleared and ++ // optimized code marker isn't updated. Evict the code, update the marker ++ // and re-enter the closure's code. ++ __ bind(&heal_optimized_code_slot); ++ GenerateTailCallToReturnedCode(masm, Runtime::kHealOptimizedCodeSlot); ++} ++ ++static void MaybeOptimizeCode(MacroAssembler* masm, Register feedback_vector, ++ Register tiering_state) { ++ // ----------- S t a t e ------------- ++ // -- a0 : actual argument count ++ // -- a3 : new target (preserved for callee if needed, and caller) ++ // -- a1 : target function (preserved for callee if needed, and caller) ++ // -- feedback vector (preserved for caller if needed) ++ // -- tiering_state : a int32 containing a non-zero optimization ++ // marker. ++ // ----------------------------------- ++ DCHECK(!AreAliased(feedback_vector, a1, a3, tiering_state)); ++ ++ TailCallRuntimeIfStateEquals(masm, tiering_state, ++ TieringState::kRequestTurbofan_Synchronous, ++ Runtime::kCompileTurbofan_Synchronous); ++ TailCallRuntimeIfStateEquals(masm, tiering_state, ++ TieringState::kRequestTurbofan_Concurrent, ++ Runtime::kCompileTurbofan_Concurrent); ++ ++ __ halt(); ++} ++ ++// Advance the current bytecode offset. This simulates what all bytecode ++// handlers do upon completion of the underlying operation. Will bail out to a ++// label if the bytecode (without prefix) is a return bytecode. Will not advance ++// the bytecode offset if the current bytecode is a JumpLoop, instead just ++// re-executing the JumpLoop to jump to the correct bytecode. ++static void AdvanceBytecodeOffsetOrReturn(MacroAssembler* masm, ++ Register bytecode_array, ++ Register bytecode_offset, ++ Register bytecode, Register scratch1, ++ Register scratch2, Register scratch3, ++ Label* if_return) { ++ Register bytecode_size_table = scratch1; ++ ++ // The bytecode offset value will be increased by one in wide and extra wide ++ // cases. In the case of having a wide or extra wide JumpLoop bytecode, we ++ // will restore the original bytecode. In order to simplify the code, we have ++ // a backup of it. ++ Register original_bytecode_offset = scratch3; ++ DCHECK(!AreAliased(bytecode_array, bytecode_offset, bytecode, ++ bytecode_size_table, original_bytecode_offset)); ++ __ Move(original_bytecode_offset, bytecode_offset); ++ __ li(bytecode_size_table, ExternalReference::bytecode_size_table_address()); ++ ++ // Check if the bytecode is a Wide or ExtraWide prefix bytecode. ++ Label process_bytecode, extra_wide; ++ STATIC_ASSERT(0 == static_cast(interpreter::Bytecode::kWide)); ++ STATIC_ASSERT(1 == static_cast(interpreter::Bytecode::kExtraWide)); ++ STATIC_ASSERT(2 == static_cast(interpreter::Bytecode::kDebugBreakWide)); ++ STATIC_ASSERT(3 == ++ static_cast(interpreter::Bytecode::kDebugBreakExtraWide)); ++ __ Branch(&process_bytecode, hi, bytecode, Operand(3)); ++ __ And(scratch2, bytecode, Operand(1)); ++ __ Branch(&extra_wide, ne, scratch2, Operand(zero_reg)); ++ ++ // Load the next bytecode and update table to the wide scaled table. ++ __ Addl(bytecode_offset, bytecode_offset, Operand(1)); ++ __ Addl(scratch2, bytecode_array, bytecode_offset); ++ __ Ldbu(bytecode, MemOperand(scratch2)); ++ __ Addl(bytecode_size_table, bytecode_size_table, ++ Operand(kByteSize * interpreter::Bytecodes::kBytecodeCount)); ++ __ jmp(&process_bytecode); ++ ++ __ bind(&extra_wide); ++ // Load the next bytecode and update table to the extra wide scaled table. ++ __ Addl(bytecode_offset, bytecode_offset, Operand(1)); ++ __ Addl(scratch2, bytecode_array, bytecode_offset); ++ __ Ldbu(bytecode, MemOperand(scratch2)); ++ __ Addl(bytecode_size_table, bytecode_size_table, ++ Operand(2 * kByteSize * interpreter::Bytecodes::kBytecodeCount)); ++ ++ __ bind(&process_bytecode); ++ ++// Bailout to the return label if this is a return bytecode. ++#define JUMP_IF_EQUAL(NAME) \ ++ __ Branch(if_return, eq, bytecode, \ ++ Operand(static_cast(interpreter::Bytecode::k##NAME))); ++ RETURN_BYTECODE_LIST(JUMP_IF_EQUAL) ++#undef JUMP_IF_EQUAL ++ ++ // If this is a JumpLoop, re-execute it to perform the jump to the beginning ++ // of the loop. ++ Label end, not_jump_loop; ++ __ Branch(¬_jump_loop, ne, bytecode, ++ Operand(static_cast(interpreter::Bytecode::kJumpLoop))); ++ // We need to restore the original bytecode_offset since we might have ++ // increased it to skip the wide / extra-wide prefix bytecode. ++ __ Move(bytecode_offset, original_bytecode_offset); ++ __ jmp(&end); ++ ++ __ bind(¬_jump_loop); ++ // Otherwise, load the size of the current bytecode and advance the offset. ++ __ Addl(scratch2, bytecode_size_table, bytecode); ++ __ Ldb(scratch2, MemOperand(scratch2)); ++ __ Addl(bytecode_offset, bytecode_offset, scratch2); ++ ++ __ bind(&end); ++} ++ ++// Read off the optimization state in the feedback vector and check if there ++// is optimized code or a tiering state that needs to be processed. ++static void LoadTieringStateAndJumpIfNeedsProcessing( ++ MacroAssembler* masm, Register optimization_state, Register feedback_vector, ++ Label* has_optimized_code_or_state) { ++ ASM_CODE_COMMENT(masm); ++ Register scratch = t2; ++ __ Ldw(optimization_state, ++ FieldMemOperand(feedback_vector, FeedbackVector::kFlagsOffset)); ++ __ And( ++ scratch, optimization_state, ++ Operand(FeedbackVector::kHasOptimizedCodeOrTieringStateIsAnyRequestMask)); ++ __ Branch(has_optimized_code_or_state, ne, scratch, Operand(zero_reg)); ++} ++ ++static void MaybeOptimizeCodeOrTailCallOptimizedCodeSlot( ++ MacroAssembler* masm, Register optimization_state, ++ Register feedback_vector) { ++ ASM_CODE_COMMENT(masm); ++ Label maybe_has_optimized_code; ++ // Check if optimized code marker is available ++ { ++ UseScratchRegisterScope temps(masm); ++ Register scratch = temps.Acquire(); ++ __ And(scratch, optimization_state, ++ Operand(FeedbackVector::kTieringStateIsAnyRequestMask)); ++ __ Branch(&maybe_has_optimized_code, eq, scratch, Operand(zero_reg)); ++ } ++ ++ Register tiering_state = optimization_state; ++ __ DecodeField(tiering_state); ++ MaybeOptimizeCode(masm, feedback_vector, tiering_state); ++ ++ __ bind(&maybe_has_optimized_code); ++ Register optimized_code_entry = optimization_state; ++ __ Ldl(tiering_state, ++ FieldMemOperand(feedback_vector, ++ FeedbackVector::kMaybeOptimizedCodeOffset)); ++ TailCallOptimizedCodeSlot(masm, optimized_code_entry, t3, a5); ++} ++ ++namespace { ++void ResetBytecodeAgeAndOsrState(MacroAssembler* masm, ++ Register bytecode_array) { ++ // Reset code age and the OSR state (optimized to a single write). ++ static_assert(BytecodeArray::kOsrStateAndBytecodeAgeAreContiguous32Bits); ++ STATIC_ASSERT(BytecodeArray::kNoAgeBytecodeAge == 0); ++ __ Stw(zero_reg, ++ FieldMemOperand(bytecode_array, ++ BytecodeArray::kOsrUrgencyAndInstallTargetOffset)); ++} ++} // namespace ++ ++// static ++void Builtins::Generate_BaselineOutOfLinePrologue(MacroAssembler* masm) { ++ UseScratchRegisterScope temps(masm); ++ temps.Include({s1, s2}); ++ auto descriptor = ++ Builtins::CallInterfaceDescriptorFor(Builtin::kBaselineOutOfLinePrologue); ++ Register closure = descriptor.GetRegisterParameter( ++ BaselineOutOfLinePrologueDescriptor::kClosure); ++ // Load the feedback vector from the closure. ++ Register feedback_vector = temps.Acquire(); ++ __ Ldl(feedback_vector, ++ FieldMemOperand(closure, JSFunction::kFeedbackCellOffset)); ++ __ Ldl(feedback_vector, FieldMemOperand(feedback_vector, Cell::kValueOffset)); ++ if (FLAG_debug_code) { ++ UseScratchRegisterScope temps(masm); ++ Register scratch = temps.Acquire(); ++ __ GetObjectType(feedback_vector, scratch, scratch); ++ __ Assert(eq, AbortReason::kExpectedFeedbackVector, scratch, ++ Operand(FEEDBACK_VECTOR_TYPE)); ++ } ++ // Check for an tiering state. ++ Label has_optimized_code_or_state; ++ Register optimization_state = no_reg; ++ { ++ UseScratchRegisterScope temps(masm); ++ optimization_state = temps.Acquire(); ++ // optimization_state will be used only in |has_optimized_code_or_state| ++ // and outside it can be reused. ++ LoadTieringStateAndJumpIfNeedsProcessing(masm, optimization_state, ++ feedback_vector, ++ &has_optimized_code_or_state); ++ } ++ // Increment invocation count for the function. ++ { ++ UseScratchRegisterScope temps(masm); ++ Register invocation_count = temps.Acquire(); ++ __ Ldw(invocation_count, ++ FieldMemOperand(feedback_vector, ++ FeedbackVector::kInvocationCountOffset)); ++ __ Addw(invocation_count, invocation_count, Operand(1)); ++ __ Stw(invocation_count, ++ FieldMemOperand(feedback_vector, ++ FeedbackVector::kInvocationCountOffset)); ++ } ++ ++ FrameScope frame_scope(masm, StackFrame::MANUAL); ++ { ++ ASM_CODE_COMMENT_STRING(masm, "Frame Setup"); ++ // Normally the first thing we'd do here is Push(ra, fp), but we already ++ // entered the frame in BaselineCompiler::Prologue, as we had to use the ++ // value lr before the call to this BaselineOutOfLinePrologue builtin. ++ Register callee_context = descriptor.GetRegisterParameter( ++ BaselineOutOfLinePrologueDescriptor::kCalleeContext); ++ Register callee_js_function = descriptor.GetRegisterParameter( ++ BaselineOutOfLinePrologueDescriptor::kClosure); ++ __ Push(callee_context, callee_js_function); ++ DCHECK_EQ(callee_js_function, kJavaScriptCallTargetRegister); ++ DCHECK_EQ(callee_js_function, kJSFunctionRegister); ++ ++ Register argc = descriptor.GetRegisterParameter( ++ BaselineOutOfLinePrologueDescriptor::kJavaScriptCallArgCount); ++ // We'll use the bytecode for both code age/OSR resetting, and pushing onto ++ // the frame, so load it into a register. ++ Register bytecode_array = descriptor.GetRegisterParameter( ++ BaselineOutOfLinePrologueDescriptor::kInterpreterBytecodeArray); ++ ResetBytecodeAgeAndOsrState(masm, bytecode_array); ++ __ Push(argc, bytecode_array); ++ ++ // Baseline code frames store the feedback vector where interpreter would ++ // store the bytecode offset. ++ if (FLAG_debug_code) { ++ UseScratchRegisterScope temps(masm); ++ Register invocation_count = temps.Acquire(); ++ __ GetObjectType(feedback_vector, invocation_count, invocation_count); ++ __ Assert(eq, AbortReason::kExpectedFeedbackVector, invocation_count, ++ Operand(FEEDBACK_VECTOR_TYPE)); ++ } ++ // Our stack is currently aligned. We have have to push something along with ++ // the feedback vector to keep it that way -- we may as well start ++ // initialising the register frame. ++ // TODO(v8:11429,leszeks): Consider guaranteeing that this call leaves ++ // `undefined` in the accumulator register, to skip the load in the baseline ++ // code. ++ __ Push(feedback_vector); ++ } ++ ++ Label call_stack_guard; ++ Register frame_size = descriptor.GetRegisterParameter( ++ BaselineOutOfLinePrologueDescriptor::kStackFrameSize); ++ { ++ ASM_CODE_COMMENT_STRING(masm, "Stack/interrupt check"); ++ // Stack check. This folds the checks for both the interrupt stack limit ++ // check and the real stack limit into one by just checking for the ++ // interrupt limit. The interrupt limit is either equal to the real stack ++ // limit or tighter. By ensuring we have space until that limit after ++ // building the frame we can quickly precheck both at once. ++ UseScratchRegisterScope temps(masm); ++ Register sp_minus_frame_size = temps.Acquire(); ++ __ Subl(sp_minus_frame_size, sp, frame_size); ++ Register interrupt_limit = temps.Acquire(); ++ __ LoadStackLimit(interrupt_limit, ++ MacroAssembler::StackLimitKind::kInterruptStackLimit); ++ __ Branch(&call_stack_guard, Uless, sp_minus_frame_size, ++ Operand(interrupt_limit)); ++ } ++ ++ // Do "fast" return to the caller pc in ra. ++ // TODO(v8:11429): Document this frame setup better. ++ __ Ret(); ++ ++ __ bind(&has_optimized_code_or_state); ++ { ++ ASM_CODE_COMMENT_STRING(masm, "Optimized marker check"); ++ UseScratchRegisterScope temps(masm); ++ temps.Exclude(optimization_state); ++ // Ensure the optimization_state is not allocated again. ++ // Drop the frame created by the baseline call. ++ __ Pop(ra, fp); ++ MaybeOptimizeCodeOrTailCallOptimizedCodeSlot(masm, optimization_state, ++ feedback_vector); ++ __ Trap(); ++ } ++ ++ __ bind(&call_stack_guard); ++ { ++ ASM_CODE_COMMENT_STRING(masm, "Stack/interrupt call"); ++ FrameScope frame_scope(masm, StackFrame::INTERNAL); ++ // Save incoming new target or generator ++ __ Push(kJavaScriptCallNewTargetRegister); ++ __ SmiTag(frame_size); ++ __ Push(frame_size); ++ __ CallRuntime(Runtime::kStackGuardWithGap); ++ __ Pop(kJavaScriptCallNewTargetRegister); ++ } ++ __ Ret(); ++ temps.Exclude({kScratchReg, kScratchReg2}); ++} ++ ++// Generate code for entering a JS function with the interpreter. ++// On entry to the function the receiver and arguments have been pushed on the ++// stack left to right. ++// ++// The live registers are: ++// o a0 : actual argument count ++// o a1: the JS function object being called. ++// o a3: the incoming new target or generator object ++// o cp: our context ++// o fp: the caller's frame pointer ++// o sp: stack pointer ++// o ra: return address ++// ++// The function builds an interpreter frame. See InterpreterFrameConstants in ++// frame-constants.h for its layout. ++void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) { ++ Register closure = a1; ++ Register feedback_vector = a2; ++ ++ // Get the bytecode array from the function object and load it into ++ // kInterpreterBytecodeArrayRegister. ++ __ Ldl(kScratchReg, ++ FieldMemOperand(closure, JSFunction::kSharedFunctionInfoOffset)); ++ __ Ldl(kInterpreterBytecodeArrayRegister, ++ FieldMemOperand(kScratchReg, SharedFunctionInfo::kFunctionDataOffset)); ++ Label is_baseline; ++ GetSharedFunctionInfoBytecodeOrBaseline( ++ masm, kInterpreterBytecodeArrayRegister, kScratchReg, &is_baseline); ++ ++ // The bytecode array could have been flushed from the shared function info, ++ // if so, call into CompileLazy. ++ Label compile_lazy; ++ __ GetObjectType(kInterpreterBytecodeArrayRegister, kScratchReg, kScratchReg); ++ __ Branch(&compile_lazy, ne, kScratchReg, Operand(BYTECODE_ARRAY_TYPE)); ++ ++ // Load the feedback vector from the closure. ++ __ Ldl(feedback_vector, ++ FieldMemOperand(closure, JSFunction::kFeedbackCellOffset)); ++ __ Ldl(feedback_vector, FieldMemOperand(feedback_vector, Cell::kValueOffset)); ++ ++ Label push_stack_frame; ++ // Check if feedback vector is valid. If valid, check for optimized code ++ // and update invocation count. Otherwise, setup the stack frame. ++ __ Ldl(a4, FieldMemOperand(feedback_vector, HeapObject::kMapOffset)); ++ __ Ldhu(a4, FieldMemOperand(a4, Map::kInstanceTypeOffset)); ++ __ Branch(&push_stack_frame, ne, a4, Operand(FEEDBACK_VECTOR_TYPE)); ++ ++ // Read off the optimization state in the feedback vector, and if there ++ // is optimized code or an tiering state, call that instead. ++ Register optimization_state = a4; ++ __ Ldw(optimization_state, ++ FieldMemOperand(feedback_vector, FeedbackVector::kFlagsOffset)); ++ ++ // Check if the optimized code slot is not empty or has a tiering state. ++ Label has_optimized_code_or_state; ++ ++ __ And(t0, optimization_state, ++ FeedbackVector::kHasOptimizedCodeOrTieringStateIsAnyRequestMask); ++ __ Branch(&has_optimized_code_or_state, ne, t0, Operand(zero_reg)); ++ ++ Label not_optimized; ++ __ bind(¬_optimized); ++ ++ // Increment invocation count for the function. ++ __ Ldw(a4, FieldMemOperand(feedback_vector, ++ FeedbackVector::kInvocationCountOffset)); ++ __ Addw(a4, a4, Operand(1)); ++ __ Stw(a4, FieldMemOperand(feedback_vector, ++ FeedbackVector::kInvocationCountOffset)); ++ ++ // Open a frame scope to indicate that there is a frame on the stack. The ++ // MANUAL indicates that the scope shouldn't actually generate code to set up ++ // the frame (that is done below). ++ __ bind(&push_stack_frame); ++ FrameScope frame_scope(masm, StackFrame::MANUAL); ++ __ PushStandardFrame(closure); ++ ++ ResetBytecodeAgeAndOsrState(masm, kInterpreterBytecodeArrayRegister); ++ ++ // Load initial bytecode offset. ++ __ li(kInterpreterBytecodeOffsetRegister, ++ Operand(BytecodeArray::kHeaderSize - kHeapObjectTag)); ++ ++ // Push bytecode array and Smi tagged bytecode array offset. ++ __ SmiTag(a4, kInterpreterBytecodeOffsetRegister); ++ __ Push(kInterpreterBytecodeArrayRegister, a4); ++ ++ // Allocate the local and temporary register file on the stack. ++ Label stack_overflow; ++ { ++ // Load frame size (word) from the BytecodeArray object. ++ __ Ldw(a4, FieldMemOperand(kInterpreterBytecodeArrayRegister, ++ BytecodeArray::kFrameSizeOffset)); ++ ++ // Do a stack check to ensure we don't go over the limit. ++ __ Subl(a5, sp, Operand(a4)); ++ __ LoadStackLimit(a2, MacroAssembler::StackLimitKind::kRealStackLimit); ++ __ Branch(&stack_overflow, lo, a5, Operand(a2)); ++ ++ // If ok, push undefined as the initial value for all register file entries. ++ Label loop_header; ++ Label loop_check; ++ __ LoadRoot(kInterpreterAccumulatorRegister, RootIndex::kUndefinedValue); ++ __ Branch(&loop_check); ++ __ bind(&loop_header); ++ // TODO(rmcilroy): Consider doing more than one push per loop iteration. ++ __ Push(kInterpreterAccumulatorRegister); ++ // Continue loop if not done. ++ __ bind(&loop_check); ++ __ Subl(a4, a4, Operand(kPointerSize)); ++ __ Branch(&loop_header, ge, a4, Operand(zero_reg)); ++ } ++ ++ // If the bytecode array has a valid incoming new target or generator object ++ // register, initialize it with incoming value which was passed in r3. ++ Label no_incoming_new_target_or_generator_register; ++ __ Ldw(a5, FieldMemOperand( ++ kInterpreterBytecodeArrayRegister, ++ BytecodeArray::kIncomingNewTargetOrGeneratorRegisterOffset)); ++ __ Branch(&no_incoming_new_target_or_generator_register, eq, a5, ++ Operand(zero_reg)); ++ __ s8addl(a5, fp, a5); ++ DCHECK_EQ(kSystemPointerSizeLog2, 3); ++ __ Stl(a3, MemOperand(a5)); ++ __ bind(&no_incoming_new_target_or_generator_register); ++ ++ // Perform interrupt stack check. ++ // TODO(solanes): Merge with the real stack limit check above. ++ Label stack_check_interrupt, after_stack_check_interrupt; ++ __ LoadStackLimit(a5, MacroAssembler::StackLimitKind::kInterruptStackLimit); ++ __ Branch(&stack_check_interrupt, lo, sp, Operand(a5)); ++ __ bind(&after_stack_check_interrupt); ++ ++ // The accumulator is already loaded with undefined. ++ ++ // Load the dispatch table into a register and dispatch to the bytecode ++ // handler at the current bytecode offset. ++ Label do_dispatch; ++ __ bind(&do_dispatch); ++ __ li(kInterpreterDispatchTableRegister, ++ ExternalReference::interpreter_dispatch_table_address(masm->isolate())); ++ __ Addl(a0, kInterpreterBytecodeArrayRegister, ++ kInterpreterBytecodeOffsetRegister); ++ __ Ldbu(t10, MemOperand(a0)); ++ __ Lsal(kScratchReg, kInterpreterDispatchTableRegister, t10, kPointerSizeLog2); ++ __ Ldl(kJavaScriptCallCodeStartRegister, MemOperand(kScratchReg)); ++ __ Call(kJavaScriptCallCodeStartRegister); ++ masm->isolate()->heap()->SetInterpreterEntryReturnPCOffset(masm->pc_offset()); ++ ++ // Any returns to the entry trampoline are either due to the return bytecode ++ // or the interpreter tail calling a builtin and then a dispatch. ++ ++ // Get bytecode array and bytecode offset from the stack frame. ++ __ Ldl(kInterpreterBytecodeArrayRegister, ++ MemOperand(fp, InterpreterFrameConstants::kBytecodeArrayFromFp)); ++ __ Ldl(kInterpreterBytecodeOffsetRegister, ++ MemOperand(fp, InterpreterFrameConstants::kBytecodeOffsetFromFp)); ++ __ SmiUntag(kInterpreterBytecodeOffsetRegister); ++ ++ // Either return, or advance to the next bytecode and dispatch. ++ Label do_return; ++ __ Addl(a1, kInterpreterBytecodeArrayRegister, ++ kInterpreterBytecodeOffsetRegister); ++ __ Ldbu(a1, MemOperand(a1)); ++ AdvanceBytecodeOffsetOrReturn(masm, kInterpreterBytecodeArrayRegister, ++ kInterpreterBytecodeOffsetRegister, a1, a2, a3, ++ a4, &do_return); ++ __ jmp(&do_dispatch); ++ ++ __ bind(&do_return); ++ // The return value is in v0. ++ LeaveInterpreterFrame(masm, t0, t1); ++ __ Jump(ra); ++ ++ __ bind(&stack_check_interrupt); ++ // Modify the bytecode offset in the stack to be kFunctionEntryBytecodeOffset ++ // for the call to the StackGuard. ++ __ li(kInterpreterBytecodeOffsetRegister, ++ Operand(Smi::FromInt(BytecodeArray::kHeaderSize - kHeapObjectTag + ++ kFunctionEntryBytecodeOffset))); ++ __ Stl(kInterpreterBytecodeOffsetRegister, ++ MemOperand(fp, InterpreterFrameConstants::kBytecodeOffsetFromFp)); ++ __ CallRuntime(Runtime::kStackGuard); ++ ++ // After the call, restore the bytecode array, bytecode offset and accumulator ++ // registers again. Also, restore the bytecode offset in the stack to its ++ // previous value. ++ __ Ldl(kInterpreterBytecodeArrayRegister, ++ MemOperand(fp, InterpreterFrameConstants::kBytecodeArrayFromFp)); ++ __ li(kInterpreterBytecodeOffsetRegister, ++ Operand(BytecodeArray::kHeaderSize - kHeapObjectTag)); ++ __ LoadRoot(kInterpreterAccumulatorRegister, RootIndex::kUndefinedValue); ++ ++ __ SmiTag(a5, kInterpreterBytecodeOffsetRegister); ++ __ Stl(a5, MemOperand(fp, InterpreterFrameConstants::kBytecodeOffsetFromFp)); ++ ++ __ jmp(&after_stack_check_interrupt); ++ ++ __ bind(&has_optimized_code_or_state); ++ MaybeOptimizeCodeOrTailCallOptimizedCodeSlot(masm, optimization_state, ++ feedback_vector); ++ __ bind(&is_baseline); ++ { ++ // Load the feedback vector from the closure. ++ __ Ldl(feedback_vector, ++ FieldMemOperand(closure, JSFunction::kFeedbackCellOffset)); ++ __ Ldl(feedback_vector, ++ FieldMemOperand(feedback_vector, Cell::kValueOffset)); ++ ++ Label install_baseline_code; ++ // Check if feedback vector is valid. If not, call prepare for baseline to ++ // allocate it. ++ __ Ldl(t0, FieldMemOperand(feedback_vector, HeapObject::kMapOffset)); ++ __ Ldhu(t0, FieldMemOperand(t0, Map::kInstanceTypeOffset)); ++ __ Branch(&install_baseline_code, ne, t0, Operand(FEEDBACK_VECTOR_TYPE)); ++ ++ // Check for an tiering state. ++ LoadTieringStateAndJumpIfNeedsProcessing(masm, optimization_state, ++ feedback_vector, ++ &has_optimized_code_or_state); ++ ++ // Load the baseline code into the closure. ++ __ Move(a2, kInterpreterBytecodeArrayRegister); ++ static_assert(kJavaScriptCallCodeStartRegister == a2, "ABI mismatch"); ++ ReplaceClosureCodeWithOptimizedCode(masm, a2, closure, t0, t1); ++ __ JumpCodeObject(a2); ++ ++ __ bind(&install_baseline_code); ++ GenerateTailCallToReturnedCode(masm, Runtime::kInstallBaselineCode); ++ } ++ __ bind(&compile_lazy); ++ GenerateTailCallToReturnedCode(masm, Runtime::kCompileLazy); ++ // Unreachable code. ++ __ sys_call(0x80); ++ ++ __ bind(&stack_overflow); ++ __ CallRuntime(Runtime::kThrowStackOverflow); ++ // Unreachable code. ++ __ sys_call(0x80); ++} ++ ++static void GenerateInterpreterPushArgs(MacroAssembler* masm, Register num_args, ++ Register start_address, ++ Register scratch, Register scratch2) { ++ // Find the address of the last argument. ++ __ Subl(scratch, num_args, Operand(1)); ++ __ slll(scratch, kPointerSizeLog2, scratch); ++ __ Subl(start_address, start_address, scratch); ++ ++ // Push the arguments. ++ __ PushArray(start_address, num_args, scratch, scratch2, ++ TurboAssembler::PushArrayOrder::kReverse); ++} ++ ++// static ++void Builtins::Generate_InterpreterPushArgsThenCallImpl( ++ MacroAssembler* masm, ConvertReceiverMode receiver_mode, ++ InterpreterPushArgsMode mode) { ++ DCHECK(mode != InterpreterPushArgsMode::kArrayFunction); ++ // ----------- S t a t e ------------- ++ // -- a0 : the number of arguments ++ // -- a2 : the address of the first argument to be pushed. Subsequent ++ // arguments should be consecutive above this, in the same order as ++ // they are to be pushed onto the stack. ++ // -- a1 : the target to call (can be any Object). ++ // ----------------------------------- ++ Label stack_overflow; ++ if (mode == InterpreterPushArgsMode::kWithFinalSpread) { ++ // The spread argument should not be pushed. ++ __ Subl(a0, a0, Operand(1)); ++ } ++ ++ if (receiver_mode == ConvertReceiverMode::kNullOrUndefined) { ++ __ Subl(a3, a0, Operand(kJSArgcReceiverSlots)); ++ } else { ++ __ mov(a3, a0); ++ } ++ ++ __ StackOverflowCheck(a3, a4, t0, &stack_overflow); ++ ++ // This function modifies a2, t0 and a4. ++ GenerateInterpreterPushArgs(masm, a3, a2, a4, t0); ++ ++ if (receiver_mode == ConvertReceiverMode::kNullOrUndefined) { ++ __ PushRoot(RootIndex::kUndefinedValue); ++ } ++ ++ if (mode == InterpreterPushArgsMode::kWithFinalSpread) { ++ // Pass the spread in the register a2. ++ // a2 already points to the penultime argument, the spread ++ // is below that. ++ __ Ldl(a2, MemOperand(a2, -kSystemPointerSize)); ++ } ++ ++ // Call the target. ++ if (mode == InterpreterPushArgsMode::kWithFinalSpread) { ++ __ Jump(BUILTIN_CODE(masm->isolate(), CallWithSpread), ++ RelocInfo::CODE_TARGET); ++ } else { ++ __ Jump(masm->isolate()->builtins()->Call(ConvertReceiverMode::kAny), ++ RelocInfo::CODE_TARGET); ++ } ++ ++ __ bind(&stack_overflow); ++ { ++ __ TailCallRuntime(Runtime::kThrowStackOverflow); ++ // Unreachable code. ++ __ sys_call(0x80); ++ } ++} ++ ++// static ++void Builtins::Generate_InterpreterPushArgsThenConstructImpl( ++ MacroAssembler* masm, InterpreterPushArgsMode mode) { ++ // ----------- S t a t e ------------- ++ // -- a0 : argument count ++ // -- a3 : new target ++ // -- a1 : constructor to call ++ // -- a2 : allocation site feedback if available, undefined otherwise. ++ // -- a4 : address of the first argument ++ // ----------------------------------- ++ Label stack_overflow; ++ __ StackOverflowCheck(a0, a5, t0, &stack_overflow); ++ ++ if (mode == InterpreterPushArgsMode::kWithFinalSpread) { ++ // The spread argument should not be pushed. ++ __ Subl(a0, a0, Operand(1)); ++ } ++ ++ Register argc_without_receiver = t9; ++ __ Subl(argc_without_receiver, a0, Operand(kJSArgcReceiverSlots)); ++ // Push the arguments, This function modifies t0, a4 and a5. ++ GenerateInterpreterPushArgs(masm, argc_without_receiver, a4, a5, t0); ++ ++ // Push a slot for the receiver. ++ __ Push(zero_reg); ++ ++ if (mode == InterpreterPushArgsMode::kWithFinalSpread) { ++ // Pass the spread in the register a2. ++ // a4 already points to the penultimate argument, the spread ++ // lies in the next interpreter register. ++ __ Ldl(a2, MemOperand(a4, -kSystemPointerSize)); ++ } else { ++ __ AssertUndefinedOrAllocationSite(a2, t0); ++ } ++ ++ if (mode == InterpreterPushArgsMode::kArrayFunction) { ++ __ AssertFunction(a1); ++ ++ // Tail call to the function-specific construct stub (still in the caller ++ // context at this point). ++ __ Jump(BUILTIN_CODE(masm->isolate(), ArrayConstructorImpl), ++ RelocInfo::CODE_TARGET); ++ } else if (mode == InterpreterPushArgsMode::kWithFinalSpread) { ++ // Call the constructor with a0, a1, and a3 unmodified. ++ __ Jump(BUILTIN_CODE(masm->isolate(), ConstructWithSpread), ++ RelocInfo::CODE_TARGET); ++ } else { ++ DCHECK_EQ(InterpreterPushArgsMode::kOther, mode); ++ // Call the constructor with a0, a1, and a3 unmodified. ++ __ Jump(BUILTIN_CODE(masm->isolate(), Construct), RelocInfo::CODE_TARGET); ++ } ++ ++ __ bind(&stack_overflow); ++ { ++ __ TailCallRuntime(Runtime::kThrowStackOverflow); ++ // Unreachable code. ++ __ sys_call(0x80); ++ } ++} ++ ++static void Generate_InterpreterEnterBytecode(MacroAssembler* masm) { ++ // Set the return address to the correct point in the interpreter entry ++ // trampoline. ++ Label builtin_trampoline, trampoline_loaded; ++ Smi interpreter_entry_return_pc_offset( ++ masm->isolate()->heap()->interpreter_entry_return_pc_offset()); ++ DCHECK_NE(interpreter_entry_return_pc_offset, Smi::zero()); ++ ++ // If the SFI function_data is an InterpreterData, the function will have a ++ // custom copy of the interpreter entry trampoline for profiling. If so, ++ // get the custom trampoline, otherwise grab the entry address of the global ++ // trampoline. ++ __ Ldl(t0, MemOperand(fp, StandardFrameConstants::kFunctionOffset)); ++ __ Ldl(t0, FieldMemOperand(t0, JSFunction::kSharedFunctionInfoOffset)); ++ __ Ldl(t0, FieldMemOperand(t0, SharedFunctionInfo::kFunctionDataOffset)); ++ __ GetObjectType(t0, kInterpreterDispatchTableRegister, ++ kInterpreterDispatchTableRegister); ++ __ Branch(&builtin_trampoline, ne, kInterpreterDispatchTableRegister, ++ Operand(INTERPRETER_DATA_TYPE)); ++ ++ __ Ldl(t0, ++ FieldMemOperand(t0, InterpreterData::kInterpreterTrampolineOffset)); ++ __ Addl(t0, t0, Operand(Code::kHeaderSize - kHeapObjectTag)); ++ __ Branch(&trampoline_loaded); ++ ++ __ bind(&builtin_trampoline); ++ __ li(t0, ExternalReference:: ++ address_of_interpreter_entry_trampoline_instruction_start( ++ masm->isolate())); ++ __ Ldl(t0, MemOperand(t0)); ++ ++ __ bind(&trampoline_loaded); ++ __ Addl(ra, t0, Operand(interpreter_entry_return_pc_offset.value())); ++ ++ // Initialize the dispatch table register. ++ __ li(kInterpreterDispatchTableRegister, ++ ExternalReference::interpreter_dispatch_table_address(masm->isolate())); ++ ++ // Get the bytecode array pointer from the frame. ++ __ Ldl(kInterpreterBytecodeArrayRegister, ++ MemOperand(fp, InterpreterFrameConstants::kBytecodeArrayFromFp)); ++ ++ if (FLAG_debug_code) { ++ // Check function data field is actually a BytecodeArray object. ++ __ SmiTst(kInterpreterBytecodeArrayRegister, kScratchReg); ++ __ Assert(ne, ++ AbortReason::kFunctionDataShouldBeBytecodeArrayOnInterpreterEntry, ++ kScratchReg, Operand(zero_reg)); ++ __ GetObjectType(kInterpreterBytecodeArrayRegister, a1, a1); ++ __ Assert(eq, ++ AbortReason::kFunctionDataShouldBeBytecodeArrayOnInterpreterEntry, ++ a1, Operand(BYTECODE_ARRAY_TYPE)); ++ } ++ ++ // Get the target bytecode offset from the frame. ++ __ SmiUntag(kInterpreterBytecodeOffsetRegister, ++ MemOperand(fp, InterpreterFrameConstants::kBytecodeOffsetFromFp)); ++ ++ if (FLAG_debug_code) { ++ Label okay; ++ __ Branch(&okay, ge, kInterpreterBytecodeOffsetRegister, ++ Operand(BytecodeArray::kHeaderSize - kHeapObjectTag)); ++ // Unreachable code. ++ __ sys_call(0x80); ++ __ bind(&okay); ++ } ++ ++ // Dispatch to the target bytecode. ++ __ Addl(a1, kInterpreterBytecodeArrayRegister, ++ kInterpreterBytecodeOffsetRegister); ++ __ Ldbu(t10, MemOperand(a1)); ++ __ s8addl(t10, kInterpreterDispatchTableRegister, a1); ++ DCHECK_EQ(kSystemPointerSizeLog2, 3); ++ __ Ldl(kJavaScriptCallCodeStartRegister, MemOperand(a1)); ++ __ Jump(kJavaScriptCallCodeStartRegister); ++} ++ ++void Builtins::Generate_InterpreterEnterAtNextBytecode(MacroAssembler* masm) { ++ // Advance the current bytecode offset stored within the given interpreter ++ // stack frame. This simulates what all bytecode handlers do upon completion ++ // of the underlying operation. ++ __ Ldl(kInterpreterBytecodeArrayRegister, ++ MemOperand(fp, InterpreterFrameConstants::kBytecodeArrayFromFp)); ++ __ Ldl(kInterpreterBytecodeOffsetRegister, ++ MemOperand(fp, InterpreterFrameConstants::kBytecodeOffsetFromFp)); ++ __ SmiUntag(kInterpreterBytecodeOffsetRegister); ++ ++ Label enter_bytecode, function_entry_bytecode; ++ __ Branch(&function_entry_bytecode, eq, kInterpreterBytecodeOffsetRegister, ++ Operand(BytecodeArray::kHeaderSize - kHeapObjectTag + ++ kFunctionEntryBytecodeOffset)); ++ ++ // Load the current bytecode. ++ __ Addl(a1, kInterpreterBytecodeArrayRegister, ++ kInterpreterBytecodeOffsetRegister); ++ __ Ldbu(a1, MemOperand(a1)); ++ ++ // Advance to the next bytecode. ++ Label if_return; ++ AdvanceBytecodeOffsetOrReturn(masm, kInterpreterBytecodeArrayRegister, ++ kInterpreterBytecodeOffsetRegister, a1, a2, a3, ++ a4, &if_return); ++ ++ __ bind(&enter_bytecode); ++ // Convert new bytecode offset to a Smi and save in the stackframe. ++ __ SmiTag(a2, kInterpreterBytecodeOffsetRegister); ++ __ Stl(a2, MemOperand(fp, InterpreterFrameConstants::kBytecodeOffsetFromFp)); ++ ++ Generate_InterpreterEnterBytecode(masm); ++ ++ __ bind(&function_entry_bytecode); ++ // If the code deoptimizes during the implicit function entry stack interrupt ++ // check, it will have a bailout ID of kFunctionEntryBytecodeOffset, which is ++ // not a valid bytecode offset. Detect this case and advance to the first ++ // actual bytecode. ++ __ li(kInterpreterBytecodeOffsetRegister, ++ Operand(BytecodeArray::kHeaderSize - kHeapObjectTag)); ++ __ Branch(&enter_bytecode); ++ ++ // We should never take the if_return path. ++ __ bind(&if_return); ++ __ Abort(AbortReason::kInvalidBytecodeAdvance); ++} ++ ++void Builtins::Generate_InterpreterEnterAtBytecode(MacroAssembler* masm) { ++ Generate_InterpreterEnterBytecode(masm); ++} ++ ++namespace { ++void Generate_ContinueToBuiltinHelper(MacroAssembler* masm, ++ bool java_script_builtin, ++ bool with_result) { ++ const RegisterConfiguration* config(RegisterConfiguration::Default()); ++ int allocatable_register_count = config->num_allocatable_general_registers(); ++ UseScratchRegisterScope temps(masm); ++ Register scratch = temps.Acquire(); ++ ++ if (with_result) { ++ if (java_script_builtin) { ++ __ mov(scratch, v0); ++ } else { ++ // Overwrite the hole inserted by the deoptimizer with the return value ++ // from the LAZY deopt point. ++ __ Stl( ++ v0, ++ MemOperand( ++ sp, config->num_allocatable_general_registers() * kPointerSize + ++ BuiltinContinuationFrameConstants::kFixedFrameSize)); ++ } ++ } ++ for (int i = allocatable_register_count - 1; i >= 0; --i) { ++ int code = config->GetAllocatableGeneralCode(i); ++ __ Pop(Register::from_code(code)); ++ if (java_script_builtin && code == kJavaScriptCallArgCountRegister.code()) { ++ __ SmiUntag(Register::from_code(code)); ++ } ++ } ++ ++ if (with_result && java_script_builtin) { ++ // Overwrite the hole inserted by the deoptimizer with the return value from ++ // the LAZY deopt point. t0 contains the arguments count, the return value ++ // from LAZY is always the last argument. ++ constexpr int return_value_offset = ++ BuiltinContinuationFrameConstants::kFixedSlotCount - ++ kJSArgcReceiverSlots; ++ __ Addl(a0, a0, Operand(return_value_offset)); ++ __ Lsal(t0, sp, a0, kSystemPointerSizeLog2); ++ __ Stl(scratch, MemOperand(t0)); ++ // Recover arguments count. ++ __ Subl(a0, a0, Operand(return_value_offset)); ++ } ++ ++ __ Ldl(fp, MemOperand( ++ sp, BuiltinContinuationFrameConstants::kFixedFrameSizeFromFp)); ++ // Load builtin index (stored as a Smi) and use it to get the builtin start ++ // address from the builtins table. ++ __ Pop(t0); ++ __ Addl(sp, sp, ++ Operand(BuiltinContinuationFrameConstants::kFixedFrameSizeFromFp)); ++ __ Pop(ra); ++ __ LoadEntryFromBuiltinIndex(t0); ++ __ Jump(t0); ++} ++} // namespace ++ ++void Builtins::Generate_ContinueToCodeStubBuiltin(MacroAssembler* masm) { ++ Generate_ContinueToBuiltinHelper(masm, false, false); ++} ++ ++void Builtins::Generate_ContinueToCodeStubBuiltinWithResult( ++ MacroAssembler* masm) { ++ Generate_ContinueToBuiltinHelper(masm, false, true); ++} ++ ++void Builtins::Generate_ContinueToJavaScriptBuiltin(MacroAssembler* masm) { ++ Generate_ContinueToBuiltinHelper(masm, true, false); ++} ++ ++void Builtins::Generate_ContinueToJavaScriptBuiltinWithResult( ++ MacroAssembler* masm) { ++ Generate_ContinueToBuiltinHelper(masm, true, true); ++} ++ ++void Builtins::Generate_NotifyDeoptimized(MacroAssembler* masm) { ++ { ++ FrameScope scope(masm, StackFrame::INTERNAL); ++ __ CallRuntime(Runtime::kNotifyDeoptimized); ++ } ++ ++ DCHECK_EQ(kInterpreterAccumulatorRegister.code(), v0.code()); ++ __ Ldl(v0, MemOperand(sp, 0 * kPointerSize)); ++ // Safe to fill delay slot Addw will emit one instruction. ++ __ Addl(sp, sp, Operand(1 * kPointerSize)); // Remove state. ++ __ Ret(); ++} ++ ++namespace { ++ ++void Generate_OSREntry(MacroAssembler* masm, Register entry_address, ++ Operand offset = Operand(zero_reg)) { ++ __ Addl(ra, entry_address, offset); ++ // And "return" to the OSR entry point of the function. ++ __ Ret(); ++} ++ ++void OnStackReplacement(MacroAssembler* masm, bool is_interpreter) { ++ { ++ FrameScope scope(masm, StackFrame::INTERNAL); ++ __ CallRuntime(Runtime::kCompileOptimizedOSR); ++ } ++ ++ // If the code object is null, just return to the caller. ++ __ Ret(eq, v0, Operand(Smi::zero())); ++ if (is_interpreter) { ++ // Drop the handler frame that is be sitting on top of the actual ++ // JavaScript frame. This is the case then OSR is triggered from bytecode. ++ __ LeaveFrame(StackFrame::STUB); ++ } ++ // Load deoptimization data from the code object. ++ // = [#deoptimization_data_offset] ++ __ Ldl(a1, MemOperand(v0, Code::kDeoptimizationDataOrInterpreterDataOffset - ++ kHeapObjectTag)); ++ ++ // Load the OSR entrypoint offset from the deoptimization data. ++ // = [#header_size + #osr_pc_offset] ++ __ SmiUntag(a1, MemOperand(a1, FixedArray::OffsetOfElementAt( ++ DeoptimizationData::kOsrPcOffsetIndex) - ++ kHeapObjectTag)); ++ ++ // Compute the target address = code_obj + header_size + osr_offset ++ // = + #header_size + ++ __ Addl(v0, v0, a1); ++ Generate_OSREntry(masm, v0, Operand(Code::kHeaderSize - kHeapObjectTag)); ++} ++} // namespace ++ ++void Builtins::Generate_InterpreterOnStackReplacement(MacroAssembler* masm) { ++ return OnStackReplacement(masm, true); ++} ++ ++void Builtins::Generate_BaselineOnStackReplacement(MacroAssembler* masm) { ++ __ Ldl(kContextRegister, ++ MemOperand(fp, StandardFrameConstants::kContextOffset)); ++ return OnStackReplacement(masm, false); ++} ++ ++// static ++void Builtins::Generate_FunctionPrototypeApply(MacroAssembler* masm) { ++ // ----------- S t a t e ------------- ++ // -- a0 : argc ++ // -- sp[0] : receiver ++ // -- sp[4] : thisArg ++ // -- sp[8] : argArray ++ // ----------------------------------- ++ ++ Register argc = a0; ++ Register arg_array = a2; ++ Register receiver = a1; ++ Register this_arg = a5; ++ Register undefined_value = a3; ++ Register scratch = a4; ++ ++ __ LoadRoot(undefined_value, RootIndex::kUndefinedValue); ++ ++ // 1. Load receiver into a1, argArray into a2 (if present), remove all ++ // arguments from the stack (including the receiver), and push thisArg (if ++ // present) instead. ++ { ++ __ Subl(scratch, argc, JSParameterCount(0)); ++ __ Ldl(this_arg, MemOperand(sp, kPointerSize)); ++ __ Ldl(arg_array, MemOperand(sp, 2 * kPointerSize)); ++ __ Seleq(arg_array, undefined_value, scratch); // if argc == 0 ++ __ Seleq(this_arg, undefined_value, scratch); // if argc == 0 ++ __ Subl(scratch, scratch, Operand(1)); ++ __ Seleq(arg_array, undefined_value, scratch); // if argc == 1 ++ __ Ldl(receiver, MemOperand(sp)); ++ __ DropArgumentsAndPushNewReceiver(argc, this_arg, ++ TurboAssembler::kCountIsInteger, ++ TurboAssembler::kCountIncludesReceiver); ++ } ++ ++ // ----------- S t a t e ------------- ++ // -- a2 : argArray ++ // -- a1 : receiver ++ // -- a3 : undefined root value ++ // -- sp[0] : thisArg ++ // ----------------------------------- ++ ++ // 2. We don't need to check explicitly for callable receiver here, ++ // since that's the first thing the Call/CallWithArrayLike builtins ++ // will do. ++ ++ // 3. Tail call with no arguments if argArray is null or undefined. ++ Label no_arguments; ++ __ JumpIfRoot(arg_array, RootIndex::kNullValue, &no_arguments); ++ __ Branch(&no_arguments, eq, arg_array, Operand(undefined_value)); ++ ++ // 4a. Apply the receiver to the given argArray. ++ __ Jump(BUILTIN_CODE(masm->isolate(), CallWithArrayLike), ++ RelocInfo::CODE_TARGET); ++ ++ // 4b. The argArray is either null or undefined, so we tail call without any ++ // arguments to the receiver. ++ __ bind(&no_arguments); ++ { ++ __ li(a0, JSParameterCount(0)); ++ DCHECK(receiver == a1); ++ __ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET); ++ } ++} ++ ++// static ++void Builtins::Generate_FunctionPrototypeCall(MacroAssembler* masm) { ++ // 1. Get the callable to call (passed as receiver) from the stack. ++ { __ Pop(a1); } ++ ++ // 2. Make sure we have at least one argument. ++ // a0: actual number of arguments ++ { ++ Label done; ++ __ Branch(&done, ne, a0, Operand(JSParameterCount(0))); ++ __ PushRoot(RootIndex::kUndefinedValue); ++ __ Addl(a0, a0, Operand(1)); ++ __ bind(&done); ++ } ++ ++ // 3. Adjust the actual number of arguments. ++ __ subl(a0, 1, a0); ++ ++ // 4. Call the callable. ++ __ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET); ++} ++ ++void Builtins::Generate_ReflectApply(MacroAssembler* masm) { ++ // ----------- S t a t e ------------- ++ // -- a0 : argc ++ // -- sp[0] : receiver ++ // -- sp[8] : target (if argc >=1) ++ // -- sp[16] : thisArgument (if argc >= 2) ++ // -- sp[24] : argumentsList (if argc == 3) ++ // ----------------------------------- ++ ++ Register argc = a0; ++ Register arguments_list = a2; ++ Register target = a1; ++ Register this_argument = a5; ++ Register undefined_value = a3; ++ Register scratch = a4; ++ ++ __ LoadRoot(undefined_value, RootIndex::kUndefinedValue); ++ ++ // 1. Load target into a1 (if present), argumentsList into a2 (if present), ++ // remove all arguments from the stack (including the receiver), and push ++ // thisArgument (if present) instead. ++ { ++ // Claim (3 - argc) dummy arguments form the stack, to put the stack in a ++ // consistent state for a simple pop operation. ++ ++ __ Subl(scratch, argc, Operand(JSParameterCount(0))); ++ __ Ldl(target, MemOperand(sp, kPointerSize)); ++ __ Ldl(this_argument, MemOperand(sp, 2 * kPointerSize)); ++ __ Ldl(arguments_list, MemOperand(sp, 3 * kPointerSize)); ++ __ Seleq(arguments_list, undefined_value, scratch); // if argc == 0 ++ __ Seleq(this_argument, undefined_value, scratch); // if argc == 0 ++ __ Seleq(target, undefined_value, scratch); // if argc == 0 ++ __ Subl(scratch, scratch, Operand(1)); ++ __ Seleq(arguments_list, undefined_value, scratch); // if argc == 1 ++ __ Seleq(this_argument, undefined_value, scratch); // if argc == 1 ++ __ Subl(scratch, scratch, Operand(1)); ++ __ Seleq(arguments_list, undefined_value, scratch); // if argc == 2 ++ ++ __ DropArgumentsAndPushNewReceiver(argc, this_argument, ++ TurboAssembler::kCountIsInteger, ++ TurboAssembler::kCountIncludesReceiver); ++ } ++ ++ // ----------- S t a t e ------------- ++ // -- a2 : argumentsList ++ // -- a1 : target ++ // -- a3 : undefined root value ++ // -- sp[0] : thisArgument ++ // ----------------------------------- ++ ++ // 2. We don't need to check explicitly for callable target here, ++ // since that's the first thing the Call/CallWithArrayLike builtins ++ // will do. ++ ++ // 3. Apply the target to the given argumentsList. ++ __ Jump(BUILTIN_CODE(masm->isolate(), CallWithArrayLike), ++ RelocInfo::CODE_TARGET); ++} ++ ++void Builtins::Generate_ReflectConstruct(MacroAssembler* masm) { ++ // ----------- S t a t e ------------- ++ // -- a0 : argc ++ // -- sp[0] : receiver ++ // -- sp[8] : target ++ // -- sp[16] : argumentsList ++ // -- sp[24] : new.target (optional) ++ // ----------------------------------- ++ ++ Register argc = a0; ++ Register arguments_list = a2; ++ Register target = a1; ++ Register new_target = a3; ++ Register undefined_value = a4; ++ Register scratch = a5; ++ ++ __ LoadRoot(undefined_value, RootIndex::kUndefinedValue); ++ ++ // 1. Load target into a1 (if present), argumentsList into a2 (if present), ++ // new.target into a3 (if present, otherwise use target), remove all ++ // arguments from the stack (including the receiver), and push thisArgument ++ // (if present) instead. ++ { ++ // Claim (3 - argc) dummy arguments form the stack, to put the stack in a ++ // consistent state for a simple pop operation. ++ ++ __ Subl(scratch, argc, Operand(JSParameterCount(0))); ++ __ Ldl(target, MemOperand(sp, kPointerSize)); ++ __ Ldl(arguments_list, MemOperand(sp, 2 * kPointerSize)); ++ __ Ldl(new_target, MemOperand(sp, 3 * kPointerSize)); ++ __ Seleq(arguments_list, undefined_value, scratch); // if argc == 0 ++ __ Seleq(new_target, undefined_value, scratch); // if argc == 0 ++ __ Seleq(target, undefined_value, scratch); // if argc == 0 ++ __ Subl(scratch, scratch, Operand(1)); ++ __ Seleq(arguments_list, undefined_value, scratch); // if argc == 1 ++ __ Seleq(new_target, target, scratch); // if argc == 1 ++ __ Subl(scratch, scratch, Operand(1)); ++ __ Seleq(new_target, target, scratch); // if argc == 2 ++ ++ __ DropArgumentsAndPushNewReceiver(argc, undefined_value, ++ TurboAssembler::kCountIsInteger, ++ TurboAssembler::kCountIncludesReceiver); ++ } ++ ++ // ----------- S t a t e ------------- ++ // -- a2 : argumentsList ++ // -- a1 : target ++ // -- a3 : new.target ++ // -- sp[0] : receiver (undefined) ++ // ----------------------------------- ++ ++ // 2. We don't need to check explicitly for constructor target here, ++ // since that's the first thing the Construct/ConstructWithArrayLike ++ // builtins will do. ++ ++ // 3. We don't need to check explicitly for constructor new.target here, ++ // since that's the second thing the Construct/ConstructWithArrayLike ++ // builtins will do. ++ ++ // 4. Construct the target with the given new.target and argumentsList. ++ __ Jump(BUILTIN_CODE(masm->isolate(), ConstructWithArrayLike), ++ RelocInfo::CODE_TARGET); ++} ++ ++namespace { ++ ++// Allocate new stack space for |count| arguments and shift all existing ++// arguments already on the stack. |pointer_to_new_space_out| points to the ++// first free slot on the stack to copy additional arguments to and ++// |argc_in_out| is updated to include |count|. ++void Generate_AllocateSpaceAndShiftExistingArguments( ++ MacroAssembler* masm, Register count, Register argc_in_out, ++ Register pointer_to_new_space_out, Register scratch1, Register scratch2, ++ Register scratch3) { ++ DCHECK(!AreAliased(count, argc_in_out, pointer_to_new_space_out, scratch1, ++ scratch2)); ++ Register old_sp = scratch1; ++ Register new_space = scratch2; ++ __ mov(old_sp, sp); ++ __ slll(count, kPointerSizeLog2, new_space); ++ __ Subl(sp, sp, Operand(new_space)); ++ ++ Register end = scratch2; ++ Register value = scratch3; ++ Register dest = pointer_to_new_space_out; ++ __ mov(dest, sp); ++ __ Lsal(end, old_sp, argc_in_out, kSystemPointerSizeLog2); ++ Label loop, done; ++ __ Branch(&done, ge, old_sp, Operand(end)); ++ __ bind(&loop); ++ __ Ldl(value, MemOperand(old_sp, 0)); ++ __ Stl(value, MemOperand(dest, 0)); ++ __ Addl(old_sp, old_sp, Operand(kSystemPointerSize)); ++ __ Addl(dest, dest, Operand(kSystemPointerSize)); ++ __ Branch(&loop, lt, old_sp, Operand(end)); ++ __ bind(&done); ++ ++ // Update total number of arguments. ++ __ Addl(argc_in_out, argc_in_out, count); ++} ++ ++} // namespace ++ ++// static ++void Builtins::Generate_CallOrConstructVarargs(MacroAssembler* masm, ++ Handle code) { ++ // ----------- S t a t e ------------- ++ // -- a1 : target ++ // -- a0 : number of parameters on the stack ++ // -- a2 : arguments list (a FixedArray) ++ // -- a4 : len (number of elements to push from args) ++ // -- a3 : new.target (for [[Construct]]) ++ // ----------------------------------- ++ if (FLAG_debug_code) { ++ // Allow a2 to be a FixedArray, or a FixedDoubleArray if a4 == 0. ++ Label ok, fail; ++ __ AssertNotSmi(a2); ++ __ GetObjectType(a2, t8, t8); ++ __ Branch(&ok, eq, t8, Operand(FIXED_ARRAY_TYPE)); ++ __ Branch(&fail, ne, t8, Operand(FIXED_DOUBLE_ARRAY_TYPE)); ++ __ Branch(&ok, eq, a4, Operand(zero_reg)); ++ // Fall through. ++ __ bind(&fail); ++ __ Abort(AbortReason::kOperandIsNotAFixedArray); ++ ++ __ bind(&ok); ++ } ++ ++ Register args = a2; ++ Register len = a4; ++ ++ // Check for stack overflow. ++ Label stack_overflow; ++ __ StackOverflowCheck(len, kScratchReg, a5, &stack_overflow); ++ ++ // Move the arguments already in the stack, ++ // including the receiver and the return address. ++ // a4: Number of arguments to make room for. ++ // a0: Number of arguments already on the stack. ++ // t10: Points to first free slot on the stack after arguments were shifted. ++ Generate_AllocateSpaceAndShiftExistingArguments(masm, a4, a0, t10, t9, t0, ++ t1); ++ ++ // Push arguments onto the stack (thisArgument is already on the stack). ++ { ++ Label done, push, loop; ++ Register src = t9; ++ Register scratch = len; ++ ++ __ Addl(src, args, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); ++ __ slll(len, kPointerSizeLog2, scratch); ++ __ Branch(&done, eq, len, Operand(zero_reg)); ++ __ Subl(scratch, sp, Operand(scratch)); ++ __ LoadRoot(t1, RootIndex::kTheHoleValue); ++ __ bind(&loop); ++ __ Ldl(a5, MemOperand(src)); ++ __ addl(src, kPointerSize, src); ++ __ Branch(&push, ne, a5, Operand(t1)); ++ __ LoadRoot(a5, RootIndex::kUndefinedValue); ++ __ bind(&push); ++ __ Stl(a5, MemOperand(t10, 0)); ++ __ Addl(t10, t10, Operand(kSystemPointerSize)); ++ __ Addl(scratch, scratch, Operand(kSystemPointerSize)); ++ __ Branch(&loop, ne, scratch, Operand(sp)); ++ __ bind(&done); ++ } ++ ++ // Tail-call to the actual Call or Construct builtin. ++ __ Jump(code, RelocInfo::CODE_TARGET); ++ ++ __ bind(&stack_overflow); ++ __ TailCallRuntime(Runtime::kThrowStackOverflow); ++} ++ ++// static ++void Builtins::Generate_CallOrConstructForwardVarargs(MacroAssembler* masm, ++ CallOrConstructMode mode, ++ Handle code) { ++ // ----------- S t a t e ------------- ++ // -- a0 : the number of arguments ++ // -- a3 : the new.target (for [[Construct]] calls) ++ // -- a1 : the target to call (can be any Object) ++ // -- a2 : start index (to support rest parameters) ++ // ----------------------------------- ++ ++ // Check if new.target has a [[Construct]] internal method. ++ if (mode == CallOrConstructMode::kConstruct) { ++ Label new_target_constructor, new_target_not_constructor; ++ __ JumpIfSmi(a3, &new_target_not_constructor); ++ __ Ldl(t1, FieldMemOperand(a3, HeapObject::kMapOffset)); ++ __ Ldbu(t1, FieldMemOperand(t1, Map::kBitFieldOffset)); ++ __ And(t1, t1, Operand(Map::Bits1::IsConstructorBit::kMask)); ++ __ Branch(&new_target_constructor, ne, t1, Operand(zero_reg)); ++ __ bind(&new_target_not_constructor); ++ { ++ FrameScope scope(masm, StackFrame::MANUAL); ++ __ EnterFrame(StackFrame::INTERNAL); ++ __ Push(a3); ++ __ CallRuntime(Runtime::kThrowNotConstructor); ++ } ++ __ bind(&new_target_constructor); ++ } ++ ++ Label stack_done, stack_overflow; ++ __ Ldl(t10, MemOperand(fp, StandardFrameConstants::kArgCOffset)); ++ __ Subl(t10, t10, Operand(kJSArgcReceiverSlots)); ++ __ Subl(t10, t10, a2); ++ __ Branch(&stack_done, le, t10, Operand(zero_reg)); ++ { ++ // Check for stack overflow. ++ __ StackOverflowCheck(t10, a4, a5, &stack_overflow); ++ ++ // Forward the arguments from the caller frame. ++ ++ // Point to the first argument to copy (skipping the receiver). ++ __ Addl(t9, fp, ++ Operand(CommonFrameConstants::kFixedFrameSizeAboveFp + ++ kSystemPointerSize)); ++ __ Lsal(t9, t9, a2, kSystemPointerSizeLog2); ++ ++ // Move the arguments already in the stack, ++ // including the receiver and the return address. ++ // t10: Number of arguments to make room for. ++ // a0: Number of arguments already on the stack. ++ // a2: Points to first free slot on the stack after arguments were shifted. ++ Generate_AllocateSpaceAndShiftExistingArguments(masm, t10, a0, a2, t0, t1, ++ t2); ++ ++ // Copy arguments from the caller frame. ++ // TODO(victorgomes): Consider using forward order as potentially more cache ++ // friendly. ++ { ++ Label loop; ++ __ bind(&loop); ++ { ++ __ Subw(t10, t10, Operand(1)); ++ __ Lsal(t0, t9, t10, kPointerSizeLog2); ++ __ Ldl(kScratchReg, MemOperand(t0)); ++ __ Lsal(t0, a2, t10, kPointerSizeLog2); ++ __ Stl(kScratchReg, MemOperand(t0)); ++ __ Branch(&loop, ne, t10, Operand(zero_reg)); ++ } ++ } ++ } ++ __ Branch(&stack_done); ++ __ bind(&stack_overflow); ++ __ TailCallRuntime(Runtime::kThrowStackOverflow); ++ __ bind(&stack_done); ++ ++ // Tail-call to the {code} handler. ++ __ Jump(code, RelocInfo::CODE_TARGET); ++} ++ ++// static ++void Builtins::Generate_CallFunction(MacroAssembler* masm, ++ ConvertReceiverMode mode) { ++ // ----------- S t a t e ------------- ++ // -- a0 : the number of arguments ++ // -- a1 : the function to call (checked to be a JSFunction) ++ // ----------------------------------- ++ __ AssertCallableFunction(a1); ++ ++ __ Ldl(a2, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset)); ++ ++ // Enter the context of the function; ToObject has to run in the function ++ // context, and we also need to take the global proxy from the function ++ // context in case of conversion. ++ __ Ldl(cp, FieldMemOperand(a1, JSFunction::kContextOffset)); ++ // We need to convert the receiver for non-native sloppy mode functions. ++ Label done_convert; ++ __ Ldwu(a3, FieldMemOperand(a2, SharedFunctionInfo::kFlagsOffset)); ++ __ And(kScratchReg, a3, ++ Operand(SharedFunctionInfo::IsNativeBit::kMask | ++ SharedFunctionInfo::IsStrictBit::kMask)); ++ __ Branch(&done_convert, ne, kScratchReg, Operand(zero_reg)); ++ { ++ // ----------- S t a t e ------------- ++ // -- a0 : the number of arguments ++ // -- a1 : the function to call (checked to be a JSFunction) ++ // -- a2 : the shared function info. ++ // -- cp : the function context. ++ // ----------------------------------- ++ ++ if (mode == ConvertReceiverMode::kNullOrUndefined) { ++ // Patch receiver to global proxy. ++ __ LoadGlobalProxy(a3); ++ } else { ++ Label convert_to_object, convert_receiver; ++ __ LoadReceiver(a3, a0); ++ __ JumpIfSmi(a3, &convert_to_object); ++ STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE); ++ __ GetObjectType(a3, a4, a4); ++ __ Branch(&done_convert, hs, a4, Operand(FIRST_JS_RECEIVER_TYPE)); ++ if (mode != ConvertReceiverMode::kNotNullOrUndefined) { ++ Label convert_global_proxy; ++ __ JumpIfRoot(a3, RootIndex::kUndefinedValue, &convert_global_proxy); ++ __ JumpIfNotRoot(a3, RootIndex::kNullValue, &convert_to_object); ++ __ bind(&convert_global_proxy); ++ { ++ // Patch receiver to global proxy. ++ __ LoadGlobalProxy(a3); ++ } ++ __ Branch(&convert_receiver); ++ } ++ __ bind(&convert_to_object); ++ { ++ // Convert receiver using ToObject. ++ // TODO(bmeurer): Inline the allocation here to avoid building the frame ++ // in the fast case? (fall back to AllocateInNewSpace?) ++ FrameScope scope(masm, StackFrame::INTERNAL); ++ __ SmiTag(a0); ++ __ Push(a0, a1); ++ __ mov(a0, a3); ++ __ Push(cp); ++ __ Call(BUILTIN_CODE(masm->isolate(), ToObject), ++ RelocInfo::CODE_TARGET); ++ __ Pop(cp); ++ __ mov(a3, v0); ++ __ Pop(a0, a1); ++ __ SmiUntag(a0); ++ } ++ __ Ldl(a2, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset)); ++ __ bind(&convert_receiver); ++ } ++ __ StoreReceiver(a3, a0, kScratchReg); ++ } ++ __ bind(&done_convert); ++ ++ // ----------- S t a t e ------------- ++ // -- a0 : the number of arguments ++ // -- a1 : the function to call (checked to be a JSFunction) ++ // -- a2 : the shared function info. ++ // -- cp : the function context. ++ // ----------------------------------- ++ ++ __ Ldhu(a2, ++ FieldMemOperand(a2, SharedFunctionInfo::kFormalParameterCountOffset)); ++ __ InvokeFunctionCode(a1, no_reg, a2, a0, InvokeType::kJump); ++} ++ ++// static ++void Builtins::Generate_CallBoundFunctionImpl(MacroAssembler* masm) { ++ // ----------- S t a t e ------------- ++ // -- a0 : the number of arguments ++ // -- a1 : the function to call (checked to be a JSBoundFunction) ++ // ----------------------------------- ++ __ AssertBoundFunction(a1); ++ ++ // Patch the receiver to [[BoundThis]]. ++ { ++ __ Ldl(t0, FieldMemOperand(a1, JSBoundFunction::kBoundThisOffset)); ++ __ StoreReceiver(t0, a0, kScratchReg); ++ } ++ ++ // Load [[BoundArguments]] into a2 and length of that into a4. ++ __ Ldl(a2, FieldMemOperand(a1, JSBoundFunction::kBoundArgumentsOffset)); ++ __ SmiUntag(a4, FieldMemOperand(a2, FixedArray::kLengthOffset)); ++ ++ // ----------- S t a t e ------------- ++ // -- a0 : the number of arguments ++ // -- a1 : the function to call (checked to be a JSBoundFunction) ++ // -- a2 : the [[BoundArguments]] (implemented as FixedArray) ++ // -- a4 : the number of [[BoundArguments]] ++ // ----------------------------------- ++ ++ // Reserve stack space for the [[BoundArguments]]. ++ { ++ Label done; ++ __ slll(a4, kPointerSizeLog2, a5); ++ __ Subl(t0, sp, Operand(a5)); ++ // Check the stack for overflow. We are not trying to catch interruptions ++ // (i.e. debug break and preemption) here, so check the "real stack limit". ++ __ LoadStackLimit(kScratchReg, ++ MacroAssembler::StackLimitKind::kRealStackLimit); ++ __ Branch(&done, hs, t0, Operand(kScratchReg)); ++ { ++ FrameScope scope(masm, StackFrame::MANUAL); ++ __ EnterFrame(StackFrame::INTERNAL); ++ __ CallRuntime(Runtime::kThrowStackOverflow); ++ } ++ __ bind(&done); ++ } ++ ++ // Pop receiver. ++ __ Pop(t0); ++ ++ // Push [[BoundArguments]]. ++ { ++ Label loop, done_loop; ++ __ SmiUntag(a4, FieldMemOperand(a2, FixedArray::kLengthOffset)); ++ __ Addl(a0, a0, Operand(a4)); ++ __ Addl(a2, a2, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); ++ __ bind(&loop); ++ __ Subl(a4, a4, Operand(1)); ++ __ Branch(&done_loop, lt, a4, Operand(zero_reg)); ++ __ s8addl(a4, a2, a5); ++ DCHECK_EQ(kSystemPointerSizeLog2, 3); ++ __ Ldl(kScratchReg, MemOperand(a5)); ++ __ Push(kScratchReg); ++ __ Branch(&loop); ++ __ bind(&done_loop); ++ } ++ ++ // Push receiver. ++ __ Push(t0); ++ ++ // Call the [[BoundTargetFunction]] via the Call builtin. ++ __ Ldl(a1, FieldMemOperand(a1, JSBoundFunction::kBoundTargetFunctionOffset)); ++ __ Jump(BUILTIN_CODE(masm->isolate(), Call_ReceiverIsAny), ++ RelocInfo::CODE_TARGET); ++} ++ ++// static ++void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode) { ++ // ----------- S t a t e ------------- ++ // -- a0 : the number of arguments ++ // -- a1 : the target to call (can be any Object). ++ // ----------------------------------- ++ Register argc = a0; ++ Register target = a1; ++ Register map = t1; ++ Register instance_type = t2; ++ Register scratch = t11; ++ DCHECK(!AreAliased(argc, target, map, instance_type, scratch)); ++ ++ Label non_callable, class_constructor; ++ __ JumpIfSmi(target, &non_callable); ++ __ LoadMap(map, target); ++ __ GetInstanceTypeRange(map, instance_type, FIRST_CALLABLE_JS_FUNCTION_TYPE, ++ scratch); ++ __ Jump(masm->isolate()->builtins()->CallFunction(mode), ++ RelocInfo::CODE_TARGET, ls, scratch, ++ Operand(LAST_CALLABLE_JS_FUNCTION_TYPE - ++ FIRST_CALLABLE_JS_FUNCTION_TYPE)); ++ __ Jump(BUILTIN_CODE(masm->isolate(), CallBoundFunction), ++ RelocInfo::CODE_TARGET, eq, instance_type, ++ Operand(JS_BOUND_FUNCTION_TYPE)); ++ ++ // Check if target has a [[Call]] internal method. ++ { ++ Register flags = t1; ++ __ Ldbu(flags, FieldMemOperand(map, Map::kBitFieldOffset)); ++ map = no_reg; ++ __ And(flags, flags, Operand(Map::Bits1::IsCallableBit::kMask)); ++ __ Branch(&non_callable, eq, flags, Operand(zero_reg)); ++ } ++ ++ __ Jump(BUILTIN_CODE(masm->isolate(), CallProxy), RelocInfo::CODE_TARGET, eq, ++ instance_type, Operand(JS_PROXY_TYPE)); ++ ++ // Check if target is a wrapped function and call CallWrappedFunction external ++ // builtin ++ __ Jump(BUILTIN_CODE(masm->isolate(), CallWrappedFunction), ++ RelocInfo::CODE_TARGET, eq, instance_type, ++ Operand(JS_WRAPPED_FUNCTION_TYPE)); ++ ++ // ES6 section 9.2.1 [[Call]] ( thisArgument, argumentsList) ++ // Check that the function is not a "classConstructor". ++ __ Branch(&class_constructor, eq, instance_type, ++ Operand(JS_CLASS_CONSTRUCTOR_TYPE)); ++ ++ // 2. Call to something else, which might have a [[Call]] internal method (if ++ // not we raise an exception). ++ // Overwrite the original receiver with the (original) target. ++ __ StoreReceiver(target, argc, kScratchReg); ++ // Let the "call_as_function_delegate" take care of the rest. ++ __ LoadNativeContextSlot(target, Context::CALL_AS_FUNCTION_DELEGATE_INDEX); ++ __ Jump(masm->isolate()->builtins()->CallFunction( ++ ConvertReceiverMode::kNotNullOrUndefined), ++ RelocInfo::CODE_TARGET); ++ ++ // 3. Call to something that is not callable. ++ __ bind(&non_callable); ++ { ++ FrameScope scope(masm, StackFrame::INTERNAL); ++ __ Push(target); ++ __ CallRuntime(Runtime::kThrowCalledNonCallable); ++ } ++ ++ // 4. The function is a "classConstructor", need to raise an exception. ++ __ bind(&class_constructor); ++ { ++ FrameScope frame(masm, StackFrame::INTERNAL); ++ __ Push(target); ++ __ CallRuntime(Runtime::kThrowConstructorNonCallableError); ++ } ++} ++ ++void Builtins::Generate_ConstructFunction(MacroAssembler* masm) { ++ // ----------- S t a t e ------------- ++ // -- a0 : the number of arguments ++ // -- a1 : the constructor to call (checked to be a JSFunction) ++ // -- a3 : the new target (checked to be a constructor) ++ // ----------------------------------- ++ __ AssertConstructor(a1); ++ __ AssertFunction(a1); ++ ++ // Calling convention for function specific ConstructStubs require ++ // a2 to contain either an AllocationSite or undefined. ++ __ LoadRoot(a2, RootIndex::kUndefinedValue); ++ ++ Label call_generic_stub; ++ ++ // Jump to JSBuiltinsConstructStub or JSConstructStubGeneric. ++ __ Ldl(a4, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset)); ++ __ Ldwu(a4, FieldMemOperand(a4, SharedFunctionInfo::kFlagsOffset)); ++ __ And(a4, a4, Operand(SharedFunctionInfo::ConstructAsBuiltinBit::kMask)); ++ __ Branch(&call_generic_stub, eq, a4, Operand(zero_reg)); ++ ++ __ Jump(BUILTIN_CODE(masm->isolate(), JSBuiltinsConstructStub), ++ RelocInfo::CODE_TARGET); ++ ++ __ bind(&call_generic_stub); ++ __ Jump(BUILTIN_CODE(masm->isolate(), JSConstructStubGeneric), ++ RelocInfo::CODE_TARGET); ++} ++ ++// static ++void Builtins::Generate_ConstructBoundFunction(MacroAssembler* masm) { ++ // ----------- S t a t e ------------- ++ // -- a0 : the number of arguments ++ // -- a1 : the function to call (checked to be a JSBoundFunction) ++ // -- a3 : the new target (checked to be a constructor) ++ // ----------------------------------- ++ __ AssertConstructor(a1); ++ __ AssertBoundFunction(a1); ++ ++ // Load [[BoundArguments]] into a2 and length of that into a4. ++ __ Ldl(a2, FieldMemOperand(a1, JSBoundFunction::kBoundArgumentsOffset)); ++ __ SmiUntag(a4, FieldMemOperand(a2, FixedArray::kLengthOffset)); ++ ++ // ----------- S t a t e ------------- ++ // -- a0 : the number of arguments ++ // -- a1 : the function to call (checked to be a JSBoundFunction) ++ // -- a2 : the [[BoundArguments]] (implemented as FixedArray) ++ // -- a3 : the new target (checked to be a constructor) ++ // -- a4 : the number of [[BoundArguments]] ++ // ----------------------------------- ++ ++ // Reserve stack space for the [[BoundArguments]]. ++ { ++ Label done; ++ __ slll(a4, kPointerSizeLog2, a5); ++ __ Subl(t0, sp, Operand(a5)); ++ // Check the stack for overflow. We are not trying to catch interruptions ++ // (i.e. debug break and preemption) here, so check the "real stack limit". ++ __ LoadStackLimit(kScratchReg, ++ MacroAssembler::StackLimitKind::kRealStackLimit); ++ __ Branch(&done, hs, t0, Operand(kScratchReg)); ++ { ++ FrameScope scope(masm, StackFrame::MANUAL); ++ __ EnterFrame(StackFrame::INTERNAL); ++ __ CallRuntime(Runtime::kThrowStackOverflow); ++ } ++ __ bind(&done); ++ } ++ ++ // Pop receiver. ++ __ Pop(t0); ++ ++ // Push [[BoundArguments]]. ++ { ++ Label loop, done_loop; ++ __ SmiUntag(a4, FieldMemOperand(a2, FixedArray::kLengthOffset)); ++ __ Addl(a0, a0, Operand(a4)); ++ __ Addl(a2, a2, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); ++ __ bind(&loop); ++ __ Subl(a4, a4, Operand(1)); ++ __ Branch(&done_loop, lt, a4, Operand(zero_reg)); ++ __ s8addl(a4, a2, a5); ++ DCHECK_EQ(kSystemPointerSizeLog2, 3); ++ __ Ldl(kScratchReg, MemOperand(a5)); ++ __ Push(kScratchReg); ++ __ Branch(&loop); ++ __ bind(&done_loop); ++ } ++ ++ // Push receiver. ++ __ Push(t0); ++ ++ // Patch new.target to [[BoundTargetFunction]] if new.target equals target. ++ { ++ Label skip_load; ++ __ Branch(&skip_load, ne, a1, Operand(a3)); ++ __ Ldl(a3, ++ FieldMemOperand(a1, JSBoundFunction::kBoundTargetFunctionOffset)); ++ __ bind(&skip_load); ++ } ++ ++ // Construct the [[BoundTargetFunction]] via the Construct builtin. ++ __ Ldl(a1, FieldMemOperand(a1, JSBoundFunction::kBoundTargetFunctionOffset)); ++ __ Jump(BUILTIN_CODE(masm->isolate(), Construct), RelocInfo::CODE_TARGET); ++} ++ ++// static ++void Builtins::Generate_Construct(MacroAssembler* masm) { ++ // ----------- S t a t e ------------- ++ // -- a0 : the number of arguments ++ // -- a1 : the constructor to call (can be any Object) ++ // -- a3 : the new target (either the same as the constructor or ++ // the JSFunction on which new was invoked initially) ++ // ----------------------------------- ++ ++ Register argc = a0; ++ Register target = a1; ++ Register map = t1; ++ Register instance_type = t2; ++ Register scratch = t11; ++ DCHECK(!AreAliased(argc, target, map, instance_type, scratch)); ++ ++ // Check if target is a Smi. ++ Label non_constructor, non_proxy; ++ __ JumpIfSmi(target, &non_constructor); ++ ++ // Check if target has a [[Construct]] internal method. ++ __ ldl(map, FieldMemOperand(target, HeapObject::kMapOffset)); ++ { ++ Register flags = t3; ++ __ Ldbu(flags, FieldMemOperand(map, Map::kBitFieldOffset)); ++ __ And(flags, flags, Operand(Map::Bits1::IsConstructorBit::kMask)); ++ __ Branch(&non_constructor, eq, flags, Operand(zero_reg)); ++ } ++ ++ // Dispatch based on instance type. ++ __ GetInstanceTypeRange(map, instance_type, FIRST_JS_FUNCTION_TYPE, scratch); ++ __ Jump(BUILTIN_CODE(masm->isolate(), ConstructFunction), ++ RelocInfo::CODE_TARGET, ls, scratch, ++ Operand(LAST_JS_FUNCTION_TYPE - FIRST_JS_FUNCTION_TYPE)); ++ ++ // Only dispatch to bound functions after checking whether they are ++ // constructors. ++ __ Jump(BUILTIN_CODE(masm->isolate(), ConstructBoundFunction), ++ RelocInfo::CODE_TARGET, eq, instance_type, ++ Operand(JS_BOUND_FUNCTION_TYPE)); ++ ++ // Only dispatch to proxies after checking whether they are constructors. ++ __ Branch(&non_proxy, ne, instance_type, Operand(JS_PROXY_TYPE)); ++ __ Jump(BUILTIN_CODE(masm->isolate(), ConstructProxy), ++ RelocInfo::CODE_TARGET); ++ ++ // Called Construct on an exotic Object with a [[Construct]] internal method. ++ __ bind(&non_proxy); ++ { ++ // Overwrite the original receiver with the (original) target. ++ __ StoreReceiver(target, argc, kScratchReg); ++ // Let the "call_as_constructor_delegate" take care of the rest. ++ __ LoadNativeContextSlot(target, ++ Context::CALL_AS_CONSTRUCTOR_DELEGATE_INDEX); ++ __ Jump(masm->isolate()->builtins()->CallFunction(), ++ RelocInfo::CODE_TARGET); ++ } ++ ++ // Called Construct on an Object that doesn't have a [[Construct]] internal ++ // method. ++ __ bind(&non_constructor); ++ __ Jump(BUILTIN_CODE(masm->isolate(), ConstructedNonConstructable), ++ RelocInfo::CODE_TARGET); ++} ++ ++#if V8_ENABLE_WEBASSEMBLY ++void Builtins::Generate_WasmCompileLazy(MacroAssembler* masm) { ++ // The function index was put in t0 by the jump table trampoline. ++ // Convert to Smi for the runtime call ++ __ SmiTag(kWasmCompileLazyFuncIndexRegister); ++ ++// Compute register lists for parameters to be saved. We save all parameter ++// registers (see wasm-linkage.h). They might be overwritten in the runtime ++// call below. We don't have any callee-saved registers in wasm, so no need to ++// store anything else. ++ RegList kSavedGpRegs = ([]() { ++ RegList saved_gp_regs; ++ for (Register gp_param_reg : wasm::kGpParamRegisters) { ++ saved_gp_regs.set(gp_param_reg); ++ } ++ ++ // All set registers were unique. ++ CHECK_EQ(saved_gp_regs.Count(), arraysize(wasm::kGpParamRegisters)); ++ // The Wasm instance must be part of the saved registers. ++ CHECK(saved_gp_regs.has(kWasmInstanceRegister)); ++ CHECK_EQ(WasmCompileLazyFrameConstants::kNumberOfSavedGpParamRegs, ++ saved_gp_regs.Count()); ++ return saved_gp_regs; ++ })(); ++ ++ DoubleRegList kSavedFpRegs = ([]() { ++ DoubleRegList saved_fp_regs; ++ for (DoubleRegister fp_param_reg : wasm::kFpParamRegisters) { ++ saved_fp_regs.set(fp_param_reg); ++ } ++ ++ CHECK_EQ(saved_fp_regs.Count(), arraysize(wasm::kFpParamRegisters)); ++ CHECK_EQ(WasmCompileLazyFrameConstants::kNumberOfSavedFpParamRegs, ++ saved_fp_regs.Count()); ++ return saved_fp_regs; ++ })(); ++ ++ { ++ HardAbortScope hard_abort(masm); // Avoid calls to Abort. ++ FrameScope scope(masm, StackFrame::WASM_COMPILE_LAZY); ++ ++ // Save registers that we need to keep alive across the runtime call. ++ __ MultiPush(kSavedGpRegs); ++#if 0 ++ // Check if machine has simd enabled, if so push vector registers. If not ++ // then only push double registers. ++ Label push_doubles, simd_pushed; ++ __ li(a1, ExternalReference::supports_wasm_simd_128_address()); ++ // If > 0 then simd is available. ++ __ Ldbu(a1, MemOperand(a1)); ++ __ Branch(&push_doubles, le, a1, Operand(zero_reg)); ++ // Save vector registers. ++#endif ++ __ MultiPushFPU(kSavedFpRegs); ++#if 0 ++ __ Branch(&simd_pushed); ++ __ bind(&push_doubles); ++ __ MultiPushFPU(kSavedFpRegs); ++ // kFixedFrameSizeFromFp is hard coded to include space for Simd ++ // registers, so we still need to allocate extra (unused) space on the stack ++ // as if they were saved. ++ __ Subl(sp, sp, kSavedFpRegs.Count() * kDoubleSize); ++ __ bind(&simd_pushed); ++#endif ++ // Pass instance and function index as an explicit arguments to the runtime ++ // function. ++ // Allocate a stack slot, where the runtime function can spill a pointer to ++ // the the NativeModule. ++ __ Push(kWasmInstanceRegister, kWasmCompileLazyFuncIndexRegister); ++ ++ // Initialize the JavaScript context with 0. CEntry will use it to ++ // set the current context on the isolate. ++ __ Move(kContextRegister, Smi::zero()); ++ __ CallRuntime(Runtime::kWasmCompileLazy, 2); ++ ++ // Restore registers. ++#if 0 ++ Label pop_doubles, simd_popped; ++ __ li(a1, ExternalReference::supports_wasm_simd_128_address()); ++ // If > 0 then simd is available. ++ __ Ldbu(a1, MemOperand(a1)); ++ __ Branch(&pop_doubles, le, a1, Operand(zero_reg)); ++ // Pop vector registers. ++ { ++ CpuFeatureScope swsa_scope( ++ masm, SW64_SIMD, CpuFeatureScope::CheckPolicy::kDontCheckSupported); ++ __ MultiPopSWSA(kSavedFpRegs); ++ } ++ __ Branch(&simd_popped); ++ __ bind(&pop_doubles); ++ __ Addl(sp, sp, kSavedFpRegs.Count() * kDoubleSize); ++#endif ++ __ MultiPopFPU(kSavedFpRegs); ++ //__ bind(&simd_popped); ++ __ MultiPop(kSavedGpRegs); ++ } ++ ++ // Untag the returned Smi, for later use. ++ // static_assert(!kSavedGpRegs.has(v0)); ++ __ SmiUntag(v0); ++ ++ // The runtime function returned the jump table slot offset as a Smi (now in ++ // t11). Use that to compute the jump target. ++ // static_assert(!kSavedGpRegs.has(t11)); ++ __ Ldl(t11, MemOperand(kWasmInstanceRegister, ++ WasmInstanceObject::kJumpTableStartOffset - kHeapObjectTag)); ++ __ Addl(t11, v0, t11); ++ ++ // Finally, jump to the jump table slot for the function. ++ __ Jump(t11); ++} ++ ++void Builtins::Generate_WasmDebugBreak(MacroAssembler* masm) { ++ HardAbortScope hard_abort(masm); // Avoid calls to Abort. ++ { ++ FrameScope scope(masm, StackFrame::WASM_DEBUG_BREAK); ++ ++ // Save all parameter registers. They might hold live values, we restore ++ // them after the runtime call. ++ __ MultiPush(WasmDebugBreakFrameConstants::kPushedGpRegs); ++ __ MultiPushFPU(WasmDebugBreakFrameConstants::kPushedFpRegs); ++ ++ // Initialize the JavaScript context with 0. CEntry will use it to ++ // set the current context on the isolate. ++ __ Move(cp, Smi::zero()); ++ __ CallRuntime(Runtime::kWasmDebugBreak, 0); ++ ++ // Restore registers. ++ __ MultiPopFPU(WasmDebugBreakFrameConstants::kPushedFpRegs); ++ __ MultiPop(WasmDebugBreakFrameConstants::kPushedGpRegs); ++ } ++ __ Ret(); ++} ++ ++void Builtins::Generate_GenericJSToWasmWrapper(MacroAssembler* masm) { ++ __ Trap(); ++} ++ ++void Builtins::Generate_WasmReturnPromiseOnSuspend(MacroAssembler* masm) { ++ // TODO(v8:12191): Implement for this platform. ++ __ Trap(); ++} ++ ++void Builtins::Generate_WasmSuspend(MacroAssembler* masm) { ++ // TODO(v8:12191): Implement for this platform. ++ __ Trap(); ++} ++ ++void Builtins::Generate_WasmResume(MacroAssembler* masm) { ++ // TODO(v8:12191): Implement for this platform. ++ __ Trap(); ++} ++ ++void Builtins::Generate_WasmOnStackReplace(MacroAssembler* masm) { ++ // Only needed on x64. ++ __ Trap(); ++} ++ ++#endif // V8_ENABLE_WEBASSEMBLY ++ ++void Builtins::Generate_CEntry(MacroAssembler* masm, int result_size, ++ SaveFPRegsMode save_doubles, ArgvMode argv_mode, ++ bool builtin_exit_frame) { ++ // Called from JavaScript; parameters are on stack as if calling JS function ++ // a0: number of arguments including receiver ++ // a1: pointer to builtin function ++ // fp: frame pointer (restored after C call) ++ // sp: stack pointer (restored as callee's sp after C call) ++ // cp: current context (C callee-saved) ++ // ++ // If argv_mode == ArgvMode::kRegister: ++ // a2: pointer to the first argument ++ ++ if (argv_mode == ArgvMode::kRegister) { ++ // Move argv into the correct register. ++ __ mov(s1, a2); ++ } else { ++ // Compute the argv pointer in a callee-saved register. ++ __ s8addl(a0, sp, s1); ++ DCHECK_EQ(kPointerSizeLog2, 3); ++ __ Subl(s1, s1, kPointerSize); ++ } ++ ++ // Enter the exit frame that transitions from JavaScript to C++. ++ FrameScope scope(masm, StackFrame::MANUAL); ++ __ EnterExitFrame( ++ save_doubles == SaveFPRegsMode::kSave, 0, ++ builtin_exit_frame ? StackFrame::BUILTIN_EXIT : StackFrame::EXIT); ++ ++ // s0: number of arguments including receiver (C callee-saved) ++ // s1: pointer to first argument (C callee-saved) ++ // s2: pointer to builtin function (C callee-saved) ++ ++ // Prepare arguments for C routine. ++ // a0 = argc ++ __ mov(s0, a0); ++ __ mov(s2, a1); ++ ++ // We are calling compiled C/C++ code. a0 and a1 hold our two arguments. We ++ // also need to reserve the 4 argument slots on the stack. ++ ++ __ AssertStackIsAligned(); ++ ++ // a0 = argc, a1 = argv, a2 = isolate ++ __ li(a2, ExternalReference::isolate_address(masm->isolate())); ++ __ mov(a1, s1); ++ ++ __ StoreReturnAddressAndCall(s2); ++ ++ // Result returned in v0 or v1:v0 (a5:v0) - do not destroy these registers! ++ ++ // Check result for exception sentinel. ++ Label exception_returned; ++ __ LoadRoot(a4, RootIndex::kException); ++ __ Branch(&exception_returned, eq, a4, Operand(v0)); ++ ++ // Check that there is no pending exception, otherwise we ++ // should have returned the exception sentinel. ++ if (FLAG_debug_code) { ++ Label okay; ++ ExternalReference pending_exception_address = ExternalReference::Create( ++ IsolateAddressId::kPendingExceptionAddress, masm->isolate()); ++ __ li(a2, pending_exception_address); ++ __ Ldl(a2, MemOperand(a2)); ++ __ LoadRoot(a4, RootIndex::kTheHoleValue); ++ // Cannot use check here as it attempts to generate call into runtime. ++ __ Branch(&okay, eq, a4, Operand(a2)); ++ __ halt(); // stop("Unexpected pending exception"); ++ __ bind(&okay); ++ } ++ ++ // Exit C frame and return. ++ // v0:v1(v0:a5): result ++ // sp: stack pointer ++ // fp: frame pointer ++ Register argc = argv_mode == ArgvMode::kRegister ++ // We don't want to pop arguments so set argc to no_reg. ++ ? no_reg ++ // s0: still holds argc (callee-saved). ++ : s0; ++ __ LeaveExitFrame(save_doubles == SaveFPRegsMode::kSave, argc, EMIT_RETURN); ++ ++ // Handling of exception. ++ __ bind(&exception_returned); ++ ++ ExternalReference pending_handler_context_address = ExternalReference::Create( ++ IsolateAddressId::kPendingHandlerContextAddress, masm->isolate()); ++ ExternalReference pending_handler_entrypoint_address = ++ ExternalReference::Create( ++ IsolateAddressId::kPendingHandlerEntrypointAddress, masm->isolate()); ++ ExternalReference pending_handler_fp_address = ExternalReference::Create( ++ IsolateAddressId::kPendingHandlerFPAddress, masm->isolate()); ++ ExternalReference pending_handler_sp_address = ExternalReference::Create( ++ IsolateAddressId::kPendingHandlerSPAddress, masm->isolate()); ++ ++ // Ask the runtime for help to determine the handler. This will set v0 to ++ // contain the current pending exception, don't clobber it. ++ ExternalReference find_handler = ++ ExternalReference::Create(Runtime::kUnwindAndFindExceptionHandler); ++ { ++ FrameScope scope(masm, StackFrame::MANUAL); ++ __ PrepareCallCFunction(3, 0, a0); ++ __ mov(a0, zero_reg); ++ __ mov(a1, zero_reg); ++ __ li(a2, ExternalReference::isolate_address(masm->isolate())); ++ __ CallCFunction(find_handler, 3); ++ } ++ ++ // Retrieve the handler context, SP and FP. ++ __ li(cp, pending_handler_context_address); ++ __ Ldl(cp, MemOperand(cp)); ++ __ li(sp, pending_handler_sp_address); ++ __ Ldl(sp, MemOperand(sp)); ++ __ li(fp, pending_handler_fp_address); ++ __ Ldl(fp, MemOperand(fp)); ++ ++ // If the handler is a JS frame, restore the context to the frame. Note that ++ // the context will be set to (cp == 0) for non-JS frames. ++ Label zero; ++ __ Branch(&zero, eq, cp, Operand(zero_reg)); ++ __ Stl(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); ++ __ bind(&zero); ++ ++ // Clear c_entry_fp, like we do in `LeaveExitFrame`. ++ { ++ // scratch may conficts to in li. ++ Register scratch = t11; ++ __ li(scratch, ExternalReference::Create(IsolateAddressId::kCEntryFPAddress, ++ masm->isolate())); ++ __ Stl(zero_reg, MemOperand(scratch)); ++ } ++ ++ // Compute the handler entry address and jump to it. ++ __ li(t12, pending_handler_entrypoint_address); ++ __ Ldl(t12, MemOperand(t12)); ++ __ Jump(t12); ++} ++ ++void Builtins::Generate_DoubleToI(MacroAssembler* masm) { ++ Label done; ++ Register result_reg = t0; ++ ++ Register scratch = GetRegisterThatIsNotOneOf(result_reg); ++ Register scratch2 = GetRegisterThatIsNotOneOf(result_reg, scratch); ++ Register scratch3 = GetRegisterThatIsNotOneOf(result_reg, scratch, scratch2); ++ ++ // Account for saved regs. ++ const int kArgumentOffset = 4 * kPointerSize; ++ ++ __ Push(result_reg, scratch, scratch2, scratch3); ++ ++ // Load the double value and perform a manual truncation. ++ Register input_high = scratch2; ++ Register input_low = scratch3; ++ ++ __ Ldw(input_low, ++ MemOperand(sp, kArgumentOffset + Register::kMantissaOffset)); ++ __ Ldw(input_high, ++ MemOperand(sp, kArgumentOffset + Register::kExponentOffset)); ++ ++ Label normal_exponent; ++ // Extract the biased exponent in result. ++ __ Extl(result_reg, input_high, HeapNumber::kExponentShift, ++ HeapNumber::kExponentBits); ++ ++ // Check for Infinity and NaNs, which should return 0. ++ __ Subw(scratch, result_reg, HeapNumber::kExponentMask); ++ __ Seleq(result_reg, zero_reg, scratch); ++ __ Branch(&done, eq, scratch, Operand(zero_reg)); ++ ++ // Express exponent as delta to (number of mantissa bits + 31). ++ __ Subw(result_reg, result_reg, ++ Operand(HeapNumber::kExponentBias + HeapNumber::kMantissaBits + 31)); ++ ++ // If the delta is strictly positive, all bits would be shifted away, ++ // which means that we can return 0. ++ __ Branch(&normal_exponent, le, result_reg, Operand(zero_reg)); ++ __ mov(result_reg, zero_reg); ++ __ Branch(&done); ++ ++ __ bind(&normal_exponent); ++ const int kShiftBase = HeapNumber::kNonMantissaBitsInTopWord - 1; ++ // Calculate shift. ++ __ Addw(scratch, result_reg, Operand(kShiftBase + HeapNumber::kMantissaBits)); ++ ++ // Save the sign. ++ Register sign = result_reg; ++ result_reg = no_reg; ++ __ And(sign, input_high, Operand(HeapNumber::kSignMask)); ++ ++ // On ARM shifts > 31 bits are valid and will result in zero. On SW64 we need ++ // to check for this specific case. ++ Label high_shift_needed, high_shift_done; ++ __ Branch(&high_shift_needed, lt, scratch, Operand(32)); ++ __ mov(input_high, zero_reg); ++ __ Branch(&high_shift_done); ++ __ bind(&high_shift_needed); ++ ++ // Set the implicit 1 before the mantissa part in input_high. ++ __ Or(input_high, input_high, ++ Operand(1 << HeapNumber::kMantissaBitsInTopWord)); ++ // Shift the mantissa bits to the correct position. ++ // We don't need to clear non-mantissa bits as they will be shifted away. ++ // If they weren't, it would mean that the answer is in the 32bit range. ++ __ Sllw(input_high, input_high, scratch); ++ ++ __ bind(&high_shift_done); ++ ++ // Replace the shifted bits with bits from the lower mantissa word. ++ Label pos_shift, shift_done; ++ __ li(kScratchReg, 32); ++ __ Subw(scratch, kScratchReg, scratch); ++ __ Branch(&pos_shift, ge, scratch, Operand(zero_reg)); ++ ++ // Negate scratch. ++ __ Subw(scratch, zero_reg, scratch); ++ __ Sllw(input_low, input_low, scratch); ++ __ Branch(&shift_done); ++ ++ __ bind(&pos_shift); ++ __ Srlw(input_low, input_low, scratch); ++ ++ __ bind(&shift_done); ++ __ Or(input_high, input_high, Operand(input_low)); ++ // Restore sign if necessary. ++ __ mov(scratch, sign); ++ result_reg = sign; ++ sign = no_reg; ++ __ Subw(result_reg, zero_reg, input_high); ++ __ Seleq(result_reg, input_high, scratch); ++ ++ __ bind(&done); ++ ++ __ Stl(result_reg, MemOperand(sp, kArgumentOffset)); ++ __ Pop(result_reg, scratch, scratch2, scratch3); ++ __ Ret(); ++} ++ ++namespace { ++ ++int AddressOffset(ExternalReference ref0, ExternalReference ref1) { ++ int64_t offset = (ref0.address() - ref1.address()); ++ DCHECK(static_cast(offset) == offset); ++ return static_cast(offset); ++} ++ ++// Calls an API function. Allocates HandleScope, extracts returned value ++// from handle and propagates exceptions. Restores context. stack_space ++// - space to be unwound on exit (includes the call JS arguments space and ++// the additional space allocated for the fast call). ++void CallApiFunctionAndReturn(MacroAssembler* masm, Register function_address, ++ ExternalReference thunk_ref, int stack_space, ++ MemOperand* stack_space_operand, ++ MemOperand return_value_operand) { ++ Isolate* isolate = masm->isolate(); ++ ExternalReference next_address = ++ ExternalReference::handle_scope_next_address(isolate); ++ const int kNextOffset = 0; ++ const int kLimitOffset = AddressOffset( ++ ExternalReference::handle_scope_limit_address(isolate), next_address); ++ const int kLevelOffset = AddressOffset( ++ ExternalReference::handle_scope_level_address(isolate), next_address); ++ ++ DCHECK(function_address == a1 || function_address == a2); ++ ++ Label profiler_enabled, end_profiler_check; ++ __ li(t12, ExternalReference::is_profiling_address(isolate)); ++ __ Ldb(t12, MemOperand(t12, 0)); ++ __ Branch(&profiler_enabled, ne, t12, Operand(zero_reg)); ++ __ li(t12, ExternalReference::address_of_runtime_stats_flag()); ++ __ Ldw(t12, MemOperand(t12, 0)); ++ __ Branch(&profiler_enabled, ne, t12, Operand(zero_reg)); ++ { ++ // Call the api function directly. ++ __ mov(t12, function_address); ++ __ Branch(&end_profiler_check); ++ } ++ ++ __ bind(&profiler_enabled); ++ { ++ // Additional parameter is the address of the actual callback. ++ __ li(t12, thunk_ref); ++ } ++ __ bind(&end_profiler_check); ++ ++ // Allocate HandleScope in callee-save registers. ++ __ li(s3, next_address); ++ __ Ldl(s0, MemOperand(s3, kNextOffset)); ++ __ Ldl(s1, MemOperand(s3, kLimitOffset)); ++ __ Ldw(s2, MemOperand(s3, kLevelOffset)); ++ __ Addw(s2, s2, Operand(1)); ++ __ Stw(s2, MemOperand(s3, kLevelOffset)); ++ ++ __ StoreReturnAddressAndCall(t12); ++ ++ Label promote_scheduled_exception; ++ Label delete_allocated_handles; ++ Label leave_exit_frame; ++ Label return_value_loaded; ++ ++ // Load value from ReturnValue. ++ __ Ldl(v0, return_value_operand); ++ __ bind(&return_value_loaded); ++ ++ // No more valid handles (the result handle was the last one). Restore ++ // previous handle scope. ++ __ Stl(s0, MemOperand(s3, kNextOffset)); ++ if (FLAG_debug_code) { ++ __ Ldw(a1, MemOperand(s3, kLevelOffset)); ++ __ Check(eq, AbortReason::kUnexpectedLevelAfterReturnFromApiCall, a1, ++ Operand(s2)); ++ } ++ __ Subw(s2, s2, Operand(1)); ++ __ Stw(s2, MemOperand(s3, kLevelOffset)); ++ __ Ldl(kScratchReg, MemOperand(s3, kLimitOffset)); ++ __ Branch(&delete_allocated_handles, ne, s1, Operand(kScratchReg)); ++ ++ // Leave the API exit frame. ++ __ bind(&leave_exit_frame); ++ ++ if (stack_space_operand == nullptr) { ++ DCHECK_NE(stack_space, 0); ++ __ li(s0, Operand(stack_space)); ++ } else { ++ DCHECK_EQ(stack_space, 0); ++ STATIC_ASSERT(kCArgSlotCount == 0); ++ __ Ldl(s0, *stack_space_operand); ++ } ++ ++ static constexpr bool kDontSaveDoubles = false; ++ static constexpr bool kRegisterContainsSlotCount = false; ++ __ LeaveExitFrame(kDontSaveDoubles, s0, NO_EMIT_RETURN, ++ kRegisterContainsSlotCount); ++ ++ // Check if the function scheduled an exception. ++ __ LoadRoot(a4, RootIndex::kTheHoleValue); ++ __ li(kScratchReg, ExternalReference::scheduled_exception_address(isolate)); ++ __ Ldl(a5, MemOperand(kScratchReg)); ++ __ Branch(&promote_scheduled_exception, ne, a4, Operand(a5)); ++ ++ __ Ret(); ++ ++ // Re-throw by promoting a scheduled exception. ++ __ bind(&promote_scheduled_exception); ++ __ TailCallRuntime(Runtime::kPromoteScheduledException); ++ ++ // HandleScope limit has changed. Delete allocated extensions. ++ __ bind(&delete_allocated_handles); ++ __ Stl(s1, MemOperand(s3, kLimitOffset)); ++ __ mov(s0, v0); ++ __ mov(a0, v0); ++ __ PrepareCallCFunction(1, s1); ++ __ li(a0, ExternalReference::isolate_address(isolate)); ++ __ CallCFunction(ExternalReference::delete_handle_scope_extensions(), 1); ++ __ mov(v0, s0); ++ __ jmp(&leave_exit_frame); ++} ++ ++} // namespace ++ ++void Builtins::Generate_CallApiCallback(MacroAssembler* masm) { ++ // ----------- S t a t e ------------- ++ // -- cp : context ++ // -- a1 : api function address ++ // -- a2 : arguments count ++ // -- a3 : call data ++ // -- a0 : holder ++ // -- sp[0] : receiver ++ // -- sp[8] : first argument ++ // -- ... ++ // -- sp[(argc) * 8] : last argument ++ // ----------------------------------- ++ ++ Register api_function_address = a1; ++ Register argc = a2; ++ Register call_data = a3; ++ Register holder = a0; ++ Register scratch = t0; ++ Register base = t1; // For addressing MemOperands on the stack. ++ ++ DCHECK(!AreAliased(api_function_address, argc, call_data, holder, scratch, ++ base)); ++ ++ using FCA = FunctionCallbackArguments; ++ ++ STATIC_ASSERT(FCA::kArgsLength == 6); ++ STATIC_ASSERT(FCA::kNewTargetIndex == 5); ++ STATIC_ASSERT(FCA::kDataIndex == 4); ++ STATIC_ASSERT(FCA::kReturnValueOffset == 3); ++ STATIC_ASSERT(FCA::kReturnValueDefaultValueIndex == 2); ++ STATIC_ASSERT(FCA::kIsolateIndex == 1); ++ STATIC_ASSERT(FCA::kHolderIndex == 0); ++ ++ // Set up FunctionCallbackInfo's implicit_args on the stack as follows: ++ // ++ // Target state: ++ // sp[0 * kPointerSize]: kHolder ++ // sp[1 * kPointerSize]: kIsolate ++ // sp[2 * kPointerSize]: undefined (kReturnValueDefaultValue) ++ // sp[3 * kPointerSize]: undefined (kReturnValue) ++ // sp[4 * kPointerSize]: kData ++ // sp[5 * kPointerSize]: undefined (kNewTarget) ++ ++ // Set up the base register for addressing through MemOperands. It will point ++ // at the receiver (located at sp + argc * kPointerSize). ++ __ s8addl(argc, sp, base); ++ DCHECK_EQ(kPointerSizeLog2, 3); ++ ++ // Reserve space on the stack. ++ __ Subl(sp, sp, Operand(FCA::kArgsLength * kPointerSize)); ++ ++ // kHolder. ++ __ Stl(holder, MemOperand(sp, 0 * kPointerSize)); ++ ++ // kIsolate. ++ __ li(scratch, ExternalReference::isolate_address(masm->isolate())); ++ __ Stl(scratch, MemOperand(sp, 1 * kPointerSize)); ++ ++ // kReturnValueDefaultValue and kReturnValue. ++ __ LoadRoot(scratch, RootIndex::kUndefinedValue); ++ __ Stl(scratch, MemOperand(sp, 2 * kPointerSize)); ++ __ Stl(scratch, MemOperand(sp, 3 * kPointerSize)); ++ ++ // kData. ++ __ Stl(call_data, MemOperand(sp, 4 * kPointerSize)); ++ ++ // kNewTarget. ++ __ Stl(scratch, MemOperand(sp, 5 * kPointerSize)); ++ ++ // Keep a pointer to kHolder (= implicit_args) in a scratch register. ++ // We use it below to set up the FunctionCallbackInfo object. ++ __ mov(scratch, sp); ++ ++ // Allocate the v8::Arguments structure in the arguments' space since ++ // it's not controlled by GC. ++ static constexpr int kApiStackSpace = 4; ++ static constexpr bool kDontSaveDoubles = false; ++ FrameScope frame_scope(masm, StackFrame::MANUAL); ++ __ EnterExitFrame(kDontSaveDoubles, kApiStackSpace); ++ ++ // EnterExitFrame may align the sp. ++ ++ // FunctionCallbackInfo::implicit_args_ (points at kHolder as set up above). ++ // Arguments are after the return address (pushed by EnterExitFrame()). ++ __ Stl(scratch, MemOperand(sp, 1 * kPointerSize)); ++ ++ // FunctionCallbackInfo::values_ (points at the first varargs argument passed ++ // on the stack). ++ __ Addl(scratch, scratch, ++ Operand((FCA::kArgsLength + 1) * kSystemPointerSize)); ++ ++ __ Stl(scratch, MemOperand(sp, 2 * kPointerSize)); ++ ++ // FunctionCallbackInfo::length_. ++ // Stored as int field, 32-bit integers within struct on stack always left ++ // justified by n64 ABI. ++ __ Stw(argc, MemOperand(sp, 3 * kPointerSize)); ++ ++ // We also store the number of bytes to drop from the stack after returning ++ // from the API function here. ++ // Note: Unlike on other architectures, this stores the number of slots to ++ // drop, not the number of bytes. ++ __ Addl(scratch, argc, Operand(FCA::kArgsLength + 1 /* receiver */)); ++ __ Stl(scratch, MemOperand(sp, 4 * kPointerSize)); ++ ++ // v8::InvocationCallback's argument. ++ DCHECK(!AreAliased(api_function_address, scratch, a0)); ++ __ Addl(a0, sp, Operand(1 * kPointerSize)); ++ ++ ExternalReference thunk_ref = ExternalReference::invoke_function_callback(); ++ ++ // There are two stack slots above the arguments we constructed on the stack. ++ // TODO(jgruber): Document what these arguments are. ++ static constexpr int kStackSlotsAboveFCA = 2; ++ MemOperand return_value_operand( ++ fp, (kStackSlotsAboveFCA + FCA::kReturnValueOffset) * kPointerSize); ++ ++ static constexpr int kUseStackSpaceOperand = 0; ++ MemOperand stack_space_operand(sp, 4 * kPointerSize); ++ ++ AllowExternalCallThatCantCauseGC scope(masm); ++ CallApiFunctionAndReturn(masm, api_function_address, thunk_ref, ++ kUseStackSpaceOperand, &stack_space_operand, ++ return_value_operand); ++} ++ ++void Builtins::Generate_CallApiGetter(MacroAssembler* masm) { ++ // Build v8::PropertyCallbackInfo::args_ array on the stack and push property ++ // name below the exit frame to make GC aware of them. ++ STATIC_ASSERT(PropertyCallbackArguments::kShouldThrowOnErrorIndex == 0); ++ STATIC_ASSERT(PropertyCallbackArguments::kHolderIndex == 1); ++ STATIC_ASSERT(PropertyCallbackArguments::kIsolateIndex == 2); ++ STATIC_ASSERT(PropertyCallbackArguments::kReturnValueDefaultValueIndex == 3); ++ STATIC_ASSERT(PropertyCallbackArguments::kReturnValueOffset == 4); ++ STATIC_ASSERT(PropertyCallbackArguments::kDataIndex == 5); ++ STATIC_ASSERT(PropertyCallbackArguments::kThisIndex == 6); ++ STATIC_ASSERT(PropertyCallbackArguments::kArgsLength == 7); ++ ++ Register receiver = ApiGetterDescriptor::ReceiverRegister(); ++ Register holder = ApiGetterDescriptor::HolderRegister(); ++ Register callback = ApiGetterDescriptor::CallbackRegister(); ++ Register scratch = a4; ++ DCHECK(!AreAliased(receiver, holder, callback, scratch)); ++ ++ Register api_function_address = a2; ++ ++ // Here and below +1 is for name() pushed after the args_ array. ++ using PCA = PropertyCallbackArguments; ++ __ Subl(sp, sp, (PCA::kArgsLength + 1) * kPointerSize); ++ __ Stl(receiver, MemOperand(sp, (PCA::kThisIndex + 1) * kPointerSize)); ++ __ Ldl(scratch, FieldMemOperand(callback, AccessorInfo::kDataOffset)); ++ __ Stl(scratch, MemOperand(sp, (PCA::kDataIndex + 1) * kPointerSize)); ++ __ LoadRoot(scratch, RootIndex::kUndefinedValue); ++ __ Stl(scratch, MemOperand(sp, (PCA::kReturnValueOffset + 1) * kPointerSize)); ++ __ Stl(scratch, MemOperand(sp, (PCA::kReturnValueDefaultValueIndex + 1) * ++ kPointerSize)); ++ __ li(scratch, ExternalReference::isolate_address(masm->isolate())); ++ __ Stl(scratch, MemOperand(sp, (PCA::kIsolateIndex + 1) * kPointerSize)); ++ __ Stl(holder, MemOperand(sp, (PCA::kHolderIndex + 1) * kPointerSize)); ++ // should_throw_on_error -> false ++ DCHECK_EQ(0, Smi::zero().ptr()); ++ __ Stl(zero_reg, ++ MemOperand(sp, (PCA::kShouldThrowOnErrorIndex + 1) * kPointerSize)); ++ __ Ldl(scratch, FieldMemOperand(callback, AccessorInfo::kNameOffset)); ++ __ Stl(scratch, MemOperand(sp, 0 * kPointerSize)); ++ ++ // v8::PropertyCallbackInfo::args_ array and name handle. ++ const int kStackUnwindSpace = PropertyCallbackArguments::kArgsLength + 1; ++ ++ // Load address of v8::PropertyAccessorInfo::args_ array and name handle. ++ __ mov(a0, sp); // a0 = Handle ++ __ Addl(a1, a0, Operand(1 * kPointerSize)); // a1 = v8::PCI::args_ ++ ++ const int kApiStackSpace = 1; ++ FrameScope frame_scope(masm, StackFrame::MANUAL); ++ __ EnterExitFrame(false, kApiStackSpace); ++ ++ // Create v8::PropertyCallbackInfo object on the stack and initialize ++ // it's args_ field. ++ __ Stl(a1, MemOperand(sp, 1 * kPointerSize)); ++ __ Addl(a1, sp, Operand(1 * kPointerSize)); ++ // a1 = v8::PropertyCallbackInfo& ++ ++ ExternalReference thunk_ref = ++ ExternalReference::invoke_accessor_getter_callback(); ++ ++ __ Ldl(scratch, FieldMemOperand(callback, AccessorInfo::kJsGetterOffset)); ++ __ Ldl(api_function_address, ++ FieldMemOperand(scratch, Foreign::kForeignAddressOffset)); ++ ++ // +3 is to skip prolog, return address and name handle. ++ MemOperand return_value_operand( ++ fp, (PropertyCallbackArguments::kReturnValueOffset + 3) * kPointerSize); ++ MemOperand* const kUseStackSpaceConstant = nullptr; ++ CallApiFunctionAndReturn(masm, api_function_address, thunk_ref, ++ kStackUnwindSpace, kUseStackSpaceConstant, ++ return_value_operand); ++} ++ ++void Builtins::Generate_DirectCEntry(MacroAssembler* masm) { ++ // The sole purpose of DirectCEntry is for movable callers (e.g. any general ++ // purpose Code object) to be able to call into C functions that may trigger ++ // GC and thus move the caller. ++ // ++ // DirectCEntry places the return address on the stack (updated by the GC), ++ // making the call GC safe. The irregexp backend relies on this. ++ ++ // Make place for arguments to fit C calling convention. Callers use ++ // EnterExitFrame/LeaveExitFrame so they handle stack restoring and we don't ++ // have to do that here. Any caller must drop kCArgsSlotsSize stack space ++ // after the call. ++ __ subl(sp, kCArgsSlotsSize, sp); ++ ++ __ Stl(ra, MemOperand(sp, kCArgsSlotsSize)); // Store the return address. ++ __ Call(t12); // Call the C++ function. ++ // set fpec 1 while return from C++. ++ __ setfpec1(); ++ __ Ldl(t12, MemOperand(sp, kCArgsSlotsSize)); // Return to calling code. ++ ++ if (FLAG_debug_code && FLAG_enable_slow_asserts) { ++ // In case of an error the return address may point to a memory area ++ // filled with kZapValue by the GC. Dereference the address and check for ++ // this. ++ __ Ldl_u(a4, MemOperand(t12)); ++ __ Assert(ne, AbortReason::kReceivedInvalidReturnAddress, a4, ++ Operand(reinterpret_cast(kZapValue))); ++ } ++ ++ __ Jump(t12); ++} ++ ++namespace { ++ ++// This code tries to be close to ia32 code so that any changes can be ++// easily ported. ++void Generate_DeoptimizationEntry(MacroAssembler* masm, ++ DeoptimizeKind deopt_kind) { ++ Isolate* isolate = masm->isolate(); ++ ++ // Unlike on ARM we don't save all the registers, just the useful ones. ++ // For the rest, there are gaps on the stack, so the offsets remain the same. ++ const int kNumberOfRegisters = Register::kNumRegisters; ++ ++ RegList restored_regs = kJSCallerSaved | kCalleeSaved; ++ RegList saved_regs = restored_regs | sp | ra; ++ ++ const int kDoubleRegsSize = kDoubleSize * DoubleRegister::kNumRegisters; ++ ++ // Save all double FPU registers before messing with them. ++ __ Subl(sp, sp, Operand(kDoubleRegsSize)); ++ const RegisterConfiguration* config = RegisterConfiguration::Default(); ++ for (int i = 0; i < config->num_allocatable_double_registers(); ++i) { ++ int code = config->GetAllocatableDoubleCode(i); ++ const DoubleRegister fpu_reg = DoubleRegister::from_code(code); ++ int offset = code * kDoubleSize; ++ __ Fstd(fpu_reg, MemOperand(sp, offset)); ++ } ++ ++ // Push saved_regs (needed to populate FrameDescription::registers_). ++ // Leave gaps for other registers. ++ __ Subl(sp, sp, kNumberOfRegisters * kPointerSize); ++ for (int16_t i = kNumberOfRegisters - 1; i >= 0; i--) { ++ if ((saved_regs.bits() & (1 << i)) != 0) { ++ __ Stl(ToRegister(i), MemOperand(sp, kPointerSize * i)); ++ } ++ } ++ ++ __ li(a2, ++ ExternalReference::Create(IsolateAddressId::kCEntryFPAddress, isolate)); ++ __ Stl(fp, MemOperand(a2)); ++ ++ const int kSavedRegistersAreaSize = ++ (kNumberOfRegisters * kPointerSize) + kDoubleRegsSize; ++ ++ // Get the address of the location in the code object (a2) (return ++ // address for lazy deoptimization) and compute the fp-to-sp delta in ++ // register a3. ++ __ mov(a2, ra); ++ __ Addl(a3, sp, Operand(kSavedRegistersAreaSize)); ++ ++ __ Subl(a3, fp, a3); ++ ++ // Allocate a new deoptimizer object. ++ __ PrepareCallCFunction(5, a4); ++ // Pass six arguments, according to n64 ABI. ++ __ mov(a0, zero_reg); ++ Label context_check; ++ __ Ldl(a1, MemOperand(fp, CommonFrameConstants::kContextOrFrameTypeOffset)); ++ __ JumpIfSmi(a1, &context_check); ++ __ Ldl(a0, MemOperand(fp, StandardFrameConstants::kFunctionOffset)); ++ __ bind(&context_check); ++ __ li(a1, Operand(static_cast(deopt_kind))); ++ // a2: code address or 0 already loaded. ++ // a3: already has fp-to-sp delta. ++ __ li(a4, ExternalReference::isolate_address(isolate)); ++ ++ // Call Deoptimizer::New(). ++ { ++ AllowExternalCallThatCantCauseGC scope(masm); ++ __ CallCFunction(ExternalReference::new_deoptimizer_function(), 5); ++ } ++ ++ // Preserve "deoptimizer" object in register v0 and get the input ++ // frame descriptor pointer to a1 (deoptimizer->input_); ++ // Move deopt-obj to a0 for call to Deoptimizer::ComputeOutputFrames() below. ++ __ mov(a0, v0); ++ __ Ldl(a1, MemOperand(v0, Deoptimizer::input_offset())); ++ ++ // Copy core registers into FrameDescription::registers_[kNumRegisters]. ++ DCHECK_EQ(Register::kNumRegisters, kNumberOfRegisters); ++ for (int i = 0; i < kNumberOfRegisters; i++) { ++ int offset = (i * kPointerSize) + FrameDescription::registers_offset(); ++ if ((saved_regs.bits() & (1 << i)) != 0) { ++ __ Ldl(a2, MemOperand(sp, i * kPointerSize)); ++ __ Stl(a2, MemOperand(a1, offset)); ++ } else if (FLAG_debug_code) { ++ __ li(a2, kDebugZapValue); ++ __ Stl(a2, MemOperand(a1, offset)); ++ } ++ } ++ ++ int double_regs_offset = FrameDescription::double_registers_offset(); ++ // Copy FPU registers to ++ // double_registers_[DoubleRegister::kNumAllocatableRegisters] ++ for (int i = 0; i < config->num_allocatable_double_registers(); ++i) { ++ int code = config->GetAllocatableDoubleCode(i); ++ int dst_offset = code * kDoubleSize + double_regs_offset; ++ int src_offset = code * kDoubleSize + kNumberOfRegisters * kPointerSize; ++ __ Fldd(f0, MemOperand(sp, src_offset)); ++ __ Fstd(f0, MemOperand(a1, dst_offset)); ++ } ++ ++ // Remove the saved registers from the stack. ++ __ Addl(sp, sp, Operand(kSavedRegistersAreaSize)); ++ ++ // Compute a pointer to the unwinding limit in register a2; that is ++ // the first stack slot not part of the input frame. ++ __ Ldl(a2, MemOperand(a1, FrameDescription::frame_size_offset())); ++ __ Addl(a2, a2, sp); ++ ++ // Unwind the stack down to - but not including - the unwinding ++ // limit and copy the contents of the activation frame to the input ++ // frame description. ++ __ Addl(a3, a1, Operand(FrameDescription::frame_content_offset())); ++ Label pop_loop; ++ Label pop_loop_header; ++ __ BranchShort(&pop_loop_header); ++ __ bind(&pop_loop); ++ __ pop(a4); ++ __ Stl(a4, MemOperand(a3, 0)); ++ __ addl(a3, sizeof(uint64_t), a3); ++ __ bind(&pop_loop_header); ++ __ BranchShort(&pop_loop, ne, a2, Operand(sp)); ++ // Compute the output frame in the deoptimizer. ++ __ Push(a0); // Preserve deoptimizer object across call. ++ // a0: deoptimizer object; a1: scratch. ++ __ PrepareCallCFunction(1, a1); ++ // Call Deoptimizer::ComputeOutputFrames(). ++ { ++ AllowExternalCallThatCantCauseGC scope(masm); ++ __ CallCFunction(ExternalReference::compute_output_frames_function(), 1); ++ } ++ __ pop(a0); // Restore deoptimizer object (class Deoptimizer). ++ ++ __ Ldl(sp, MemOperand(a0, Deoptimizer::caller_frame_top_offset())); ++ ++ // Replace the current (input) frame with the output frames. ++ Label outer_push_loop, inner_push_loop, outer_loop_header, inner_loop_header; ++ // Outer loop state: a4 = current "FrameDescription** output_", ++ // a1 = one past the last FrameDescription**. ++ __ Ldw(a1, MemOperand(a0, Deoptimizer::output_count_offset())); ++ __ Ldl(a4, MemOperand(a0, Deoptimizer::output_offset())); // a4 is output_. ++ __ Lsal(a1, a4, a1, kPointerSizeLog2); ++ __ BranchShort(&outer_loop_header); ++ __ bind(&outer_push_loop); ++ // Inner loop state: a2 = current FrameDescription*, a3 = loop index. ++ __ Ldl(a2, MemOperand(a4, 0)); // output_[ix] ++ __ Ldl(a3, MemOperand(a2, FrameDescription::frame_size_offset())); ++ __ BranchShort(&inner_loop_header); ++ __ bind(&inner_push_loop); ++ __ Subl(a3, a3, Operand(sizeof(uint64_t))); ++ __ Addl(t9, a2, Operand(a3)); ++ __ Ldl(t10, MemOperand(t9, FrameDescription::frame_content_offset())); ++ __ Push(t10); ++ __ bind(&inner_loop_header); ++ __ BranchShort(&inner_push_loop, ne, a3, Operand(zero_reg)); ++ ++ __ Addl(a4, a4, Operand(kPointerSize)); ++ __ bind(&outer_loop_header); ++ __ BranchShort(&outer_push_loop, lt, a4, Operand(a1)); ++ ++ __ Ldl(a1, MemOperand(a0, Deoptimizer::input_offset())); ++ for (int i = 0; i < config->num_allocatable_double_registers(); ++i) { ++ int code = config->GetAllocatableDoubleCode(i); ++ const DoubleRegister fpu_reg = DoubleRegister::from_code(code); ++ int src_offset = code * kDoubleSize + double_regs_offset; ++ __ Fldd(fpu_reg, MemOperand(a1, src_offset)); ++ } ++ ++ // Push pc and continuation from the last output frame. ++ __ Ldl(t9, MemOperand(a2, FrameDescription::pc_offset())); ++ __ Push(t9); ++ __ Ldl(t9, MemOperand(a2, FrameDescription::continuation_offset())); ++ __ Push(t9); ++ ++ // Technically restoring 'at' should work unless zero_reg is also restored ++ // but it's safer to check for this. ++ DCHECK(!(restored_regs.has(at))); ++ // Restore the registers from the last output frame. ++ __ mov(at, a2); ++ for (int i = kNumberOfRegisters - 1; i >= 0; i--) { ++ int offset = (i * kPointerSize) + FrameDescription::registers_offset(); ++ if ((restored_regs.bits() & (1 << i)) != 0) { ++ __ Ldl(ToRegister(i), MemOperand(at, offset)); ++ } ++ } ++ ++ __ pop(at); // Get continuation, leave pc on stack. ++ __ pop(ra); ++ __ Jump(at); ++ __ halt(); ++} ++ ++} // namespace ++ ++void Builtins::Generate_DeoptimizationEntry_Eager(MacroAssembler* masm) { ++ Generate_DeoptimizationEntry(masm, DeoptimizeKind::kEager); ++} ++ ++void Builtins::Generate_DeoptimizationEntry_Lazy(MacroAssembler* masm) { ++ Generate_DeoptimizationEntry(masm, DeoptimizeKind::kLazy); ++} ++ ++void Builtins::Generate_DeoptimizationEntry_Unused(MacroAssembler* masm) { ++ Generate_DeoptimizationEntry(masm, DeoptimizeKind::kUnused); ++} ++ ++namespace { ++ ++// Restarts execution either at the current or next (in execution order) ++// bytecode. If there is baseline code on the shared function info, converts an ++// interpreter frame into a baseline frame and continues execution in baseline ++// code. Otherwise execution continues with bytecode. ++void Generate_BaselineOrInterpreterEntry(MacroAssembler* masm, ++ bool next_bytecode, ++ bool is_osr = false) { ++ Label start; ++ __ bind(&start); ++ ++ // Get function from the frame. ++ Register closure = a1; ++ __ Ldl(closure, MemOperand(fp, StandardFrameConstants::kFunctionOffset)); ++ ++ // Get the Code object from the shared function info. ++ Register code_obj = s1; ++ __ Ldl(code_obj, ++ FieldMemOperand(closure, JSFunction::kSharedFunctionInfoOffset)); ++ __ Ldl(code_obj, ++ FieldMemOperand(code_obj, SharedFunctionInfo::kFunctionDataOffset)); ++ ++ // Check if we have baseline code. For OSR entry it is safe to assume we ++ // always have baseline code. ++ if (!is_osr) { ++ Label start_with_baseline; ++ __ GetObjectType(code_obj, t2, t2); ++ __ Branch(&start_with_baseline, eq, t2, Operand(CODE_TYPE)); ++ ++ // Start with bytecode as there is no baseline code. ++ Builtin builtin_id = next_bytecode ++ ? Builtin::kInterpreterEnterAtNextBytecode ++ : Builtin::kInterpreterEnterAtBytecode; ++ __ Jump(masm->isolate()->builtins()->code_handle(builtin_id), ++ RelocInfo::CODE_TARGET); ++ ++ // Start with baseline code. ++ __ bind(&start_with_baseline); ++ } else if (FLAG_debug_code) { ++ __ GetObjectType(code_obj, t2, t2); ++ __ Assert(eq, AbortReason::kExpectedBaselineData, t2, Operand(CODET_TYPE)); ++ } ++ ++ if (FLAG_debug_code) { ++ AssertCodeIsBaseline(masm, code_obj, t2); ++ } ++ ++ // Replace BytecodeOffset with the feedback vector. ++ Register feedback_vector = a2; ++ __ Ldl(feedback_vector, ++ FieldMemOperand(closure, JSFunction::kFeedbackCellOffset)); ++ __ Ldl(feedback_vector, FieldMemOperand(feedback_vector, Cell::kValueOffset)); ++ ++ Label install_baseline_code; ++ // Check if feedback vector is valid. If not, call prepare for baseline to ++ // allocate it. ++ __ GetObjectType(feedback_vector, t2, t2); ++ __ Branch(&install_baseline_code, ne, t2, Operand(FEEDBACK_VECTOR_TYPE)); ++ ++ // Save BytecodeOffset from the stack frame. ++ __ SmiUntag(kInterpreterBytecodeOffsetRegister, ++ MemOperand(fp, InterpreterFrameConstants::kBytecodeOffsetFromFp)); ++ // Replace BytecodeOffset with the feedback vector. ++ __ Stl(feedback_vector, ++ MemOperand(fp, InterpreterFrameConstants::kBytecodeOffsetFromFp)); ++ feedback_vector = no_reg; ++ ++ // Compute baseline pc for bytecode offset. ++ ExternalReference get_baseline_pc_extref; ++ if (next_bytecode || is_osr) { ++ get_baseline_pc_extref = ++ ExternalReference::baseline_pc_for_next_executed_bytecode(); ++ } else { ++ get_baseline_pc_extref = ++ ExternalReference::baseline_pc_for_bytecode_offset(); ++ } ++ ++ Register get_baseline_pc = a3; ++ __ li(get_baseline_pc, get_baseline_pc_extref); ++ ++ // If the code deoptimizes during the implicit function entry stack interrupt ++ // check, it will have a bailout ID of kFunctionEntryBytecodeOffset, which is ++ // not a valid bytecode offset. ++ // TODO(pthier): Investigate if it is feasible to handle this special case ++ // in TurboFan instead of here. ++ Label valid_bytecode_offset, function_entry_bytecode; ++ if (!is_osr) { ++ __ Branch(&function_entry_bytecode, eq, kInterpreterBytecodeOffsetRegister, ++ Operand(BytecodeArray::kHeaderSize - kHeapObjectTag + ++ kFunctionEntryBytecodeOffset)); ++ } ++ ++ __ Subl(kInterpreterBytecodeOffsetRegister, ++ kInterpreterBytecodeOffsetRegister, ++ (BytecodeArray::kHeaderSize - kHeapObjectTag)); ++ ++ __ bind(&valid_bytecode_offset); ++ // Get bytecode array from the stack frame. ++ __ Ldl(kInterpreterBytecodeArrayRegister, ++ MemOperand(fp, InterpreterFrameConstants::kBytecodeArrayFromFp)); ++ // Save the accumulator register, since it's clobbered by the below call. ++ __ Push(kInterpreterAccumulatorRegister); ++ { ++ Register arg_reg_1 = a0; ++ Register arg_reg_2 = a1; ++ Register arg_reg_3 = a2; ++ __ Move(arg_reg_1, code_obj); ++ __ Move(arg_reg_2, kInterpreterBytecodeOffsetRegister); ++ __ Move(arg_reg_3, kInterpreterBytecodeArrayRegister); ++ FrameScope scope(masm, StackFrame::INTERNAL); ++ __ PrepareCallCFunction(3, 0, a4); ++ __ CallCFunction(get_baseline_pc, 3, 0); ++ } ++ __ Addl(code_obj, code_obj, kReturnRegister0); ++ __ Pop(kInterpreterAccumulatorRegister); ++ ++ if (is_osr) { ++ // TODO(pthier): Separate baseline Sparkplug from TF arming and don't disarm ++ // Sparkplug here. ++ // TODO(liuyu): Remove Ld as arm64 after register reallocation. ++ __ Ldl(kInterpreterBytecodeArrayRegister, ++ MemOperand(fp, InterpreterFrameConstants::kBytecodeArrayFromFp)); ++ ResetBytecodeAgeAndOsrState(masm, kInterpreterBytecodeArrayRegister); ++ Generate_OSREntry(masm, code_obj, ++ Operand(Code::kHeaderSize - kHeapObjectTag)); ++ } else { ++ __ Addl(code_obj, code_obj, Code::kHeaderSize - kHeapObjectTag); ++ __ Jump(code_obj); ++ } ++ __ Trap(); // Unreachable. ++ ++ if (!is_osr) { ++ __ bind(&function_entry_bytecode); ++ // If the bytecode offset is kFunctionEntryOffset, get the start address of ++ // the first bytecode. ++ __ mov(kInterpreterBytecodeOffsetRegister, zero_reg); ++ if (next_bytecode) { ++ __ li(get_baseline_pc, ++ ExternalReference::baseline_pc_for_bytecode_offset()); ++ } ++ __ Branch(&valid_bytecode_offset); ++ } ++ ++ __ bind(&install_baseline_code); ++ { ++ FrameScope scope(masm, StackFrame::INTERNAL); ++ __ Push(kInterpreterAccumulatorRegister); ++ __ Push(closure); ++ __ CallRuntime(Runtime::kInstallBaselineCode, 1); ++ __ Pop(kInterpreterAccumulatorRegister); ++ } ++ // Retry from the start after installing baseline code. ++ __ Branch(&start); ++} ++ ++} // namespace ++ ++void Builtins::Generate_BaselineOrInterpreterEnterAtBytecode( ++ MacroAssembler* masm) { ++ Generate_BaselineOrInterpreterEntry(masm, false); ++} ++ ++void Builtins::Generate_BaselineOrInterpreterEnterAtNextBytecode( ++ MacroAssembler* masm) { ++ Generate_BaselineOrInterpreterEntry(masm, true); ++} ++ ++void Builtins::Generate_InterpreterOnStackReplacement_ToBaseline( ++ MacroAssembler* masm) { ++ Generate_BaselineOrInterpreterEntry(masm, false, true); ++} ++ ++#undef __ ++ ++} // namespace internal ++} // namespace v8 ++ ++#endif // V8_TARGET_ARCH_SW64 +diff --git a/deps/v8/src/codegen/assembler-arch.h b/deps/v8/src/codegen/assembler-arch.h +index 2e1b56c4..7d84c6d6 100644 +--- a/deps/v8/src/codegen/assembler-arch.h ++++ b/deps/v8/src/codegen/assembler-arch.h +@@ -23,6 +23,8 @@ + #include "src/codegen/mips64/assembler-mips64.h" + #elif V8_TARGET_ARCH_LOONG64 + #include "src/codegen/loong64/assembler-loong64.h" ++#elif V8_TARGET_ARCH_SW64 ++#include "src/codegen/sw64/assembler-sw64.h" + #elif V8_TARGET_ARCH_S390 + #include "src/codegen/s390/assembler-s390.h" + #elif V8_TARGET_ARCH_RISCV64 +diff --git a/deps/v8/src/codegen/assembler-inl.h b/deps/v8/src/codegen/assembler-inl.h +index 084f12cc..4b95cd8b 100644 +--- a/deps/v8/src/codegen/assembler-inl.h ++++ b/deps/v8/src/codegen/assembler-inl.h +@@ -23,6 +23,8 @@ + #include "src/codegen/mips64/assembler-mips64-inl.h" + #elif V8_TARGET_ARCH_LOONG64 + #include "src/codegen/loong64/assembler-loong64-inl.h" ++#elif V8_TARGET_ARCH_SW64 ++#include "src/codegen/sw64/assembler-sw64-inl.h" + #elif V8_TARGET_ARCH_S390 + #include "src/codegen/s390/assembler-s390-inl.h" + #elif V8_TARGET_ARCH_RISCV64 +diff --git a/deps/v8/src/codegen/constants-arch.h b/deps/v8/src/codegen/constants-arch.h +index 7eb32baf..4e9eb875 100644 +--- a/deps/v8/src/codegen/constants-arch.h ++++ b/deps/v8/src/codegen/constants-arch.h +@@ -17,6 +17,8 @@ + #include "src/codegen/mips64/constants-mips64.h" + #elif V8_TARGET_ARCH_LOONG64 + #include "src/codegen/loong64/constants-loong64.h" ++#elif V8_TARGET_ARCH_SW64 ++#include "src/codegen/sw64/constants-sw64.h" + #elif V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_PPC64 + #include "src/codegen/ppc/constants-ppc.h" + #elif V8_TARGET_ARCH_S390 +diff --git a/deps/v8/src/codegen/cpu-features.h b/deps/v8/src/codegen/cpu-features.h +index 88f28b92..04473bca 100644 +--- a/deps/v8/src/codegen/cpu-features.h ++++ b/deps/v8/src/codegen/cpu-features.h +@@ -55,6 +55,13 @@ enum CpuFeature { + #elif V8_TARGET_ARCH_LOONG64 + FPU, + ++#elif V8_TARGET_ARCH_SW64 ++ FPU, ++//SKTODO ++ FP64FPU, ++ SW64_SIMD, // SSA instructions ++ SW_8A, // SW 8A instructions ++ + #elif V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_PPC64 + PPC_6_PLUS, + PPC_7_PLUS, +diff --git a/deps/v8/src/codegen/external-reference.cc b/deps/v8/src/codegen/external-reference.cc +index 6d206663..733cff1b 100644 +--- a/deps/v8/src/codegen/external-reference.cc ++++ b/deps/v8/src/codegen/external-reference.cc +@@ -741,6 +741,8 @@ ExternalReference ExternalReference::invoke_accessor_getter_callback() { + #define re_stack_check_func RegExpMacroAssemblerMIPS::CheckStackGuardState + #elif V8_TARGET_ARCH_LOONG64 + #define re_stack_check_func RegExpMacroAssemblerLOONG64::CheckStackGuardState ++#elif V8_TARGET_ARCH_SW64 ++#define re_stack_check_func RegExpMacroAssemblerSW64::CheckStackGuardState + #elif V8_TARGET_ARCH_S390 + #define re_stack_check_func RegExpMacroAssemblerS390::CheckStackGuardState + #elif V8_TARGET_ARCH_RISCV64 +@@ -1375,6 +1377,101 @@ static int EnterMicrotaskContextWrapper(HandleScopeImplementer* hsi, + + FUNCTION_REFERENCE(call_enter_context_function, EnterMicrotaskContextWrapper) + ++ ++// ===================================================================== ++// add start for SW64. ++ ++#define SW_CONST64(x) (x ## LL) ++ ++const int sw_min_int = (int)1 << (sizeof(int)*8-1); // 0x80000000 == smallest int ++const long sw_min_long = SW_CONST64(0x8000000000000000); ++ ++static int sw_div(int x, int y) { ++ if (x == sw_min_int && y == SW_CONST64(-1)) { ++ return x; ++ } else { ++#if defined(USE_SIMULATOR) ++ if (0 == y) return 0; ++#endif ++ ++ return x / y; ++ } ++} ++ ++static int sw_divu(uint32_t x, uint32_t y) { ++ return (int)(x / y); ++} ++ ++static long sw_ddiv(long x, long y) { ++ if (x == sw_min_long && y == SW_CONST64(-1)) { ++ return x; ++ } else { ++ return x / y; ++ } ++} ++ ++static long sw_ddivu(uint64_t x, uint64_t y) { ++ return (long)(x / y); ++} ++ ++static int sw_mod(int x, int y) { ++ if (x == sw_min_int && y == SW_CONST64(-1)) { ++ return 0; ++ } else { ++ return x % y; ++ } ++} ++ ++static int sw_modu(uint32_t x, uint32_t y) { ++ return (int)(x % y); ++} ++ ++static long sw_dmod(long x, long y) { ++ if (x == sw_min_long && y == SW_CONST64(-1)) { ++ return 0; ++ } else { ++ return x % y; ++ } ++} ++ ++static long sw_dmodu(uint64_t x, uint64_t y) { ++ return (long)(x % y); ++} ++ ++ExternalReference ExternalReference::math_sw_div_function() { ++ return ExternalReference(Redirect(FUNCTION_ADDR(sw_div))); ++} ++ ++ExternalReference ExternalReference::math_sw_divu_function() { ++ return ExternalReference(Redirect(FUNCTION_ADDR(sw_divu))); ++} ++ ++ExternalReference ExternalReference::math_sw_ddiv_function() { ++ return ExternalReference(Redirect(FUNCTION_ADDR(sw_ddiv))); ++} ++ ++ExternalReference ExternalReference::math_sw_ddivu_function() { ++ return ExternalReference(Redirect(FUNCTION_ADDR(sw_ddivu))); ++} ++ ++ExternalReference ExternalReference::math_sw_mod_function() { ++ return ExternalReference(Redirect(FUNCTION_ADDR(sw_mod))); ++} ++ ++ExternalReference ExternalReference::math_sw_modu_function() { ++ return ExternalReference(Redirect(FUNCTION_ADDR(sw_modu))); ++} ++ ++ExternalReference ExternalReference::math_sw_dmod_function() { ++ return ExternalReference(Redirect(FUNCTION_ADDR(sw_dmod))); ++} ++ ++ExternalReference ExternalReference::math_sw_dmodu_function() { ++ return ExternalReference(Redirect(FUNCTION_ADDR(sw_dmodu))); ++} ++// add end for SW64. ++// ====================================================================== ++ + FUNCTION_REFERENCE( + js_finalization_registry_remove_cell_from_unregister_token_map, + JSFinalizationRegistry::RemoveCellFromUnregisterTokenMap) +diff --git a/deps/v8/src/codegen/external-reference.h b/deps/v8/src/codegen/external-reference.h +index 7715aa6a..bcfa5d06 100644 +--- a/deps/v8/src/codegen/external-reference.h ++++ b/deps/v8/src/codegen/external-reference.h +@@ -126,6 +126,14 @@ class StatsCounter; + V(f64_acos_wrapper_function, "f64_acos_wrapper") \ + V(f64_asin_wrapper_function, "f64_asin_wrapper") \ + V(f64_mod_wrapper_function, "f64_mod_wrapper") \ ++ V(math_sw_div_function, "sw_div") \ ++ V(math_sw_divu_function, "sw_divu") \ ++ V(math_sw_ddiv_function, "sw_ddiv") \ ++ V(math_sw_ddivu_function, "sw_ddivu") \ ++ V(math_sw_mod_function, "sw_mod") \ ++ V(math_sw_modu_function, "sw_modu") \ ++ V(math_sw_dmod_function, "sw_dmod") \ ++ V(math_sw_dmodu_function, "sw_dmodu") \ + V(get_date_field_function, "JSDate::GetField") \ + V(get_or_create_hash_raw, "get_or_create_hash_raw") \ + V(gsab_byte_length, "GsabByteLength") \ +diff --git a/deps/v8/src/codegen/interface-descriptors-inl.h b/deps/v8/src/codegen/interface-descriptors-inl.h +index 349874e2..f86cbd2d 100644 +--- a/deps/v8/src/codegen/interface-descriptors-inl.h ++++ b/deps/v8/src/codegen/interface-descriptors-inl.h +@@ -29,6 +29,8 @@ + #include "src/codegen/mips/interface-descriptors-mips-inl.h" + #elif V8_TARGET_ARCH_LOONG64 + #include "src/codegen/loong64/interface-descriptors-loong64-inl.h" ++#elif V8_TARGET_ARCH_SW64 ++#include "src/codegen/sw64/interface-descriptors-sw64-inl.h" + #elif V8_TARGET_ARCH_RISCV64 + #include "src/codegen/riscv64/interface-descriptors-riscv64-inl.h" + #else +@@ -225,7 +227,7 @@ constexpr RegList WriteBarrierDescriptor::ComputeSavedRegisters( + saved_registers.set(SlotAddressRegister()); + } + #elif V8_TARGET_ARCH_ARM64 || V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_LOONG64 || \ +- V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_MIPS ++ V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_SW64 + if (object != ObjectRegister()) saved_registers.set(ObjectRegister()); + // The slot address is always clobbered. + saved_registers.set(SlotAddressRegister()); +@@ -328,7 +330,7 @@ constexpr auto BaselineOutOfLinePrologueDescriptor::registers() { + #if V8_TARGET_ARCH_X64 || V8_TARGET_ARCH_ARM64 || V8_TARGET_ARCH_ARM || \ + V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_PPC64 || V8_TARGET_ARCH_S390 || \ + V8_TARGET_ARCH_RISCV64 || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_MIPS || \ +- V8_TARGET_ARCH_LOONG64 ++ V8_TARGET_ARCH_LOONG64 || V8_TARGET_ARCH_SW64 + return RegisterArray( + kContextRegister, kJSFunctionRegister, kJavaScriptCallArgCountRegister, + kJavaScriptCallExtraArg1Register, kJavaScriptCallNewTargetRegister, +@@ -349,7 +351,7 @@ constexpr auto BaselineLeaveFrameDescriptor::registers() { + #if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X64 || V8_TARGET_ARCH_ARM64 || \ + V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_PPC64 || \ + V8_TARGET_ARCH_S390 || V8_TARGET_ARCH_RISCV64 || V8_TARGET_ARCH_MIPS64 || \ +- V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_LOONG64 ++ V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_LOONG64 || V8_TARGET_ARCH_SW64 + return RegisterArray(ParamsSizeRegister(), WeightRegister()); + #else + return DefaultRegisterArray(); +diff --git a/deps/v8/src/codegen/macro-assembler.h b/deps/v8/src/codegen/macro-assembler.h +index 8f1668b0..5d6909b6 100644 +--- a/deps/v8/src/codegen/macro-assembler.h ++++ b/deps/v8/src/codegen/macro-assembler.h +@@ -60,6 +60,9 @@ enum class SmiCheck { kOmit, kInline }; + #elif V8_TARGET_ARCH_LOONG64 + #include "src/codegen/loong64/constants-loong64.h" + #include "src/codegen/loong64/macro-assembler-loong64.h" ++#elif V8_TARGET_ARCH_SW64 ++#include "src/codegen/sw64/constants-sw64.h" ++#include "src/codegen/sw64/macro-assembler-sw64.h" + #elif V8_TARGET_ARCH_S390 + #include "src/codegen/s390/constants-s390.h" + #include "src/codegen/s390/macro-assembler-s390.h" +diff --git a/deps/v8/src/codegen/register-arch.h b/deps/v8/src/codegen/register-arch.h +index c9c3a984..0d3a4e36 100644 +--- a/deps/v8/src/codegen/register-arch.h ++++ b/deps/v8/src/codegen/register-arch.h +@@ -23,6 +23,8 @@ + #include "src/codegen/mips64/register-mips64.h" + #elif V8_TARGET_ARCH_LOONG64 + #include "src/codegen/loong64/register-loong64.h" ++#elif V8_TARGET_ARCH_SW64 ++#include "src/codegen/sw64/register-sw64.h" + #elif V8_TARGET_ARCH_S390 + #include "src/codegen/s390/register-s390.h" + #elif V8_TARGET_ARCH_RISCV64 +diff --git a/deps/v8/src/codegen/register-configuration.cc b/deps/v8/src/codegen/register-configuration.cc +index 91f06aec..78d3eb71 100644 +--- a/deps/v8/src/codegen/register-configuration.cc ++++ b/deps/v8/src/codegen/register-configuration.cc +@@ -83,6 +83,8 @@ static int get_num_allocatable_double_registers() { + kMaxAllocatableDoubleRegisterCount; + #elif V8_TARGET_ARCH_LOONG64 + kMaxAllocatableDoubleRegisterCount; ++#elif V8_TARGET_ARCH_SW64 ++ kMaxAllocatableDoubleRegisterCount; + #elif V8_TARGET_ARCH_PPC + kMaxAllocatableDoubleRegisterCount; + #elif V8_TARGET_ARCH_PPC64 +diff --git a/deps/v8/src/codegen/reglist.h b/deps/v8/src/codegen/reglist.h +index eb9ff451..713492d3 100644 +--- a/deps/v8/src/codegen/reglist.h ++++ b/deps/v8/src/codegen/reglist.h +@@ -19,6 +19,8 @@ + #include "src/codegen/mips/reglist-mips.h" + #elif V8_TARGET_ARCH_MIPS64 + #include "src/codegen/mips64/reglist-mips64.h" ++#elif V8_TARGET_ARCH_SW64 ++#include "src/codegen/sw64/reglist-sw64.h" + #elif V8_TARGET_ARCH_LOONG64 + #include "src/codegen/loong64/reglist-loong64.h" + #elif V8_TARGET_ARCH_S390 +diff --git a/deps/v8/src/codegen/reloc-info.cc b/deps/v8/src/codegen/reloc-info.cc +index d110e387..ce43e7d3 100644 +--- a/deps/v8/src/codegen/reloc-info.cc ++++ b/deps/v8/src/codegen/reloc-info.cc +@@ -320,7 +320,7 @@ bool RelocInfo::OffHeapTargetIsCodedSpecially() { + #elif defined(V8_TARGET_ARCH_IA32) || defined(V8_TARGET_ARCH_MIPS) || \ + defined(V8_TARGET_ARCH_MIPS64) || defined(V8_TARGET_ARCH_PPC) || \ + defined(V8_TARGET_ARCH_PPC64) || defined(V8_TARGET_ARCH_S390) || \ +- defined(V8_TARGET_ARCH_RISCV64) || defined(V8_TARGET_ARCH_LOONG64) ++ defined(V8_TARGET_ARCH_RISCV64) || defined(V8_TARGET_ARCH_LOONG64) || defined(V8_TARGET_ARCH_SW64) + return true; + #endif + } +diff --git a/deps/v8/src/codegen/sw64/assembler-sw64-inl.h b/deps/v8/src/codegen/sw64/assembler-sw64-inl.h +new file mode 100644 +index 00000000..898106f5 +--- /dev/null ++++ b/deps/v8/src/codegen/sw64/assembler-sw64-inl.h +@@ -0,0 +1,291 @@ ++ ++// Copyright (c) 1994-2006 Sun Microsystems Inc. ++// All Rights Reserved. ++// ++// Redistribution and use in source and binary forms, with or without ++// modification, are permitted provided that the following conditions are ++// met: ++// ++// - Redistributions of source code must retain the above copyright notice, ++// this list of conditions and the following disclaimer. ++// ++// - Redistribution in binary form must reproduce the above copyright ++// notice, this list of conditions and the following disclaimer in the ++// documentation and/or other materials provided with the distribution. ++// ++// - Neither the name of Sun Microsystems or the names of contributors may ++// be used to endorse or promote products derived from this software without ++// specific prior written permission. ++// ++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ ++// The original source code covered by the above license above has been ++// modified significantly by Google Inc. ++// Copyright 2012 the V8 project authors. All rights reserved. ++ ++#ifndef V8_CODEGEN_SW64_ASSEMBLER_SW64_INL_H_ ++#define V8_CODEGEN_SW64_ASSEMBLER_SW64_INL_H_ ++ ++#include "src/codegen/assembler.h" ++#include "src/codegen/sw64/assembler-sw64.h" ++#include "src/debug/debug.h" ++#include "src/objects/objects-inl.h" ++ ++namespace v8 { ++namespace internal { ++ ++bool CpuFeatures::SupportsOptimizer() { return IsSupported(FPU); } ++ ++// ----------------------------------------------------------------------------- ++// Operand and MemOperand. ++ ++bool Operand::is_reg() const { return rm_.is_valid(); } ++ ++int64_t Operand::immediate() const { ++ DCHECK(!is_reg()); ++ DCHECK(!IsHeapObjectRequest()); ++ return value_.immediate; ++} ++ ++// ----------------------------------------------------------------------------- ++// RelocInfo. ++ ++void RelocInfo::apply(intptr_t delta) { ++ if (IsInternalReference(rmode_) || IsInternalReferenceEncoded(rmode_)) { ++ // Absolute code pointer inside code object moves with the code object. ++ Assembler::RelocateInternalReference(rmode_, pc_, delta); ++ } ++} ++ ++Address RelocInfo::target_address() { ++ DCHECK(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_) || IsWasmCall(rmode_)); ++ return Assembler::target_address_at(pc_, constant_pool_); ++} ++ ++Address RelocInfo::target_address_address() { ++ DCHECK(HasTargetAddressAddress()); ++ // Read the address of the word containing the target_address in an ++ // instruction stream. ++ // The only architecture-independent user of this function is the serializer. ++ // The serializer uses it to find out how many raw bytes of instruction to ++ // output before the next target. ++ // For an instruction like LUI/ORI where the target bits are mixed into the ++ // instruction bits, the size of the target will be zero, indicating that the ++ // serializer should not step forward in memory after a target is resolved ++ // and written. In this case the target_address_address function should ++ // return the end of the instructions to be patched, allowing the ++ // deserializer to deserialize the instructions as raw bytes and put them in ++ // place, ready to be patched with the target. After jump optimization, ++ // that is the address of the instruction that follows J/JAL/JR/JALR ++ // instruction. ++ return pc_ + Assembler::kInstructionsFor64BitConstant * kInstrSize; ++} ++ ++Address RelocInfo::constant_pool_entry_address() { UNREACHABLE(); } ++ ++int RelocInfo::target_address_size() { return Assembler::kSpecialTargetSize; } ++ ++void Assembler::deserialization_set_special_target_at( ++ Address instruction_payload, Code code, Address target) { ++ set_target_address_at(instruction_payload, ++ !code.is_null() ? code.constant_pool() : kNullAddress, ++ target); ++} ++ ++int Assembler::deserialization_special_target_size( ++ Address instruction_payload) { ++ return kSpecialTargetSize; ++} ++ ++void Assembler::set_target_internal_reference_encoded_at(Address pc, ++ Address target) { ++ // Encoded internal references are j/jal instructions. ++ Instr instr = Assembler::instr_at(pc + 0 * kInstrSize); ++ ++ uint64_t imm28 = target & static_cast(kImm28Mask); ++ ++ instr &= ~kImm26Mask; ++ uint64_t imm26 = imm28 >> 2; ++ DCHECK(is_uint26(imm26)); ++ ++ instr_at_put(pc, instr | (imm26 & kImm26Mask)); ++ // Currently used only by deserializer, and all code will be flushed ++ // after complete deserialization, no need to flush on each reference. ++} ++ ++void Assembler::deserialization_set_target_internal_reference_at( ++ Address pc, Address target, RelocInfo::Mode mode) { ++ if (mode == RelocInfo::INTERNAL_REFERENCE_ENCODED) { ++ UNREACHABLE(); // DCHECK(IsJ(instr_at(pc))); ++ set_target_internal_reference_encoded_at(pc, target); ++ } else { ++ DCHECK(mode == RelocInfo::INTERNAL_REFERENCE); ++ Memory
(pc) = target; ++ } ++} ++ ++HeapObject RelocInfo::target_object(PtrComprCageBase cage_base) { ++ DCHECK(IsCodeTarget(rmode_) || IsFullEmbeddedObject(rmode_) || ++ IsDataEmbeddedObject(rmode_)); ++ if (IsDataEmbeddedObject(rmode_)) { ++ return HeapObject::cast(Object(ReadUnalignedValue
(pc_))); ++ } ++ return HeapObject::cast( ++ Object(Assembler::target_address_at(pc_, constant_pool_))); ++} ++ ++Handle RelocInfo::target_object_handle(Assembler* origin) { ++ if (IsDataEmbeddedObject(rmode_)) { ++ return Handle::cast(ReadUnalignedValue>(pc_)); ++ } else { ++ DCHECK(IsCodeTarget(rmode_) || IsFullEmbeddedObject(rmode_)); ++ return Handle(reinterpret_cast( ++ Assembler::target_address_at(pc_, constant_pool_))); ++} ++} ++ ++void RelocInfo::set_target_object(Heap* heap, HeapObject target, ++ WriteBarrierMode write_barrier_mode, ++ ICacheFlushMode icache_flush_mode) { ++ DCHECK(IsCodeTarget(rmode_) || IsFullEmbeddedObject(rmode_) || ++ IsDataEmbeddedObject(rmode_)); ++ if (IsDataEmbeddedObject(rmode_)) { ++ WriteUnalignedValue(pc_, target.ptr()); ++ // No need to flush icache since no instructions were changed. ++ } else { ++ Assembler::set_target_address_at(pc_, constant_pool_, target.ptr(), ++ icache_flush_mode); ++ } ++ if (write_barrier_mode == UPDATE_WRITE_BARRIER && !host().is_null() && ++ !FLAG_disable_write_barriers) { ++ WriteBarrierForCode(host(), this, target); ++ } ++} ++ ++Address RelocInfo::target_external_reference() { ++ DCHECK(rmode_ == EXTERNAL_REFERENCE); ++ return Assembler::target_address_at(pc_, constant_pool_); ++} ++ ++void RelocInfo::set_target_external_reference( ++ Address target, ICacheFlushMode icache_flush_mode) { ++ DCHECK(rmode_ == RelocInfo::EXTERNAL_REFERENCE); ++ Assembler::set_target_address_at(pc_, constant_pool_, target, ++ icache_flush_mode); ++} ++ ++Address RelocInfo::target_internal_reference() { ++ if (rmode_ == INTERNAL_REFERENCE) { ++ return Memory
(pc_); ++ } else { ++ // Encoded internal references are j/jal instructions. ++ DCHECK(rmode_ == INTERNAL_REFERENCE_ENCODED); ++ Instr instr = Assembler::instr_at(pc_ + 0 * kInstrSize); ++ instr &= kImm26Mask; ++ uint64_t imm28 = instr << 2; ++ uint64_t segment = pc_ & ~static_cast(kImm28Mask); ++ return static_cast
(segment | imm28); ++ } ++} ++ ++Address RelocInfo::target_internal_reference_address() { ++ DCHECK(rmode_ == INTERNAL_REFERENCE || rmode_ == INTERNAL_REFERENCE_ENCODED); ++ return pc_; ++} ++ ++Address RelocInfo::target_runtime_entry(Assembler* origin) { ++ DCHECK(IsRuntimeEntry(rmode_)); ++ return target_address(); ++} ++ ++void RelocInfo::set_target_runtime_entry(Address target, ++ WriteBarrierMode write_barrier_mode, ++ ICacheFlushMode icache_flush_mode) { ++ DCHECK(IsRuntimeEntry(rmode_)); ++ if (target_address() != target) ++ set_target_address(target, write_barrier_mode, icache_flush_mode); ++} ++ ++Address RelocInfo::target_off_heap_target() { ++ DCHECK(IsOffHeapTarget(rmode_)); ++ return Assembler::target_address_at(pc_, constant_pool_); ++} ++ ++void RelocInfo::WipeOut() { ++ DCHECK(IsFullEmbeddedObject(rmode_) || IsCodeTarget(rmode_) || ++ IsRuntimeEntry(rmode_) || IsExternalReference(rmode_) || ++ IsInternalReference(rmode_) || IsInternalReferenceEncoded(rmode_) || ++ IsOffHeapTarget(rmode_)); ++ if (IsInternalReference(rmode_)) { ++ Memory
(pc_) = kNullAddress; ++ } else if (IsInternalReferenceEncoded(rmode_)) { ++ Assembler::set_target_internal_reference_encoded_at(pc_, kNullAddress); ++ } else { ++ Assembler::set_target_address_at(pc_, constant_pool_, kNullAddress); ++ } ++} ++ ++// ----------------------------------------------------------------------------- ++// Assembler. ++ ++void Assembler::CheckBuffer() { ++ if (buffer_space() <= kGap) { ++ GrowBuffer(); ++ } ++} ++ ++void Assembler::CheckForEmitInForbiddenSlot() { ++ if (!is_buffer_growth_blocked()) { ++ CheckBuffer(); ++ } ++} ++ ++template ++void Assembler::EmitHelper(T x) { ++ *reinterpret_cast(pc_) = x; ++ pc_ += sizeof(x); ++ CheckTrampolinePoolQuick(); ++} ++ ++void Assembler::EmitHelper(Instr x) { ++ *reinterpret_cast(pc_) = x; ++ pc_ += kInstrSize; ++ CheckTrampolinePoolQuick(); ++} ++ ++void Assembler::EmitHelper(uint8_t x) { ++ *reinterpret_cast(pc_) = x; ++ pc_ += sizeof(x); ++ if (reinterpret_cast(pc_) % kInstrSize == 0) { ++ CheckTrampolinePoolQuick(); ++ } ++} ++ ++void Assembler::emit(Instr x) { ++ if (!is_buffer_growth_blocked()) { ++ CheckBuffer(); ++ } ++ EmitHelper(x); ++} ++ ++void Assembler::emit(uint64_t data) { ++ CheckForEmitInForbiddenSlot(); ++ EmitHelper(data); ++} ++ ++EnsureSpace::EnsureSpace(Assembler* assembler) { assembler->CheckBuffer(); } ++ ++} // namespace internal ++} // namespace v8 ++ ++#endif // V8_SW64_ASSEMBLER_SW64_INL_H_ +diff --git a/deps/v8/src/codegen/sw64/assembler-sw64.cc b/deps/v8/src/codegen/sw64/assembler-sw64.cc +new file mode 100644 +index 00000000..0ff56205 +--- /dev/null ++++ b/deps/v8/src/codegen/sw64/assembler-sw64.cc +@@ -0,0 +1,3881 @@ ++// Copyright (c) 1994-2006 Sun Microsystems Inc. ++// All Rights Reserved. ++// ++// Redistribution and use in source and binary forms, with or without ++// modification, are permitted provided that the following conditions are ++// met: ++// ++// - Redistributions of source code must retain the above copyright notice, ++// this list of conditions and the following disclaimer. ++// ++// - Redistribution in binary form must reproduce the above copyright ++// notice, this list of conditions and the following disclaimer in the ++// documentation and/or other materials provided with the distribution. ++// ++// - Neither the name of Sun Microsystems or the names of contributors may ++// be used to endorse or promote products derived from this software without ++// specific prior written permission. ++// ++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ ++// The original source code covered by the above license above has been ++// modified significantly by Google Inc. ++// Copyright 2012 the V8 project authors. All rights reserved. ++ ++#include "src/codegen/sw64/assembler-sw64.h" ++ ++#if V8_TARGET_ARCH_SW64 ++ ++#include "src/base/cpu.h" ++#include "src/codegen/machine-type.h" ++#include "src/codegen/safepoint-table.h" ++#include "src/codegen/string-constants.h" ++#include "src/codegen/sw64/assembler-sw64-inl.h" ++#include "src/deoptimizer/deoptimizer.h" ++#include "src/objects/heap-number-inl.h" ++ ++namespace v8 { ++namespace internal { ++ ++// Get the CPU features enabled by the build. For cross compilation the ++// preprocessor symbols CAN_USE_FPU_INSTRUCTIONS ++// can be defined to enable FPU instructions when building the ++// snapshot. ++static unsigned CpuFeaturesImpliedByCompiler() { ++ unsigned answer = 0; ++ ++ // If the compiler is allowed to use FPU then we can use FPU too in our code ++ // generation even when generating snapshots. This won't work for cross ++ // compilation. ++ answer |= 1u << FPU; ++ ++ return answer; ++} ++ ++bool CpuFeatures::SupportsWasmSimd128() { return false; } ++ ++void CpuFeatures::ProbeImpl(bool cross_compile) { ++ supported_ |= CpuFeaturesImpliedByCompiler(); ++ ++ // Only use statically determined features for cross compile (snapshot). ++ if (cross_compile) return; ++ ++ // Probe for additional features at runtime. ++ base::CPU cpu; ++ if (cpu.has_fpu()) supported_ |= 1u << FPU; ++ if (cpu.has_swsa()) supported_ |= 1u << SW64_SIMD; ++ if (strcmp(FLAG_sw64_arch, "native") == 0) { ++ if (cpu.use_sw8a()) { ++ supported_ |= 1u << SW_8A; ++ FLAG_use_sw8a = true; ++ if (FLAG_use_sw8a) { ++ FLAG_use_cmovdw = true; ++ FLAG_use_cmovdwu = true; ++ FLAG_use_cmovdl = true; ++ FLAG_use_cmovdlu = true; ++ FLAG_use_cmovls = true; ++ FLAG_use_cmovuls = true; ++ FLAG_use_cmovld = true; ++ FLAG_use_cmovuld = true; ++ FLAG_use_cmovws = true; ++ FLAG_use_cmovuws = true; ++ FLAG_use_cmovwd = true; ++ FLAG_use_cmovuwd = true; ++ ++ FLAG_use_frecx = true; ++ FLAG_use_frisx = true; ++ FLAG_use_fridx = true; ++ FLAG_use_revbx = true; ++ FLAG_use_sllx = true; ++ FLAG_use_divx = true; ++ ++ FLAG_use_ldx_a = true; ++ FLAG_use_lldx = true; ++ FLAG_use_csax = true; ++ FLAG_use_addpi = true; ++ } ++ } ++ } ++} ++ ++void CpuFeatures::PrintTarget() {} ++void CpuFeatures::PrintFeatures() {} ++ ++int ToNumber(Register reg) { ++ DCHECK(reg.is_valid()); ++ const int kNumbers[] = { ++ // (SW64) ++ 0, // v0 ++ 1, // t0 ++ 2, // t1 ++ 3, // t2 ++ 4, // t3 ++ 5, // t4 ++ 6, // t5 ++ 7, // t6 ++ 8, // t7 ++ 9, // s0 ++ 10, // s1 ++ 11, // s2 ++ 12, // s3 ++ 13, // s4 ++ 14, // s5 ++ 15, // fp ++ 16, // a0 ++ 17, // a1 ++ 18, // a2 ++ 19, // a3 ++ 20, // a4 ++ 21, // a5 ++ 22, // t8 ++ 23, // t9 ++ 24, // t10 ++ 25, // t11 ++ 26, // ra ++ 27, // t12 ++ 28, // at ++ 29, // gp ++ 30, // sp ++ 31 // zero_reg ++ }; ++ return kNumbers[reg.code()]; ++} ++ ++Register ToRegister(int num) { ++ DCHECK(num >= 0 && num < kNumRegisters); ++ const Register kRegisters[] = { ++ v0, t0, t1, t2, t3, t4, t5, t6, t7, s0, s1, s2, s3, s4, s5, fp, ++ a0, a1, a2, a3, a4, a5, t8, t9, t10, t11, ra, t12, at, gp, sp, zero_reg}; ++ return kRegisters[num]; ++} ++ ++// ----------------------------------------------------------------------------- ++// Implementation of RelocInfo. ++ ++const int RelocInfo::kApplyMask = ++ RelocInfo::ModeMask(RelocInfo::INTERNAL_REFERENCE) | ++ RelocInfo::ModeMask(RelocInfo::INTERNAL_REFERENCE_ENCODED); ++ ++bool RelocInfo::IsCodedSpecially() { ++ // The deserializer needs to know whether a pointer is specially coded. Being ++ // specially coded on SW64 means that it is a ldih/ldi instruction, and that ++ // is always the case inside code objects. ++ return true; ++} ++ ++bool RelocInfo::IsInConstantPool() { return false; } ++ ++uint32_t RelocInfo::wasm_call_tag() const { ++ DCHECK(rmode_ == WASM_CALL || rmode_ == WASM_STUB_CALL); ++ return static_cast( ++ Assembler::target_address_at(pc_, constant_pool_)); ++} ++ ++// ----------------------------------------------------------------------------- ++// Implementation of Operand and MemOperand. ++// See assembler-sw64-inl.h for inlined constructors. ++ ++Operand::Operand(Handle handle) ++ : rm_(no_reg), rmode_(RelocInfo::FULL_EMBEDDED_OBJECT) { ++ value_.immediate = static_cast(handle.address()); ++} ++ ++Operand Operand::EmbeddedNumber(double value) { ++ int32_t smi; ++ if (DoubleToSmiInteger(value, &smi)) return Operand(Smi::FromInt(smi)); ++ Operand result(0, RelocInfo::FULL_EMBEDDED_OBJECT); ++ result.is_heap_object_request_ = true; ++ result.value_.heap_object_request = HeapObjectRequest(value); ++ return result; ++} ++ ++Operand Operand::EmbeddedStringConstant(const StringConstantBase* str) { ++ Operand result(0, RelocInfo::FULL_EMBEDDED_OBJECT); ++ result.is_heap_object_request_ = true; ++ result.value_.heap_object_request = HeapObjectRequest(str); ++ return result; ++} ++ ++MemOperand::MemOperand(Register rm, int32_t offset) : Operand(rm) { ++ offset_ = offset; ++} ++ ++MemOperand::MemOperand(Register rm, int32_t unit, int32_t multiplier, ++ OffsetAddend offset_addend) ++ : Operand(rm) { ++ offset_ = unit * multiplier + offset_addend; ++} ++ ++void Assembler::AllocateAndInstallRequestedHeapObjects(Isolate* isolate) { ++ DCHECK_IMPLIES(isolate == nullptr, heap_object_requests_.empty()); ++ for (auto& request : heap_object_requests_) { ++ Handle object; ++ switch (request.kind()) { ++ case HeapObjectRequest::kHeapNumber: ++ object = isolate->factory()->NewHeapNumber( ++ request.heap_number()); ++ break; ++ case HeapObjectRequest::kStringConstant: ++ const StringConstantBase* str = request.string(); ++ CHECK_NOT_NULL(str); ++ object = str->AllocateStringConstant(isolate); ++ break; ++ } ++ Address pc = reinterpret_cast
(buffer_start_) + request.offset(); ++ set_target_value_at(pc, reinterpret_cast(object.location())); ++ } ++} ++ ++Assembler::Assembler(const AssemblerOptions& options, ++ std::unique_ptr buffer) ++ : AssemblerBase(options, std::move(buffer)), scratch_register_list_({at}) { ++ if (CpuFeatures::IsSupported(SW64_SIMD)) { ++ EnableCpuFeature(SW64_SIMD); ++ } ++ reloc_info_writer.Reposition(buffer_start_ + buffer_->size(), pc_); ++ ++ last_trampoline_pool_end_ = 0; ++ no_trampoline_pool_before_ = 0; ++ trampoline_pool_blocked_nesting_ = 0; ++ // We leave space (16 * kTrampolineSlotsSize) ++ // for BlockTrampolinePoolScope buffer. ++ next_buffer_check_ = FLAG_force_long_branches ++ ? kMaxInt ++ : kMaxBranchOffset - kTrampolineSlotsSize * 16; ++ internal_trampoline_exception_ = false; ++ last_bound_pos_ = 0; ++ ++ trampoline_emitted_ = FLAG_force_long_branches; ++ unbound_labels_count_ = 0; ++ block_buffer_growth_ = false; ++} ++ ++void Assembler::GetCode(Isolate* isolate, CodeDesc* desc) { ++ GetCode(isolate, desc, kNoSafepointTable, kNoHandlerTable); ++} ++ ++void Assembler::GetCode(Isolate* isolate, CodeDesc* desc, ++ SafepointTableBuilder* safepoint_table_builder, ++ int handler_table_offset) { ++ // As a crutch to avoid having to add manual Align calls wherever we use a ++ // raw workflow to create InstructionStream objects (mostly in tests), add ++ // another Align call here. It does no harm - the end of the InstructionStream ++ // object is aligned to the (larger) kCodeAlignment anyways. ++ // TODO(jgruber): Consider moving responsibility for proper alignment to ++ // metadata table builders (safepoint, handler, constant pool, code ++ // comments). ++ DataAlign(Code::kMetadataAlignment); ++ ++ int code_comments_size = WriteCodeComments(); ++ ++ DCHECK(pc_ <= reloc_info_writer.pos()); // No overlap. ++ ++ AllocateAndInstallRequestedHeapObjects(isolate); ++ ++ // Set up code descriptor. ++ // TODO(jgruber): Reconsider how these offsets and sizes are maintained up to ++ // this point to make CodeDesc initialization less fiddly. ++ ++ static constexpr int kConstantPoolSize = 0; ++ const int instruction_size = pc_offset(); ++ const int code_comments_offset = instruction_size - code_comments_size; ++ const int constant_pool_offset = code_comments_offset - kConstantPoolSize; ++ const int handler_table_offset2 = (handler_table_offset == kNoHandlerTable) ++ ? constant_pool_offset ++ : handler_table_offset; ++ const int safepoint_table_offset = ++ (safepoint_table_builder == kNoSafepointTable) ++ ? handler_table_offset2 ++ : safepoint_table_builder->safepoint_table_offset(); ++ const int reloc_info_offset = ++ static_cast(reloc_info_writer.pos() - buffer_->start()); ++ CodeDesc::Initialize(desc, this, safepoint_table_offset, ++ handler_table_offset2, constant_pool_offset, ++ code_comments_offset, reloc_info_offset); ++} ++ ++void Assembler::Align(int m) { ++ DCHECK(m >= 4 && base::bits::IsPowerOfTwo(m)); ++ while ((pc_offset() & (m - 1)) != 0) { ++ nop(); ++ } ++} ++ ++void Assembler::CodeTargetAlign() { ++ // No advantage to aligning branch/call targets to more than ++ // single instruction, that I am aware of. ++ Align(4); ++} ++ ++uint32_t Assembler::GetSwRa(Instr instr) { ++ return (instr & sRaFieldMask) >> sRaShift; ++} ++ ++uint32_t Assembler::GetSwRb(Instr instr) { ++ return (instr & sRbFieldMask) >> sRbShift; ++} ++ ++uint32_t Assembler::GetSwRc(Instr instr) { ++ return (instr & sRcFieldMask) >> sRcShift; ++} ++ ++uint32_t Assembler::GetLabelConst(Instr instr) { return instr & ~kImm16Mask; } ++ ++#define OP(x) (((x)&0x3F) << 26) ++#define OPR(oo, ff) (OP(oo) | (((ff)&0xFF) << 5)) ++ ++int32_t Assembler::GetSwOpcodeField(Instr instr) { return instr & OP(-1); } ++ ++int32_t Assembler::GetSwOpcodeAndFunctionField(Instr instr) { ++ return instr & OPR(-1, -1); ++} ++ ++#undef OP ++#undef OPR ++ ++uint32_t Assembler::GetSwImmediate8(Instr instr) { ++ return (instr & sImm8Mask) >> sImm8Shift; ++} ++ ++uint32_t Assembler::GetSwImmediate16(Instr instr) { ++ return (instr & sImm16Mask) >> sImm16Shift; ++} ++ ++// Labels refer to positions in the (to be) generated code. ++// There are bound, linked, and unused labels. ++// ++// Bound labels refer to known positions in the already ++// generated code. pos() is the position the label refers to. ++// ++// Linked labels refer to unknown positions in the code ++// to be generated; pos() is the position of the last ++// instruction using the label. ++ ++// The link chain is terminated by a value in the instruction of -1, ++// which is an otherwise illegal value (branch -1 is inf loop). ++// The instruction 16-bit offset field addresses 32-bit words, but in ++// code is conv to an 18-bit value addressing bytes, hence the -4 value. ++ ++const int kEndOfChain = -4; ++// Determines the end of the Jump chain (a subset of the label link chain). ++const int kEndOfJumpChain = 0; ++ ++bool Assembler::IsLdih(Instr instr) { ++ int32_t opcode = GetSwOpcodeField(instr); ++ ++ return opcode == op_ldih; ++} ++ ++bool Assembler::IsLdi(Instr instr) { ++ int32_t opcode = GetSwOpcodeField(instr); ++ ++ return opcode == op_ldi; ++} ++ ++bool Assembler::IsBranch(Instr instr) { ++ int32_t opcode = GetSwOpcodeField(instr); ++ return opcode == op_lbr || opcode == op_br || ++ opcode == op_bsr || // unconditional branch ++ opcode == op_beq || opcode == op_bne || opcode == op_blt || ++ opcode == op_ble || opcode == op_bgt || opcode == op_bge || ++ opcode == op_blbc || opcode == op_blbs || opcode == op_fbeq || ++ opcode == op_fbne || opcode == op_fblt || opcode == op_fble || ++ opcode == op_fbgt || opcode == op_fbge; ++} ++ ++bool Assembler::IsEmittedConstant(Instr instr) { ++ uint32_t label_constant = GetLabelConst(instr); ++ return label_constant == 0; // Emitted label const in reg-exp engine. ++} ++ ++bool Assembler::IsBeq(Instr instr) { return GetSwOpcodeField(instr) == op_beq; } ++ ++bool Assembler::IsBne(Instr instr) { return GetSwOpcodeField(instr) == op_bne; } ++ ++bool Assembler::IsAddImmediate(Instr instr) { ++ int32_t opcode = GetSwOpcodeAndFunctionField(instr); ++ return opcode == op_addw_l || opcode == op_addl_l; ++} ++ ++bool Assembler::IsAndImmediate(Instr instr) { ++ return GetSwOpcodeAndFunctionField(instr) == op_and_l; ++} ++ ++int Assembler::target_at(int pos, bool is_internal) { ++ if (is_internal) { ++ int64_t* p = reinterpret_cast(buffer_start_ + pos); ++ int64_t address = *p; ++ if (address == kEndOfJumpChain) { ++ return kEndOfChain; ++ } else { ++ int64_t instr_address = reinterpret_cast(p); ++ DCHECK(instr_address - address < INT_MAX); ++ int delta = static_cast(instr_address - address); ++ DCHECK(pos > delta); ++ return pos - delta; ++ } ++ } ++ Instr instr = instr_at(pos); ++ if ((instr & ~sImm21Mask) == 0) { ++ // Emitted label constant, not part of a branch. ++ if (instr == 0) { ++ return kEndOfChain; ++ } else { ++ int32_t imm23 = ((instr & static_cast(sImm21Mask)) << 11) >> 9; ++ return (imm23 + pos); ++ } ++ } ++ // Check we have a branch or jump instruction. ++ DCHECK(IsBranch(instr) || IsLdi(instr)); ++ // Do NOT change this to <<2. We rely on arithmetic shifts here, assuming ++ // the compiler uses arithmectic shifts for signed integers. ++ if (IsBranch(instr)) { ++ int32_t imm23 = ((instr & static_cast(sImm21Mask)) << 11) >> 9; ++ ++ if (imm23 == kEndOfChain) { ++ // EndOfChain sentinel is returned directly, not relative to pc or pos. ++ return kEndOfChain; ++ } else { ++ return pos + kInstrSize + imm23; ++ } ++ } else if (IsLdi(instr)) { ++ Instr instr0_ldi = instr_at(pos + 0 * kInstrSize); ++ Instr instr2_ldih = instr_at(pos + 2 * kInstrSize); ++ Instr instr3_ldi = instr_at(pos + 3 * kInstrSize); ++ DCHECK(IsLdi(instr0_ldi)); ++ DCHECK(IsLdih(instr2_ldih)); ++ DCHECK(IsLdi(instr3_ldi)); ++ ++ // TODO(plind) create named constants for shift values. ++ int64_t imm = static_cast(instr0_ldi << 16) << 16; ++ imm += static_cast(instr2_ldih << 16); ++ imm += static_cast(instr3_ldi << 16) >> 16; ++ ++ if (imm == kEndOfJumpChain) { ++ // EndOfChain sentinel is returned directly, not relative to pc or pos. ++ return kEndOfChain; ++ } else { ++ uint64_t instr_address = reinterpret_cast(buffer_start_ + pos); ++ int64_t delta = instr_address - imm; ++ DCHECK(pos > delta); ++ return (int)(pos - delta); ++ } ++ } else { ++ UNIMPLEMENTED_SW64(); ++ return -1; ++ } ++} ++ ++void Assembler::target_at_put(int pos, int target_pos, bool is_internal) { ++ if (is_internal) { ++ uint64_t imm = reinterpret_cast(buffer_start_) + target_pos; ++ *reinterpret_cast(buffer_start_ + pos) = imm; ++ return; ++ } ++ Instr instr = instr_at(pos); ++ if ((instr & ~sImm21Mask) == 0) { ++ DCHECK(target_pos == kEndOfChain || target_pos >= 0); ++ // Emitted label constant, not part of a branch. ++ // Make label relative to Code pointer of generated Code object. ++ instr_at_put(pos, target_pos + (Code::kHeaderSize - kHeapObjectTag)); ++ return; ++ } ++ ++ DCHECK(IsBranch(instr) || IsLdi(instr)); ++ if (IsBranch(instr)) { ++ int32_t imm23 = target_pos - (pos + kInstrSize); ++ DCHECK((imm23 & 3) == 0); ++ ++ int32_t imm21 = imm23 >> 2; ++ instr &= ~sImm21Mask; ++ DCHECK(is_int21(imm21)); ++ ++ instr_at_put(pos, instr | (imm21 & sImm21Mask)); ++ } else if (IsLdi(instr)) { ++ Instr instr0_ldi = instr_at(pos + 0 * kInstrSize); ++ Instr instr2_ldih = instr_at(pos + 2 * kInstrSize); ++ Instr instr3_ldi = instr_at(pos + 3 * kInstrSize); ++ DCHECK(IsLdi(instr0_ldi)); ++ DCHECK(IsLdih(instr2_ldih)); ++ DCHECK(IsLdi(instr3_ldi)); ++ ++ int64_t imm = reinterpret_cast(buffer_start_) + target_pos; ++ DCHECK((imm & 3) == 0); ++ ++ instr0_ldi &= ~kImm16Mask; ++ instr2_ldih &= ~kImm16Mask; ++ instr3_ldi &= ~kImm16Mask; ++ ++ int32_t lsb32 = (int32_t)(imm); ++ int32_t msb32 = (int32_t)((imm - lsb32) >> 32); ++ instr_at_put(pos + 0 * kInstrSize, ++ instr0_ldi | ((int16_t)(msb32 & 0xffff) & 0xffff)); ++ instr_at_put(pos + 2 * kInstrSize, ++ instr2_ldih | (((lsb32 - (int16_t)lsb32) >> 16) & 0xffff)); ++ instr_at_put(pos + 3 * kInstrSize, ++ instr3_ldi | ((int16_t)(lsb32 & 0xffff) & 0xffff)); ++ } else { ++ UNIMPLEMENTED_SW64(); ++ } ++} ++ ++void Assembler::print(const Label* L) { ++ if (L->is_unused()) { ++ PrintF("unused label\n"); ++ } else if (L->is_bound()) { ++ PrintF("bound label to %d\n", L->pos()); ++ } else if (L->is_linked()) { ++ Label l; ++ l.link_to(L->pos()); ++ PrintF("unbound label"); ++ while (l.is_linked()) { ++ PrintF("@ %d ", l.pos()); ++ Instr instr = instr_at(l.pos()); ++ if ((instr & ~kImm16Mask) == 0) { ++ PrintF("value\n"); ++ } else { ++ PrintF("%d\n", instr); ++ } ++ next(&l, is_internal_reference(&l)); ++ } ++ } else { ++ PrintF("label in inconsistent state (pos = %d)\n", L->pos_); ++ } ++} ++ ++void Assembler::bind_to(Label* L, int pos) { ++ DCHECK(0 <= pos && pos <= pc_offset()); // Must have valid binding position. ++ int trampoline_pos = kInvalidSlotPos; ++ bool is_internal = false; ++ if (L->is_linked() && !trampoline_emitted_) { ++ unbound_labels_count_--; ++ if (!is_internal_reference(L)) { ++ next_buffer_check_ += kTrampolineSlotsSize; ++ } ++ } ++ ++ while (L->is_linked()) { ++ int fixup_pos = L->pos(); ++ int dist = pos - fixup_pos; ++ is_internal = is_internal_reference(L); ++ next(L, is_internal); // Call next before overwriting link with target at ++ // fixup_pos. ++ Instr instr = instr_at(fixup_pos); ++ if (is_internal) { ++ target_at_put(fixup_pos, pos, is_internal); ++ } else { ++ if (IsBranch(instr)) { ++ int branch_offset = BranchOffset(instr); ++ if (dist > branch_offset) { ++ if (trampoline_pos == kInvalidSlotPos) { ++ trampoline_pos = get_trampoline_entry(fixup_pos); ++ CHECK_NE(trampoline_pos, kInvalidSlotPos); ++ } ++ CHECK((trampoline_pos - fixup_pos) <= branch_offset); ++ target_at_put(fixup_pos, trampoline_pos, false); ++ fixup_pos = trampoline_pos; ++ } ++ target_at_put(fixup_pos, pos, false); ++ } else { ++ DCHECK(IsLdi(instr) || IsEmittedConstant(instr)); ++ target_at_put(fixup_pos, pos, false); ++ } ++ } ++ } ++ L->bind_to(pos); ++ ++ // Keep track of the last bound label so we don't eliminate any instructions ++ // before a bound label. ++ if (pos > last_bound_pos_) last_bound_pos_ = pos; ++} ++ ++void Assembler::bind(Label* L) { ++ DCHECK(!L->is_bound()); // Label can only be bound once. ++ bind_to(L, pc_offset()); ++} ++ ++void Assembler::next(Label* L, bool is_internal) { ++ DCHECK(L->is_linked()); ++ int link = target_at(L->pos(), is_internal); ++ if (link == kEndOfChain) { ++ L->Unuse(); ++ } else { ++ DCHECK_GE(link, 0); ++ L->link_to(link); ++ } ++} ++ ++bool Assembler::is_near(Label* L) { ++ DCHECK(L->is_bound()); ++ return pc_offset() - L->pos() < kMaxBranchOffset - 4 * kInstrSize; ++} ++ ++bool Assembler::is_near(Label* L, OffsetSize bits) { ++ if (L == nullptr || !L->is_bound()) return true; ++ return ((pc_offset() - L->pos()) < ++ (1 << (bits + 2 - 1)) - 1 - 5 * kInstrSize); ++} ++ ++int Assembler::BranchOffset(Instr instr) { ++ int bits = OffsetSize::kOffset21; ++ uint32_t opcode = (instr >> 26) << 26; ++ if (opcode == op_lbr) bits = OffsetSize::kOffset26; ++ return (1 << (bits + 2 - 1)) - 1; ++} ++ ++// We have to use a temporary register for things that can be relocated even ++// if they can be encoded in the SW64's 16 bits of immediate-offset instruction ++// space. There is no guarantee that the relocated location can be similarly ++// encoded. ++bool Assembler::MustUseReg(RelocInfo::Mode rmode) { ++ return !RelocInfo::IsNoInfo(rmode); ++} ++ ++// Returns the next free trampoline entry. ++int32_t Assembler::get_trampoline_entry(int32_t pos) { ++ int32_t trampoline_entry = kInvalidSlotPos; ++ if (!internal_trampoline_exception_) { ++ if (trampoline_.start() > pos) { ++ trampoline_entry = trampoline_.take_slot(); ++ } ++ ++ if (kInvalidSlotPos == trampoline_entry) { ++ internal_trampoline_exception_ = true; ++ } ++ } ++ return trampoline_entry; ++} ++ ++uint64_t Assembler::jump_address(Label* L) { ++ int64_t target_pos; ++ if (L->is_bound()) { ++ target_pos = L->pos(); ++ } else { ++ if (L->is_linked()) { ++ target_pos = L->pos(); // L's link. ++ L->link_to(pc_offset()); ++ } else { ++ L->link_to(pc_offset()); ++ return kEndOfJumpChain; ++ } ++ } ++ uint64_t imm = reinterpret_cast(buffer_start_) + target_pos; ++ DCHECK_EQ(imm & 3, 0); ++ ++ return imm; ++} ++ ++uint64_t Assembler::jump_offset(Label* L) { ++ int64_t target_pos; ++ ++ if (L->is_bound()) { ++ target_pos = L->pos(); ++ } else { ++ if (L->is_linked()) { ++ target_pos = L->pos(); // L's link. ++ L->link_to(pc_offset()); ++ } else { ++ L->link_to(pc_offset()); ++ return kEndOfJumpChain; ++ } ++ } ++ int64_t imm = target_pos - (pc_offset()); ++ DCHECK_EQ(imm & 3, 0); ++ ++ return static_cast(imm); ++} ++ ++uint64_t Assembler::branch_long_offset(Label* L) { ++ int64_t target_pos; ++ ++ if (L->is_bound()) { ++ target_pos = L->pos(); ++ } else { ++ if (L->is_linked()) { ++ target_pos = L->pos(); // L's link. ++ L->link_to(pc_offset()); ++ } else { ++ L->link_to(pc_offset()); ++ return kEndOfJumpChain; ++ } ++ } ++ int64_t offset = target_pos - (pc_offset() + kInstrSize); ++ DCHECK_EQ(offset & 3, 0); ++ ++ return static_cast(offset); ++} ++ ++int32_t Assembler::branch_offset_helper(Label* L, OffsetSize bits) { ++ int32_t target_pos; ++ ++ if (L->is_bound()) { ++ target_pos = L->pos(); ++ } else { ++ if (L->is_linked()) { ++ target_pos = L->pos(); ++ L->link_to(pc_offset()); ++ } else { ++ L->link_to(pc_offset()); ++ if (!trampoline_emitted_) { ++ unbound_labels_count_++; ++ next_buffer_check_ -= kTrampolineSlotsSize; ++ } ++ return kEndOfChain; ++ } ++ } ++ ++ int32_t offset = target_pos - (pc_offset() + kInstrSize); ++ DCHECK(is_intn(offset, bits + 2)); ++ DCHECK_EQ(offset & 3, 0); ++ ++ return offset; ++} ++ ++void Assembler::label_at_put(Label* L, int at_offset) { ++ int target_pos; ++ if (L->is_bound()) { ++ target_pos = L->pos(); ++ instr_at_put(at_offset, target_pos + (Code::kHeaderSize - kHeapObjectTag)); ++ } else { ++ if (L->is_linked()) { ++ target_pos = L->pos(); // L's link. ++ int32_t imm18 = target_pos - at_offset; ++ DCHECK_EQ(imm18 & 3, 0); ++ int32_t imm16 = imm18 >> 2; ++ DCHECK(is_int16(imm16)); ++ instr_at_put(at_offset, (imm16 & kImm16Mask)); ++ } else { ++ target_pos = kEndOfChain; ++ instr_at_put(at_offset, 0); ++ if (!trampoline_emitted_) { ++ unbound_labels_count_++; ++ next_buffer_check_ -= kTrampolineSlotsSize; ++ } ++ } ++ L->link_to(at_offset); ++ } ++} ++ ++//------- Branch and jump instructions -------- ++ ++void Assembler::br(int offset) { br(zero_reg, offset); } ++ ++void Assembler::bsr(int offset) { bsr(ra, offset); } ++ ++// ------------Memory-instructions------------- ++ ++void Assembler::AdjustBaseAndOffset(MemOperand* src, ++ OffsetAccessType access_type, ++ int second_access_add_to_offset) { ++ // This method is used to adjust the base register and offset pair ++ // for a load/store when the offset doesn't fit into int16_t. ++ // It is assumed that 'base + offset' is sufficiently aligned for memory ++ // operands that are machine word in size or smaller. For doubleword-sized ++ // operands it's assumed that 'base' is a multiple of 8, while 'offset' ++ // may be a multiple of 4 (e.g. 4-byte-aligned long and double arguments ++ // and spilled variables on the stack accessed relative to the stack ++ // pointer register). ++ // We preserve the "alignment" of 'offset' by adjusting it by a multiple of 8. ++ ++ bool doubleword_aligned = (src->offset() & (kDoubleSize - 1)) == 0; ++ bool two_accesses = static_cast(access_type) || !doubleword_aligned; ++ DCHECK_LE(second_access_add_to_offset, 7); // Must be <= 7. ++ ++ // is_int16 must be passed a signed value, hence the static cast below. ++ if (is_int16(src->offset()) && ++ (!two_accesses || is_int16(static_cast( ++ src->offset() + second_access_add_to_offset)))) { ++ // Nothing to do: 'offset' (and, if needed, 'offset + 4', or other specified ++ // value) fits into int16_t. ++ return; ++ } ++ ++ DCHECK(src->rm() != ++ at); // Must not overwrite the register 'base' while loading 'offset'. ++ ++#ifdef DEBUG ++ // Remember the "(mis)alignment" of 'offset', it will be checked at the end. ++ uint32_t misalignment = src->offset() & (kDoubleSize - 1); ++#endif ++ ++ // Do not load the whole 32-bit 'offset' if it can be represented as ++ // a sum of two 16-bit signed offsets. This can save an instruction or two. ++ // To simplify matters, only do this for a symmetric range of offsets from ++ // about -64KB to about +64KB, allowing further addition of 4 when accessing ++ // 64-bit variables with two 32-bit accesses. ++ constexpr int32_t kMinOffsetForSimpleAdjustment = ++ 0x7FF8; // Max int16_t that's a multiple of 8. ++ constexpr int32_t kMaxOffsetForSimpleAdjustment = ++ 2 * kMinOffsetForSimpleAdjustment; ++ ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ if (0 <= src->offset() && src->offset() <= kMaxOffsetForSimpleAdjustment) { ++ ldi(scratch, kMinOffsetForSimpleAdjustment, src->rm()); ++ src->offset_ -= kMinOffsetForSimpleAdjustment; ++ } else if (-kMaxOffsetForSimpleAdjustment <= src->offset() && ++ src->offset() < 0) { ++ ldi(scratch, -kMinOffsetForSimpleAdjustment, src->rm()); ++ src->offset_ += kMinOffsetForSimpleAdjustment; ++ } else { ++ // Do not load the whole 32-bit 'offset' if it can be represented as ++ // a sum of three 16-bit signed offsets. This can save an instruction. ++ // To simplify matters, only do this for a symmetric range of offsets from ++ // about -96KB to about +96KB, allowing further addition of 4 when accessing ++ // 64-bit variables with two 32-bit accesses. ++ constexpr int32_t kMinOffsetForMediumAdjustment = ++ 2 * kMinOffsetForSimpleAdjustment; ++ constexpr int32_t kMaxOffsetForMediumAdjustment = ++ 3 * kMinOffsetForSimpleAdjustment; ++ if (0 <= src->offset() && src->offset() <= kMaxOffsetForMediumAdjustment) { ++ ldi(scratch, kMinOffsetForMediumAdjustment / 2, src->rm()); ++ ldi(scratch, kMinOffsetForMediumAdjustment / 2, scratch); ++ src->offset_ -= kMinOffsetForMediumAdjustment; ++ } else if (-kMaxOffsetForMediumAdjustment <= src->offset() && ++ src->offset() < 0) { ++ ldi(scratch, -kMinOffsetForMediumAdjustment / 2, src->rm()); ++ ldi(scratch, -kMinOffsetForMediumAdjustment / 2, scratch); ++ src->offset_ += kMinOffsetForMediumAdjustment; ++ } else { ++ // Now that all shorter options have been exhausted, load the full 32-bit ++ // offset. ++ int32_t loaded_offset = RoundDown(src->offset(), kDoubleSize); ++ int16_t lo_offset = static_cast(loaded_offset); ++ int16_t hi_offset = (loaded_offset - (int16_t)loaded_offset) >> 16; ++ if (((int32_t)hi_offset == -32768) && ((int32_t)lo_offset < 0)) { ++ // range from 0x7FFF8000 to 0x7FFFFFFF ++ ldih(scratch, 0x4000, zero_reg); ++ ldih(scratch, 0x4000, scratch); ++ if (lo_offset != 0) ldi(scratch, lo_offset, scratch); ++ } else { ++ ldih(scratch, hi_offset, zero_reg); ++ if (lo_offset != 0) ldi(scratch, lo_offset, scratch); ++ } ++ addl(scratch, src->rm(), scratch); ++ src->offset_ -= loaded_offset; ++ } ++ } ++ src->rm_ = scratch; ++ ++ DCHECK(is_int16(src->offset())); ++ if (two_accesses) { ++ DCHECK(is_int16( ++ static_cast(src->offset() + second_access_add_to_offset))); ++ } ++ DCHECK(misalignment == (src->offset() & (kDoubleSize - 1))); ++} ++ ++void Assembler::fmovd(FPURegister fs, FPURegister fd) { fcpys(fs, fs, fd); } ++ ++void Assembler::fmovs(FPURegister fs, FPURegister fd) { fcpys(fs, fs, fd); } ++ ++// Conversions. ++void Assembler::fcvtsw(FPURegister fs, FPURegister fd) { ++ DCHECK(fs != kScratchDoubleReg1 && fd != kScratchDoubleReg1); ++ DCHECK(fs != kScratchDoubleReg2 && fd != kScratchDoubleReg2); ++ fcvtsd(fs, kScratchDoubleReg2); ++ fcvtdl(kScratchDoubleReg2, kScratchDoubleReg1); ++ fcvtlw(kScratchDoubleReg1, fd); ++} ++ ++void Assembler::fcvtdw(FPURegister fs, FPURegister fd) { ++ DCHECK(fs != kScratchDoubleReg1 && fd != kScratchDoubleReg1); ++ fcvtdl(fs, kScratchDoubleReg1); ++ fcvtlw(kScratchDoubleReg1, fd); ++} ++ ++void Assembler::ftruncsw(FPURegister fs, FPURegister fd) { ++ DCHECK(fs != kScratchDoubleReg1 && fd != kScratchDoubleReg1); ++ DCHECK(fs != kScratchDoubleReg2 && fd != kScratchDoubleReg2); ++ fcvtsd(fs, kScratchDoubleReg1); ++ fcvtdl_z(kScratchDoubleReg1, kScratchDoubleReg2); ++ fcvtlw(kScratchDoubleReg2, fd); ++} ++ ++void Assembler::ftruncdw(FPURegister fs, FPURegister fd) { ++ DCHECK(fs != kScratchDoubleReg1 && fd != kScratchDoubleReg1); ++ fcvtdl_z(fs, kScratchDoubleReg1); ++ fcvtlw(kScratchDoubleReg1, fd); ++} ++ ++void Assembler::froundsw(FPURegister fs, FPURegister fd) { ++ DCHECK(fs != kScratchDoubleReg1 && fd != kScratchDoubleReg1); ++ DCHECK(fs != kScratchDoubleReg2 && fd != kScratchDoubleReg2); ++ fcvtsd(fs, kScratchDoubleReg1); ++ fcvtdl_g(kScratchDoubleReg1, kScratchDoubleReg2); ++ fcvtlw(kScratchDoubleReg2, fd); ++} ++ ++void Assembler::frounddw(FPURegister fs, FPURegister fd) { ++ DCHECK(fs != kScratchDoubleReg1 && fd != kScratchDoubleReg1); ++ fcvtdl_g(fs, kScratchDoubleReg1); ++ fcvtlw(kScratchDoubleReg1, fd); ++} ++ ++void Assembler::ffloorsw(FPURegister fs, FPURegister fd) { ++ DCHECK(fs != kScratchDoubleReg1 && fd != kScratchDoubleReg1); ++ DCHECK(fs != kScratchDoubleReg2 && fd != kScratchDoubleReg2); ++ fcvtsd(fs, kScratchDoubleReg2); ++ fcvtdl_n(kScratchDoubleReg2, kScratchDoubleReg1); ++ fcvtlw(kScratchDoubleReg1, fd); ++} ++ ++void Assembler::ffloordw(FPURegister fs, FPURegister fd) { ++ DCHECK(fs != kScratchDoubleReg1 && fd != kScratchDoubleReg1); ++ fcvtdl_n(fs, kScratchDoubleReg1); ++ fcvtlw(kScratchDoubleReg1, fd); ++} ++ ++void Assembler::fceilsw(FPURegister fs, FPURegister fd) { ++ DCHECK(fs != kScratchDoubleReg1 && fd != kScratchDoubleReg1); ++ DCHECK(fs != kScratchDoubleReg2 && fd != kScratchDoubleReg2); ++ fcvtsd(fs, kScratchDoubleReg2); ++ fcvtdl_p(kScratchDoubleReg2, kScratchDoubleReg1); ++ fcvtlw(kScratchDoubleReg1, fd); ++} ++ ++void Assembler::fceildw(FPURegister fs, FPURegister fd) { ++ DCHECK(fs != kScratchDoubleReg1 && fd != kScratchDoubleReg1); ++ fcvtdl_p(fs, kScratchDoubleReg1); ++ fcvtlw(kScratchDoubleReg1, fd); ++} ++ ++void Assembler::fcvtsl(FPURegister fs, FPURegister fd) { ++ DCHECK(fs != kScratchDoubleReg1 && fd != kScratchDoubleReg1); ++ fcvtsd(fs, kScratchDoubleReg1); ++ fcvtdl(kScratchDoubleReg1, fd); ++} ++ ++void Assembler::ftruncsl(FPURegister fs, FPURegister fd) { ++ DCHECK(fs != kScratchDoubleReg1 && fd != kScratchDoubleReg1); ++ fcvtsd(fs, kScratchDoubleReg1); ++ fcvtdl_z(kScratchDoubleReg1, fd); ++} ++ ++void Assembler::ftruncdl(FPURegister fs, FPURegister fd) { ++ DCHECK(fs != kScratchDoubleReg1 && fd != kScratchDoubleReg1); ++ if (fs == fd) { ++ fmovd(fs, kScratchDoubleReg1); ++ fcvtdl_z(kScratchDoubleReg1, fd); ++ } else { ++ fcvtdl_z(fs, fd); ++ } ++} ++ ++void Assembler::froundsl(FPURegister fs, FPURegister fd) { ++ DCHECK(fs != kScratchDoubleReg1 && fd != kScratchDoubleReg1); ++ fcvtsd(fs, kScratchDoubleReg1); ++ fcvtdl_g(kScratchDoubleReg1, fd); ++} ++ ++void Assembler::frounddl(FPURegister fs, FPURegister fd) { ++ DCHECK(fs != kScratchDoubleReg1 && fd != kScratchDoubleReg1); ++ if (fs == fd) { ++ fmovd(fs, kScratchDoubleReg1); ++ fcvtdl_g(kScratchDoubleReg1, fd); ++ } else { ++ fcvtdl_g(fs, fd); ++ } ++} ++ ++void Assembler::ffloorsl(FPURegister fs, FPURegister fd) { ++ DCHECK(fs != kScratchDoubleReg1 && fd != kScratchDoubleReg1); ++ fcvtsd(fs, kScratchDoubleReg1); ++ fcvtdl_n(kScratchDoubleReg1, fd); ++} ++ ++void Assembler::ffloordl(FPURegister fs, FPURegister fd) { ++ DCHECK(fs != kScratchDoubleReg1 && fd != kScratchDoubleReg1); ++ if (fs == fd) { ++ fmovd(fs, kScratchDoubleReg1); ++ fcvtdl_n(kScratchDoubleReg1, fd); ++ } else { ++ fcvtdl_n(fs, fd); ++ } ++} ++ ++void Assembler::fceilsl(FPURegister fs, FPURegister fd) { ++ DCHECK(fs != kScratchDoubleReg1 && fd != kScratchDoubleReg1); ++ fcvtsd(fs, kScratchDoubleReg1); ++ fcvtdl_p(kScratchDoubleReg1, fd); ++} ++ ++void Assembler::fceildl(FPURegister fs, FPURegister fd) { ++ DCHECK(fs != kScratchDoubleReg1 && fd != kScratchDoubleReg1); ++ if (fs == fd) { ++ fmovd(fs, kScratchDoubleReg1); ++ fcvtdl_p(kScratchDoubleReg1, fd); ++ } else { ++ fcvtdl_p(fs, fd); ++ } ++} ++ ++void Assembler::fcvtws(FPURegister fs, FPURegister fd) { ++ DCHECK(fs != kScratchDoubleReg1 && fd != kScratchDoubleReg1); ++ fcvtwl(fs, kScratchDoubleReg1); ++ fcvtls(kScratchDoubleReg1, fd); ++} ++ ++void Assembler::fcvtls_(FPURegister fs, FPURegister fd) { ++ DCHECK(fs != kScratchDoubleReg1 && fd != kScratchDoubleReg1); ++ if (fs == fd) { ++ fmovd(fs, kScratchDoubleReg1); ++ fcvtls(kScratchDoubleReg1, fd); ++ } else { ++ fcvtls(fs, fd); ++ } ++} ++ ++void Assembler::fcvtds_(FPURegister fs, FPURegister fd) { ++ DCHECK(fs != kScratchDoubleReg1 && fd != kScratchDoubleReg1); ++ if (fs == fd) { ++ fmovd(fs, kScratchDoubleReg1); ++ fcvtds(kScratchDoubleReg1, fd); ++ } else { ++ fcvtds(fs, fd); ++ } ++} ++ ++void Assembler::fcvtwd(FPURegister fs, FPURegister fd) { ++ DCHECK(fs != kScratchDoubleReg1 && fd != kScratchDoubleReg1); ++ fcvtwl(fs, kScratchDoubleReg1); ++ fcvtld(kScratchDoubleReg1, fd); ++} ++ ++void Assembler::fcvtld_(FPURegister fs, FPURegister fd) { ++ DCHECK(fs != kScratchDoubleReg1 && fd != kScratchDoubleReg1); ++ if (fs == fd) { ++ fmovd(fs, kScratchDoubleReg1); ++ fcvtld(kScratchDoubleReg1, fd); ++ } else { ++ fcvtld(fs, fd); ++ } ++} ++ ++void Assembler::fcvtsd_(FPURegister fs, FPURegister fd) { ++ DCHECK(fs != kScratchDoubleReg1 && fd != kScratchDoubleReg1); ++ if (fs == fd) { ++ fmovs(fs, kScratchDoubleReg1); ++ fcvtsd(kScratchDoubleReg1, fd); ++ } else { ++ fcvtsd(fs, fd); ++ } ++} ++ ++void Assembler::GenInstrB_SW(Opcode_ops_bra opcode, Register Ra, int32_t disp) { ++ DCHECK(Ra.is_valid() && is_int21(disp)); ++ Instr instr = opcode | (Ra.code() << sRaShift) | (disp & sImm21Mask); ++ emit(instr); ++} ++ ++void Assembler::GenInstrFB_SW(Opcode_ops_bra opcode, FloatRegister fa, ++ int32_t disp) { ++ DCHECK(fa.is_valid() && is_int21(disp)); ++ Instr instr = opcode | (fa.code() << sRaShift) | (disp & sImm21Mask); ++ emit(instr); ++} ++ ++void Assembler::GenInstrM_SW(Opcode_ops_mem opcode, Register Ra, int16_t disp, ++ Register Rb) { ++ DCHECK(Ra.is_valid() && Rb.is_valid() && is_int16(disp)); ++ Instr instr = opcode | (Ra.code() << sRaShift) | (Rb.code() << sRbShift) | ++ (disp & sImm16Mask); ++ emit(instr); ++} ++ ++void Assembler::GenInstrFM_SW(Opcode_ops_mem opcode, FloatRegister fa, ++ int16_t disp, Register Rb) { ++ DCHECK(fa.is_valid() && Rb.is_valid() && is_int16(disp)); ++ Instr instr = opcode | (fa.code() << sRaShift) | (Rb.code() << sRbShift) | ++ (disp & sImm16Mask); ++ emit(instr); ++} ++ ++void Assembler::GenInstrMWithFun_SW(Opcode_ops_atmem opcode, Register Ra, ++ int16_t disp, Register Rb) { ++ DCHECK(Ra.is_valid() && Rb.is_valid() && is_int12(disp)); ++ Instr instr = opcode | (Ra.code() << sRaShift) | (Rb.code() << sRbShift) | ++ (disp & sImm12Mask); ++ emit(instr); ++} ++ ++void Assembler::GenInstrR_SW(Opcode_ops_opr opcode, Register Ra, Register Rb, ++ Register Rc) { ++ DCHECK(Ra.is_valid() && Rb.is_valid() && Rc.is_valid()); ++ Instr instr = opcode | (Ra.code() << sRaShift) | (Rb.code() << sRbShift) | ++ (Rc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::GenInstrI_SW(Opcode_ops_oprl opcode, Register Ra, int16_t imm, ++ Register Rc) { ++ DCHECK(Ra.is_valid() && is_uint8(imm) && Rc.is_valid()); ++ Instr instr = opcode | (Ra.code() << sRaShift) | ++ ((imm << sImm8Shift) & sImm8Mask) | (Rc.code() << sRcShift); ++ emit(instr); ++} ++ ++// Float-point ALU instructions. ++void Assembler::GenInstrFR_SW(Opcode_ops_fp opcode, FloatRegister fa, ++ FloatRegister fb, FloatRegister fc) { ++ DCHECK(fa.is_valid() && fb.is_valid() && fc.is_valid()); ++ Instr instr = opcode | (fa.code() << sRaShift) | (fb.code() << sRbShift) | ++ (fc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::GenInstrFR_SW(Opcode_ops_fp opcode, FloatRegister fb, ++ FloatRegister fc) { ++ DCHECK(fb.is_valid() && fc.is_valid()); ++ Instr instr = opcode | (fb.code() << sRbShift) | (fc.code() << sRcShift); ++ emit(instr); ++} ++ ++// 20180914 ++void Assembler::GenInstrFR_SW(Opcode_ops_fpl opcode, FloatRegister fa, ++ int16_t imm, FloatRegister fc) { ++ DCHECK(fa.is_valid() && is_uint8(imm) && fc.is_valid()); ++ Instr instr = opcode | (fa.code() << sRaShift) | ++ ((imm << sImm8Shift) & sImm8Mask) | (fc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::GenInstrFR_SW(Opcode_ops_fpl opcode, FloatRegister fa, ++ FloatRegister fb, int16_t fmalit, ++ FloatRegister fc) { ++ DCHECK(fa.is_valid() && fb.is_valid() && is_uint5(fmalit) && fc.is_valid()); ++ Instr instr = opcode | (fa.code() << sRaShift) | (fb.code() << sRbShift) | ++ ((fmalit << sImm5Shift) & sImm5Mask) | (fc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::GenInstrFMA_SW(Opcode_ops_fmal opcode, FloatRegister fa, ++ FloatRegister fb, int16_t fmalit, ++ FloatRegister fc) { ++ DCHECK(fa.is_valid() && fb.is_valid() && is_uint5(fmalit) && fc.is_valid()); ++ Instr instr = opcode | (fa.code() << sRaShift) | (fb.code() << sRbShift) | ++ ((fmalit << sImm5Shift) & sImm5Mask) | (fc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::GenInstrFMA_SW(Opcode_ops_fmal opcode, FloatRegister fa, ++ int16_t fmalit, FloatRegister fc) { ++ DCHECK(fa.is_valid() && is_uint5(fmalit) && fc.is_valid()); ++ Instr instr = opcode | (fa.code() << sRaShift) | ++ ((fmalit << sImm5Shift) & sImm5Mask) | (fc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::GenInstrFMA_SW(Opcode_ops_fma opcode, FloatRegister fa, ++ FloatRegister fb, FloatRegister fc) { ++ DCHECK(fa.is_valid() && fb.is_valid() && fc.is_valid()); ++ Instr instr = opcode | (fa.code() << sRaShift) | (fb.code() << sRbShift) | ++ (fc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::GenInstrSWSA_SW(Opcode_ops_atmem opcode, FloatRegister fa, ++ int16_t atmdisp, Register Rb) { ++ DCHECK(fa.is_valid() && is_uint11(atmdisp) && Rb.is_valid()); ++ Instr instr = opcode | (fa.code() << sRaShift) | ++ ((atmdisp << sImm11Shift) & sImm11Mask) | ++ (Rb.code() << sRbShift); ++ emit(instr); ++} ++ ++// FMA + FSEL** instructions. ++void Assembler::GenInstrFMA_SW(Opcode_ops_fma opcode, FloatRegister fa, ++ FloatRegister fb, FloatRegister f3, ++ FloatRegister fc) { ++ DCHECK(fa.is_valid() && fb.is_valid() && f3.is_valid() && fc.is_valid()); ++ Instr instr = opcode | (fa.code() << sRaShift) | (fb.code() << sRbShift) | ++ (f3.code() << sR3Shift) | (fc.code() << sRcShift); ++ emit(instr); ++} ++ ++// SEL** instructions. ++void Assembler::GenInstrSelR_SW(Opcode_ops_sel opcode, Register Ra, Register Rb, ++ Register R3, Register Rc) { ++ DCHECK(Ra.is_valid() && Rb.is_valid() && R3.is_valid() && Rc.is_valid()); ++ Instr instr = opcode | (Ra.code() << sRaShift) | (Rb.code() << sRbShift) | ++ (R3.code() << sR3Shift) | (Rc.code() << sRcShift); ++ emit(instr); ++} ++ ++// SEL**_l instructions. ++void Assembler::GenInstrSelI_SW(Opcode_ops_sel_l opcode, Register Ra, ++ int32_t imm, Register R3, Register Rc) { ++ DCHECK(Ra.is_valid() && is_int8(imm) && R3.is_valid() && Rc.is_valid()); ++ Instr instr = opcode | (Ra.code() << sRaShift) | ++ ((imm << sImm8Shift) & sImm8Mask) | (R3.code() << sR3Shift) | ++ (Rc.code() << sRcShift); ++ emit(instr); ++} ++ ++// All SW64 instructions ++ ++void Assembler::sys_call_b(int palfn) { ++ DCHECK(is_int26(palfn)); ++ Instr instr = op_sys_call | palfn; ++ emit(instr); ++} ++ ++void Assembler::sys_call(int palfn) { ++ DCHECK(is_int26(palfn)); ++ Instr instr = op_sys_call | (palfn & ((1 << 26) - 1)); ++ emit(instr); ++} ++ ++void Assembler::call(Register Ra, Register Rb, int jmphint) { ++ // call ra, (rb), jmphint; ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ GenInstrM_SW(op_call, Ra, jmphint, Rb); ++} ++ ++void Assembler::ret(Register Ra, Register Rb, int rethint) { ++ // ret ra, (rb), rethint; ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ GenInstrM_SW(op_ret, Ra, rethint, Rb); ++} ++ ++void Assembler::jmp(Register Ra, Register Rb, int jmphint) { ++ // jmp ra, (rb), jmphint; ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ GenInstrM_SW(op_jmp, Ra, jmphint, Rb); ++} ++ ++void Assembler::br(Register Ra, int bdisp) { ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ GenInstrB_SW(op_br, Ra, bdisp); ++} ++ ++void Assembler::bsr(Register Ra, int bdisp) { ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ GenInstrB_SW(op_bsr, Ra, bdisp); ++} ++ ++void Assembler::memb(void) { ++ Instr instr = op_memb; ++ emit(instr); ++} ++ ++void Assembler::imemb(void) { ++ Instr instr = op_imemb; ++ emit(instr); ++} ++ ++void Assembler::wmemb(void) { ++ Instr instr = op_wmemb; ++ emit(instr); ++} ++ ++void Assembler::rtc(Register Ra, Register Rb) { ++ DCHECK(Ra.is_valid() && Rb.is_valid()); ++ Instr instr = op_rtc | Ra.code() << sRaShift | Rb.code() << sRbShift; ++ emit(instr); ++} ++ ++void Assembler::rcid(Register Ra) { ++ DCHECK(Ra.is_valid()); ++ Instr instr = op_rcid | Ra.code() << sRaShift; ++ emit(instr); ++} ++ ++void Assembler::halt(void) { ++ Instr instr = op_halt; ++ emit(instr); ++} ++ ++void Assembler::rd_f(Register Ra) { ++ DCHECK(Ra.is_valid()); ++ Instr instr = op_rd_f | Ra.code() << sRaShift; ++ emit(instr); ++} ++ ++void Assembler::wr_f(Register Ra) { ++ DCHECK(Ra.is_valid()); ++ Instr instr = op_wr_f | Ra.code() << sRaShift; ++ emit(instr); ++} ++ ++void Assembler::rtid(Register Ra) { ++ DCHECK(Ra.is_valid()); ++ Instr instr = op_rtid | Ra.code() << sRaShift; ++ emit(instr); ++} ++ ++void Assembler::csrrs(Register Ra, int rpiindex) { ++ DCHECK(Ra.is_valid() && is_uint8(rpiindex)); ++ Instr instr = op_csrrs | (Ra.code() << sRaShift) | (rpiindex & sRpiMask); ++ emit(instr); ++} ++ ++void Assembler::csrrc(Register Ra, int rpiindex) { ++ DCHECK(Ra.is_valid() && is_uint8(rpiindex)); ++ Instr instr = op_csrrc | (Ra.code() << sRaShift) | (rpiindex & sRpiMask); ++ emit(instr); ++} ++ ++void Assembler::csrr(Register Ra, int rpiindex) { ++ DCHECK(Ra.is_valid() && is_uint8(rpiindex)); ++ Instr instr = op_csrr | (Ra.code() << sRaShift) | (rpiindex & sRpiMask); ++ emit(instr); ++} ++ ++void Assembler::csrw(Register Ra, int rpiindex) { ++ DCHECK(Ra.is_valid() && is_uint8(rpiindex)); ++ Instr instr = op_csrw | (Ra.code() << sRaShift) | (rpiindex & sRpiMask); ++ emit(instr); ++} ++ ++void Assembler::pri_ret(Register Ra) { ++ DCHECK(Ra.is_valid()); ++ Instr instr = op_pri_ret | Ra.code() << sRaShift; ++ emit(instr); ++} ++ ++void Assembler::lldw(Register Ra, int atmdisp, Register Rb) { ++ GenInstrMWithFun_SW(op_lldw, Ra, atmdisp, Rb); ++} ++ ++void Assembler::lldl(Register Ra, int atmdisp, Register Rb) { ++ GenInstrMWithFun_SW(op_lldl, Ra, atmdisp, Rb); ++} ++ ++void Assembler::ldw_inc(Register Ra, int atmdisp, Register Rb) { ++ GenInstrMWithFun_SW(op_ldw_inc, Ra, atmdisp, Rb); ++} ++ ++void Assembler::ldl_inc(Register Ra, int atmdisp, Register Rb) { ++ GenInstrMWithFun_SW(op_ldl_inc, Ra, atmdisp, Rb); ++} ++ ++void Assembler::ldw_dec(Register Ra, int atmdisp, Register Rb) { ++ GenInstrMWithFun_SW(op_ldw_dec, Ra, atmdisp, Rb); ++} ++ ++void Assembler::ldl_dec(Register Ra, int atmdisp, Register Rb) { ++ GenInstrMWithFun_SW(op_ldl_dec, Ra, atmdisp, Rb); ++} ++ ++void Assembler::ldw_set(Register Ra, int atmdisp, Register Rb) { ++ GenInstrMWithFun_SW(op_ldw_set, Ra, atmdisp, Rb); ++} ++ ++void Assembler::ldl_set(Register Ra, int atmdisp, Register Rb) { ++ GenInstrMWithFun_SW(op_ldl_set, Ra, atmdisp, Rb); ++} ++ ++void Assembler::lstw(Register Ra, int atmdisp, Register Rb) { ++ GenInstrMWithFun_SW(op_lstw, Ra, atmdisp, Rb); ++} ++ ++void Assembler::lstl(Register Ra, int atmdisp, Register Rb) { ++ GenInstrMWithFun_SW(op_lstl, Ra, atmdisp, Rb); ++} ++ ++void Assembler::ldw_nc(Register Ra, int atmdisp, Register Rb) { ++ DCHECK(Ra.is_valid() && is_uint11(atmdisp) && Rb.is_valid()); ++ Instr instr = op_ldw_nc | (Ra.code() << sRaShift) | ++ ((atmdisp << sImm11Shift) & sImm11Mask) | ++ (Rb.code() << sRbShift); ++ emit(instr); ++} ++ ++void Assembler::ldl_nc(Register Ra, int atmdisp, Register Rb) { ++ DCHECK(Ra.is_valid() && is_uint11(atmdisp) && Rb.is_valid()); ++ Instr instr = op_ldl_nc | (Ra.code() << sRaShift) | ++ ((atmdisp << sImm11Shift) & sImm11Mask) | ++ (Rb.code() << sRbShift); ++ emit(instr); ++} ++ ++void Assembler::ldd_nc(Register Ra, int atmdisp, Register Rb) { ++ DCHECK(Ra.is_valid() && is_uint11(atmdisp) && Rb.is_valid()); ++ Instr instr = op_ldd_nc | (Ra.code() << sRaShift) | ++ ((atmdisp << sImm11Shift) & sImm11Mask) | ++ (Rb.code() << sRbShift); ++ emit(instr); ++} ++ ++void Assembler::stw_nc(Register Ra, int atmdisp, Register Rb) { ++ DCHECK(Ra.is_valid() && is_uint11(atmdisp) && Rb.is_valid()); ++ Instr instr = op_stw_nc | (Ra.code() << sRaShift) | ++ ((atmdisp << sImm11Shift) & sImm11Mask) | ++ (Rb.code() << sRbShift); ++ emit(instr); ++} ++ ++void Assembler::stl_nc(Register Ra, int atmdisp, Register Rb) { ++ DCHECK(Ra.is_valid() && is_uint11(atmdisp) && Rb.is_valid()); ++ Instr instr = op_stl_nc | (Ra.code() << sRaShift) | ++ ((atmdisp << sImm11Shift) & sImm11Mask) | ++ (Rb.code() << sRbShift); ++ emit(instr); ++} ++ ++void Assembler::std_nc(Register Ra, int atmdisp, Register Rb) { ++ DCHECK(Ra.is_valid() && is_uint11(atmdisp) && Rb.is_valid()); ++ Instr instr = op_std_nc | (Ra.code() << sRaShift) | ++ ((atmdisp << sImm11Shift) & sImm11Mask) | ++ (Rb.code() << sRbShift); ++ emit(instr); ++} ++ ++void Assembler::ldwe(FloatRegister fa, int mdisp, Register Rb) { ++ GenInstrFM_SW(op_ldwe, fa, mdisp, Rb); ++} ++ ++void Assembler::ldse(FloatRegister fa, int mdisp, Register Rb) { ++ GenInstrFM_SW(op_ldse, fa, mdisp, Rb); ++} ++ ++void Assembler::ldde(FloatRegister fa, int mdisp, Register Rb) { ++ GenInstrFM_SW(op_ldde, fa, mdisp, Rb); ++} ++ ++void Assembler::vlds(FloatRegister fa, int mdisp, Register Rb) { ++ GenInstrFM_SW(op_vlds, fa, mdisp, Rb); ++} ++ ++void Assembler::vldd(FloatRegister fa, int mdisp, Register Rb) { ++ GenInstrFM_SW(op_vldd, fa, mdisp, Rb); ++} ++ ++void Assembler::vsts(FloatRegister fa, int mdisp, Register Rb) { ++ GenInstrFM_SW(op_vsts, fa, mdisp, Rb); ++} ++ ++void Assembler::vstd(FloatRegister fa, int mdisp, Register Rb) { ++ GenInstrFM_SW(op_vstd, fa, mdisp, Rb); ++} ++ ++void Assembler::addw(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_addw, Ra, Rb, Rc); ++} ++ ++void Assembler::addw(Register Ra, int imm, Register Rc) { ++ GenInstrI_SW(op_addw_l, Ra, imm, Rc); ++} ++ ++void Assembler::subw(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_subw, Ra, Rb, Rc); ++} ++ ++void Assembler::subw(Register Ra, int imm, Register Rc) { ++ GenInstrI_SW(op_subw_l, Ra, imm, Rc); ++} ++ ++void Assembler::s4addw(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_s4addw, Ra, Rb, Rc); ++} ++ ++void Assembler::s4addw(Register Ra, int imm, Register Rc) { ++ GenInstrI_SW(op_s4addw_l, Ra, imm, Rc); ++} ++ ++void Assembler::s4subw(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_s4subw, Ra, Rb, Rc); ++} ++ ++void Assembler::s4subw(Register Ra, int imm, Register Rc) { ++ GenInstrI_SW(op_s4subw_l, Ra, imm, Rc); ++} ++ ++void Assembler::s8addw(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_s8addw, Ra, Rb, Rc); ++} ++ ++void Assembler::s8addw(Register Ra, int imm, Register Rc) { ++ GenInstrI_SW(op_s8addw_l, Ra, imm, Rc); ++} ++ ++void Assembler::s8subw(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_s8subw, Ra, Rb, Rc); ++} ++ ++void Assembler::s8subw(Register Ra, int imm, Register Rc) { ++ GenInstrI_SW(op_s8subw_l, Ra, imm, Rc); ++} ++ ++void Assembler::addl(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_addl, Ra, Rb, Rc); ++} ++ ++void Assembler::addl(Register Ra, int imm, Register Rc) { ++ GenInstrI_SW(op_addl_l, Ra, imm, Rc); ++} ++ ++void Assembler::subl(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_subl, Ra, Rb, Rc); ++} ++ ++void Assembler::subl(Register Ra, int imm, Register Rc) { ++ GenInstrI_SW(op_subl_l, Ra, imm, Rc); ++} ++ ++void Assembler::s4addl(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_s4addl, Ra, Rb, Rc); ++} ++ ++void Assembler::s4addl(Register Ra, int imm, Register Rc) { ++ GenInstrI_SW(op_s4addl_l, Ra, imm, Rc); ++} ++ ++void Assembler::s4subl(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_s4subl, Ra, Rb, Rc); ++} ++ ++void Assembler::s4subl(Register Ra, int imm, Register Rc) { ++ GenInstrI_SW(op_s4subl_l, Ra, imm, Rc); ++} ++ ++void Assembler::s8addl(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_s8addl, Ra, Rb, Rc); ++} ++ ++void Assembler::s8addl(Register Ra, int imm, Register Rc) { ++ GenInstrI_SW(op_s8addl_l, Ra, imm, Rc); ++} ++ ++void Assembler::s8subl(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_s8subl, Ra, Rb, Rc); ++} ++ ++void Assembler::s8subl(Register Ra, int imm, Register Rc) { ++ GenInstrI_SW(op_s8subl_l, Ra, imm, Rc); ++} ++ ++void Assembler::mulw(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_mulw, Ra, Rb, Rc); ++} ++ ++void Assembler::mulw(Register Ra, int imm, Register Rc) { ++ GenInstrI_SW(op_mulw_l, Ra, imm, Rc); ++} ++ ++void Assembler::divw(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_divw, Ra, Rb, Rc); ++} ++ ++void Assembler::udivw(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_udivw, Ra, Rb, Rc); ++} ++ ++void Assembler::remw(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_remw, Ra, Rb, Rc); ++} ++ ++void Assembler::uremw(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_uremw, Ra, Rb, Rc); ++} ++ ++void Assembler::mull(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_mull, Ra, Rb, Rc); ++} ++ ++void Assembler::mull(Register Ra, int imm, Register Rc) { ++ GenInstrI_SW(op_mull_l, Ra, imm, Rc); ++} ++ ++void Assembler::umulh(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_umulh, Ra, Rb, Rc); ++} ++ ++void Assembler::umulh(Register Ra, int imm, Register Rc) { ++ GenInstrI_SW(op_umulh_l, Ra, imm, Rc); ++} ++ ++void Assembler::divl(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_divl, Ra, Rb, Rc); ++} ++ ++void Assembler::udivl(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_udivl, Ra, Rb, Rc); ++} ++ ++void Assembler::reml(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_reml, Ra, Rb, Rc); ++} ++ ++void Assembler::ureml(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_ureml, Ra, Rb, Rc); ++} ++ ++void Assembler::addpi(int apint, Register Rc) { ++ DCHECK(is_int13(apint) && Rc.is_valid()); ++ Instr instr = op_addpi | ((apint << sImm13Shift) & sImm13Mask) | ++ (Rc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::addpis(int apint, Register Rc) { ++ DCHECK(is_int13(apint) && Rc.is_valid()); ++ Instr instr = op_addpis | ((apint << sImm13Shift) & sImm13Mask) | ++ (Rc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::cmpeq(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_cmpeq, Ra, Rb, Rc); ++} ++ ++void Assembler::cmpeq(Register Ra, int lit, Register Rc) { ++ GenInstrI_SW(op_cmpeq_l, Ra, lit, Rc); ++} ++ ++void Assembler::cmplt(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_cmplt, Ra, Rb, Rc); ++} ++ ++void Assembler::cmplt(Register Ra, int lit, Register Rc) { ++ GenInstrI_SW(op_cmplt_l, Ra, lit, Rc); ++} ++ ++void Assembler::cmple(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_cmple, Ra, Rb, Rc); ++} ++ ++void Assembler::cmple(Register Ra, int lit, Register Rc) { ++ GenInstrI_SW(op_cmple_l, Ra, lit, Rc); ++} ++ ++void Assembler::cmpult(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_cmpult, Ra, Rb, Rc); ++} ++ ++void Assembler::cmpult(Register Ra, int lit, Register Rc) { ++ GenInstrI_SW(op_cmpult_l, Ra, lit, Rc); ++} ++ ++void Assembler::cmpule(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_cmpule, Ra, Rb, Rc); ++} ++ ++void Assembler::cmpule(Register Ra, int lit, Register Rc) { ++ GenInstrI_SW(op_cmpule_l, Ra, lit, Rc); ++} ++ ++void Assembler::sbt(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_sbt, Ra, Rb, Rc); ++} ++ ++void Assembler::sbt(Register Ra, int lit, Register Rc) { ++ GenInstrI_SW(op_sbt_l, Ra, lit, Rc); ++} ++ ++void Assembler::cbt(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_cbt, Ra, Rb, Rc); ++} ++ ++void Assembler::cbt(Register Ra, int lit, Register Rc) { ++ GenInstrI_SW(op_cbt_l, Ra, lit, Rc); ++} ++ ++void Assembler::and_(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_and, Ra, Rb, Rc); ++} ++ ++void Assembler::and_(Register Ra, int lit, Register Rc) { ++ GenInstrI_SW(op_and_l, Ra, lit, Rc); ++} ++ ++void Assembler::bic(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_bic, Ra, Rb, Rc); ++} ++ ++void Assembler::bic(Register Ra, int lit, Register Rc) { ++ GenInstrI_SW(op_bic_l, Ra, lit, Rc); ++} ++ ++void Assembler::andnot(Register Ra, Register Rb, Register Rc) { ++ bic(Ra, Rb, Rc); ++} ++ ++void Assembler::andnot(Register Ra, int lit, Register Rc) { bic(Ra, lit, Rc); } ++ ++void Assembler::bis(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_bis, Ra, Rb, Rc); ++} ++ ++void Assembler::bis(Register Ra, int lit, Register Rc) { ++ GenInstrI_SW(op_bis_l, Ra, lit, Rc); ++} ++ ++void Assembler::or_(Register Ra, Register Rb, Register Rc) { ++ // GenInstrR_SW(op_bis, Ra, Rb, Rc); ++ bis(Ra, Rb, Rc); ++} ++ ++void Assembler::or_(Register Ra, int lit, Register Rc) { ++ // GenInstrI_SW(op_bis_l, Ra, lit, Rc); ++ bis(Ra, lit, Rc); ++} ++ ++void Assembler::ornot(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_ornot, Ra, Rb, Rc); ++} ++ ++void Assembler::ornot(Register Ra, int lit, Register Rc) { ++ GenInstrI_SW(op_ornot_l, Ra, lit, Rc); ++} ++ ++void Assembler::xor_(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_xor, Ra, Rb, Rc); ++} ++ ++void Assembler::xor_(Register Ra, int lit, Register Rc) { ++ GenInstrI_SW(op_xor_l, Ra, lit, Rc); ++} ++ ++void Assembler::eqv(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_eqv, Ra, Rb, Rc); ++} ++ ++void Assembler::eqv(Register Ra, int lit, Register Rc) { ++ GenInstrI_SW(op_eqv_l, Ra, lit, Rc); ++} ++ ++// 0x10.40-0x10.47 INS[0-7]B ++void Assembler::inslb(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_inslb, Ra, Rb, Rc); ++} ++ ++void Assembler::inslb(Register Ra, int lit, Register Rc) { ++ GenInstrI_SW(op_inslb_l, Ra, lit, Rc); ++} ++ ++void Assembler::inslh(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_inslh, Ra, Rb, Rc); ++} ++ ++void Assembler::inslh(Register Ra, int lit, Register Rc) { ++ GenInstrI_SW(op_inslh_l, Ra, lit, Rc); ++} ++ ++void Assembler::inslw(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_inslw, Ra, Rb, Rc); ++} ++ ++void Assembler::inslw(Register Ra, int lit, Register Rc) { ++ GenInstrI_SW(op_inslw_l, Ra, lit, Rc); ++} ++ ++void Assembler::insll(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_insll, Ra, Rb, Rc); ++} ++ ++void Assembler::insll(Register Ra, int lit, Register Rc) { ++ GenInstrI_SW(op_insll_l, Ra, lit, Rc); ++} ++ ++void Assembler::inshb(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_inshb, Ra, Rb, Rc); ++} ++ ++void Assembler::inshb(Register Ra, int lit, Register Rc) { ++ GenInstrI_SW(op_inshb_l, Ra, lit, Rc); ++} ++ ++void Assembler::inshh(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_inshh, Ra, Rb, Rc); ++} ++ ++void Assembler::inshh(Register Ra, int lit, Register Rc) { ++ GenInstrI_SW(op_inshh_l, Ra, lit, Rc); ++} ++ ++void Assembler::inshw(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_inshw, Ra, Rb, Rc); ++} ++ ++void Assembler::inshw(Register Ra, int lit, Register Rc) { ++ GenInstrI_SW(op_inshw_l, Ra, lit, Rc); ++} ++ ++void Assembler::inshl(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_inshl, Ra, Rb, Rc); ++} ++ ++void Assembler::inshl(Register Ra, int lit, Register Rc) { ++ GenInstrI_SW(op_inshl_l, Ra, lit, Rc); ++} ++ ++void Assembler::slll(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_slll, Ra, Rb, Rc); ++} ++ ++void Assembler::slll(Register Ra, int lit, Register Rc) { ++ GenInstrI_SW(op_slll_l, Ra, lit, Rc); ++} ++ ++void Assembler::srll(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_srll, Ra, Rb, Rc); ++} ++ ++void Assembler::srll(Register Ra, int lit, Register Rc) { ++ GenInstrI_SW(op_srll_l, Ra, lit, Rc); ++} ++ ++void Assembler::sral(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_sral, Ra, Rb, Rc); ++} ++ ++void Assembler::sral(Register Ra, int lit, Register Rc) { ++ GenInstrI_SW(op_sral_l, Ra, lit, Rc); ++} ++ ++void Assembler::roll(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_roll, Ra, Rb, Rc); ++} ++ ++void Assembler::roll(Register Ra, int lit, Register Rc) { ++ GenInstrI_SW(op_roll_l, Ra, lit, Rc); ++} ++ ++void Assembler::sllw(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_sllw, Ra, Rb, Rc); ++} ++ ++void Assembler::sllw(Register Ra, int lit, Register Rc) { ++ GenInstrI_SW(op_sllw_l, Ra, lit, Rc); ++} ++ ++void Assembler::srlw(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_srlw, Ra, Rb, Rc); ++} ++ ++void Assembler::srlw(Register Ra, int lit, Register Rc) { ++ GenInstrI_SW(op_srlw_l, Ra, lit, Rc); ++} ++ ++void Assembler::sraw(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_sraw, Ra, Rb, Rc); ++} ++ ++void Assembler::sraw(Register Ra, int lit, Register Rc) { ++ GenInstrI_SW(op_sraw_l, Ra, lit, Rc); ++} ++ ++void Assembler::rolw(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_rolw, Ra, Rb, Rc); ++} ++ ++void Assembler::rolw(Register Ra, int lit, Register Rc) { ++ GenInstrI_SW(op_rolw_l, Ra, lit, Rc); ++} ++ ++// 0x10.50-0x10.57 EXT[0-7]B ++void Assembler::extlb(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_extlb, Ra, Rb, Rc); ++} ++ ++void Assembler::extlb(Register Ra, int lit, Register Rc) { ++ GenInstrI_SW(op_extlb_l, Ra, lit, Rc); ++} ++ ++void Assembler::extlh(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_extlh, Ra, Rb, Rc); ++} ++ ++void Assembler::extlh(Register Ra, int lit, Register Rc) { ++ GenInstrI_SW(op_extlh_l, Ra, lit, Rc); ++} ++ ++void Assembler::extlw(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_extlw, Ra, Rb, Rc); ++} ++ ++void Assembler::extlw(Register Ra, int lit, Register Rc) { ++ GenInstrI_SW(op_extlw_l, Ra, lit, Rc); ++} ++ ++void Assembler::extll(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_extll, Ra, Rb, Rc); ++} ++ ++void Assembler::extll(Register Ra, int lit, Register Rc) { ++ GenInstrI_SW(op_extll_l, Ra, lit, Rc); ++} ++ ++void Assembler::exthb(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_exthb, Ra, Rb, Rc); ++} ++ ++void Assembler::exthb(Register Ra, int lit, Register Rc) { ++ GenInstrI_SW(op_exthb_l, Ra, lit, Rc); ++} ++ ++void Assembler::exthh(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_exthh, Ra, Rb, Rc); ++} ++ ++void Assembler::exthh(Register Ra, int lit, Register Rc) { ++ GenInstrI_SW(op_exthh_l, Ra, lit, Rc); ++} ++ ++void Assembler::exthw(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_exthw, Ra, Rb, Rc); ++} ++ ++void Assembler::exthw(Register Ra, int lit, Register Rc) { ++ GenInstrI_SW(op_exthw_l, Ra, lit, Rc); ++} ++ ++void Assembler::exthl(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_exthl, Ra, Rb, Rc); ++} ++ ++void Assembler::exthl(Register Ra, int lit, Register Rc) { ++ GenInstrI_SW(op_exthl_l, Ra, lit, Rc); ++} ++ ++void Assembler::ctpop(Register Rb, Register Rc) { ++ DCHECK(Rb.is_valid() && Rc.is_valid()); ++ Instr instr = op_ctpop | Rb.code() << sRbShift | Rc.code() << sRcShift; ++ emit(instr); ++} ++ ++void Assembler::ctlz(Register Rb, Register Rc) { ++ DCHECK(Rb.is_valid() && Rc.is_valid()); ++ Instr instr = op_ctlz | Rb.code() << sRbShift | Rc.code() << sRcShift; ++ emit(instr); ++} ++ ++void Assembler::cttz(Register Rb, Register Rc) { ++ DCHECK(Rb.is_valid() && Rc.is_valid()); ++ Instr instr = op_cttz | Rb.code() << sRbShift | Rc.code() << sRcShift; ++ emit(instr); ++} ++ ++void Assembler::revbh(Register Rb, Register Rc) { ++ DCHECK(Rb.is_valid() && Rc.is_valid()); ++ Instr instr = op_revbh | Rb.code() << sRbShift | Rc.code() << sRcShift; ++ emit(instr); ++} ++ ++void Assembler::revbw(Register Rb, Register Rc) { ++ DCHECK(Rb.is_valid() && Rc.is_valid()); ++ Instr instr = op_revbw | Rb.code() << sRbShift | Rc.code() << sRcShift; ++ emit(instr); ++} ++ ++void Assembler::revbl(Register Rb, Register Rc) { ++ DCHECK(Rb.is_valid() && Rc.is_valid()); ++ Instr instr = op_revbl | Rb.code() << sRbShift | Rc.code() << sRcShift; ++ emit(instr); ++} ++ ++void Assembler::casw(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_casw, Ra, Rb, Rc); ++} ++ ++void Assembler::casl(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_casl, Ra, Rb, Rc); ++} ++ ++// 0x10.60-0x10.67 MASK[0-7]B ++void Assembler::masklb(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_masklb, Ra, Rb, Rc); ++} ++ ++void Assembler::masklb(Register Ra, int lit, Register Rc) { ++ GenInstrI_SW(op_masklb_l, Ra, lit, Rc); ++} ++ ++void Assembler::masklh(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_masklh, Ra, Rb, Rc); ++} ++ ++void Assembler::masklh(Register Ra, int lit, Register Rc) { ++ GenInstrI_SW(op_masklh_l, Ra, lit, Rc); ++} ++ ++void Assembler::masklw(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_masklw, Ra, Rb, Rc); ++} ++ ++void Assembler::masklw(Register Ra, int lit, Register Rc) { ++ GenInstrI_SW(op_masklw_l, Ra, lit, Rc); ++} ++ ++void Assembler::maskll(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_maskll, Ra, Rb, Rc); ++} ++ ++void Assembler::maskll(Register Ra, int lit, Register Rc) { ++ GenInstrI_SW(op_maskll_l, Ra, lit, Rc); ++} ++ ++void Assembler::maskhb(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_maskhb, Ra, Rb, Rc); ++} ++ ++void Assembler::maskhb(Register Ra, int lit, Register Rc) { ++ GenInstrI_SW(op_maskhb_l, Ra, lit, Rc); ++} ++ ++void Assembler::maskhh(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_maskhh, Ra, Rb, Rc); ++} ++ ++void Assembler::maskhh(Register Ra, int lit, Register Rc) { ++ GenInstrI_SW(op_maskhh_l, Ra, lit, Rc); ++} ++ ++void Assembler::maskhw(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_maskhw, Ra, Rb, Rc); ++} ++ ++void Assembler::maskhw(Register Ra, int lit, Register Rc) { ++ GenInstrI_SW(op_maskhw_l, Ra, lit, Rc); ++} ++ ++void Assembler::maskhl(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_maskhl, Ra, Rb, Rc); ++} ++ ++void Assembler::maskhl(Register Ra, int lit, Register Rc) { ++ GenInstrI_SW(op_maskhl_l, Ra, lit, Rc); ++} ++ ++void Assembler::zap(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_zap, Ra, Rb, Rc); ++} ++ ++void Assembler::zap(Register Ra, int lit, Register Rc) { ++ GenInstrI_SW(op_zap_l, Ra, lit, Rc); ++} ++ ++void Assembler::zapnot(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_zapnot, Ra, Rb, Rc); ++} ++ ++void Assembler::zapnot(Register Ra, int lit, Register Rc) { ++ GenInstrI_SW(op_zapnot_l, Ra, lit, Rc); ++} ++ ++void Assembler::sextb(Register Rb, Register Rc) { ++ DCHECK(Rb.is_valid() && Rc.is_valid()); ++ Instr instr = op_sextb | (Rb.code() << sRbShift) | (Rc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::sextb(int lit, Register Rc) { ++ DCHECK(is_uint8(lit) && Rc.is_valid()); ++ Instr instr = op_sextb_l | (lit & sImm8Mask) | (Rc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::sexth(Register Rb, Register Rc) { ++ DCHECK(Rb.is_valid() && Rc.is_valid()); ++ Instr instr = op_sexth | (Rb.code() << sRbShift) | (Rc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::sexth(int lit, Register Rc) { ++ DCHECK(is_uint8(lit) && Rc.is_valid()); ++ Instr instr = op_sexth_l | (lit & sImm8Mask) | (Rc.code() << sRcShift); ++ emit(instr); ++} ++ ++// 0x10.6c CMPGEB ++void Assembler::cmpgeb(Register Ra, Register Rb, Register Rc) { ++ GenInstrR_SW(op_cmpgeb, Ra, Rb, Rc); ++} ++ ++void Assembler::cmpgeb(Register Ra, int lit, Register Rc) { ++ GenInstrI_SW(op_cmpgeb_l, Ra, lit, Rc); ++} ++ ++// void Assembler::ftois(FloatRegister fa, Register Rc ) ++// void Assembler::ftoid(FloatRegister fa, Register Rc ) ++void Assembler::fimovs(FloatRegister fa, Register Rc) { ++ DCHECK(fa.is_valid() && Rc.is_valid()); ++ Instr instr = op_fimovs | (fa.code() << sRaShift) | (Rc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::fimovd(FloatRegister fa, Register Rc) { ++ DCHECK(fa.is_valid() && Rc.is_valid()); ++ Instr instr = op_fimovd | (fa.code() << sRaShift) | (Rc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::seleq(Register Ra, Register Rb, Register R3, Register Rc) { ++ GenInstrSelR_SW(op_seleq, Ra, Rb, R3, Rc); ++} ++ ++void Assembler::seleq(Register Ra, int lit, Register R3, Register Rc) { ++ GenInstrSelI_SW(op_seleq_l, Ra, lit, R3, Rc); ++} ++ ++void Assembler::selge(Register Ra, Register Rb, Register R3, Register Rc) { ++ GenInstrSelR_SW(op_selge, Ra, Rb, R3, Rc); ++} ++ ++void Assembler::selge(Register Ra, int lit, Register R3, Register Rc) { ++ GenInstrSelI_SW(op_selge_l, Ra, lit, R3, Rc); ++} ++ ++void Assembler::selgt(Register Ra, Register Rb, Register R3, Register Rc) { ++ GenInstrSelR_SW(op_selgt, Ra, Rb, R3, Rc); ++} ++ ++void Assembler::selgt(Register Ra, int lit, Register R3, Register Rc) { ++ GenInstrSelI_SW(op_selgt_l, Ra, lit, R3, Rc); ++} ++ ++void Assembler::selle(Register Ra, Register Rb, Register R3, Register Rc) { ++ GenInstrSelR_SW(op_selle, Ra, Rb, R3, Rc); ++} ++ ++void Assembler::selle(Register Ra, int lit, Register R3, Register Rc) { ++ GenInstrSelI_SW(op_selle_l, Ra, lit, R3, Rc); ++} ++ ++void Assembler::sellt(Register Ra, Register Rb, Register R3, Register Rc) { ++ GenInstrSelR_SW(op_sellt, Ra, Rb, R3, Rc); ++} ++ ++void Assembler::sellt(Register Ra, int lit, Register R3, Register Rc) { ++ GenInstrSelI_SW(op_sellt_l, Ra, lit, R3, Rc); ++} ++ ++void Assembler::selne(Register Ra, Register Rb, Register R3, Register Rc) { ++ GenInstrSelR_SW(op_selne, Ra, Rb, R3, Rc); ++} ++ ++void Assembler::selne(Register Ra, int lit, Register R3, Register Rc) { ++ GenInstrSelI_SW(op_selne_l, Ra, lit, R3, Rc); ++} ++ ++void Assembler::sellbc(Register Ra, Register Rb, Register R3, Register Rc) { ++ GenInstrSelR_SW(op_sellbc, Ra, Rb, R3, Rc); ++} ++ ++void Assembler::sellbc(Register Ra, int lit, Register R3, Register Rc) { ++ GenInstrSelI_SW(op_sellbc_l, Ra, lit, R3, Rc); ++} ++ ++void Assembler::sellbs(Register Ra, Register Rb, Register R3, Register Rc) { ++ GenInstrSelR_SW(op_sellbs, Ra, Rb, R3, Rc); ++} ++ ++void Assembler::sellbs(Register Ra, int lit, Register R3, Register Rc) { ++ GenInstrSelI_SW(op_sellbs_l, Ra, lit, R3, Rc); ++} ++ ++void Assembler::vlog(int vlog, FloatRegister fa, FloatRegister fb, ++ FloatRegister f3, FloatRegister fc) { ++ UNREACHABLE(); ++} ++ ++void Assembler::f_exclude_same_src_fc(Opcode_ops_fp opcode, FloatRegister fa, ++ FloatRegister fb, FloatRegister fc) { ++ if ((strcmp(FLAG_sw64_arch, "sw8a") == 0) || ++ CpuFeatures::IsSupported(SW_8A) || FLAG_sw8a_structure) { ++ GenInstrFR_SW(opcode, fa, fb, fc); ++ } else { ++ if (fa == fc || fb == fc) { ++ DCHECK(fa != kScratchDoubleReg && fb != kScratchDoubleReg); ++ GenInstrFR_SW(opcode, fa, fb, kScratchDoubleReg); ++ fmov(kScratchDoubleReg, fc); ++ } else { ++ GenInstrFR_SW(opcode, fa, fb, fc); ++ } ++ } ++} ++ ++void Assembler::f_exclude_same_src_fc(Opcode_ops_fp opcode, FloatRegister fb, ++ FloatRegister fc) { ++ if ((strcmp(FLAG_sw64_arch, "sw8a") == 0) || ++ CpuFeatures::IsSupported(SW_8A) || FLAG_sw8a_structure) { ++ GenInstrFR_SW(opcode, fb, fc); ++ } else { ++ if (fb == fc) { ++ DCHECK(fb != kScratchDoubleReg); ++ GenInstrFR_SW(opcode, fb, kScratchDoubleReg); ++ fmov(kScratchDoubleReg, fc); ++ } else { ++ GenInstrFR_SW(opcode, fb, fc); ++ } ++ } ++} ++ ++void Assembler::vbisw(FloatRegister fa, FloatRegister fb, FloatRegister fc) { ++ f_exclude_same_src_fc(op_vbisw, fa, fb, fc); ++} ++ ++void Assembler::vxorw(FloatRegister fa, FloatRegister fb, FloatRegister fc) { ++ f_exclude_same_src_fc(op_vxorw, fa, fb, fc); ++} ++ ++void Assembler::vandw(FloatRegister fa, FloatRegister fb, FloatRegister fc) { ++ f_exclude_same_src_fc(op_vandw, fa, fb, fc); ++} ++ ++void Assembler::veqvw(FloatRegister fa, FloatRegister fb, FloatRegister fc) { ++ f_exclude_same_src_fc(op_veqvw, fa, fb, fc); ++} ++ ++void Assembler::vornotw(FloatRegister fa, FloatRegister fb, FloatRegister fc) { ++ f_exclude_same_src_fc(op_vornotw, fa, fb, fc); ++} ++ ++void Assembler::vbicw(FloatRegister fa, FloatRegister fb, FloatRegister fc) { ++ f_exclude_same_src_fc(op_vbicw, fa, fb, fc); ++} ++ ++void Assembler::fadds(FloatRegister fa, FloatRegister fb, FloatRegister fc) { ++ f_exclude_same_src_fc(op_fadds, fa, fb, fc); ++} ++ ++void Assembler::faddd(FloatRegister fa, FloatRegister fb, FloatRegister fc) { ++ f_exclude_same_src_fc(op_faddd, fa, fb, fc); ++} ++ ++void Assembler::fsubs(FloatRegister fa, FloatRegister fb, FloatRegister fc) { ++ f_exclude_same_src_fc(op_fsubs, fa, fb, fc); ++} ++ ++void Assembler::fsubd(FloatRegister fa, FloatRegister fb, FloatRegister fc) { ++ f_exclude_same_src_fc(op_fsubd, fa, fb, fc); ++} ++ ++void Assembler::fmuls(FloatRegister fa, FloatRegister fb, FloatRegister fc) { ++ f_exclude_same_src_fc(op_fmuls, fa, fb, fc); ++} ++ ++void Assembler::fmuld(FloatRegister fa, FloatRegister fb, FloatRegister fc) { ++ f_exclude_same_src_fc(op_fmuld, fa, fb, fc); ++} ++ ++void Assembler::fdivs(FloatRegister fa, FloatRegister fb, FloatRegister fc) { ++ f_exclude_same_src_fc(op_fdivs, fa, fb, fc); ++} ++ ++void Assembler::fdivd(FloatRegister fa, FloatRegister fb, FloatRegister fc) { ++ f_exclude_same_src_fc(op_fdivd, fa, fb, fc); ++} ++ ++void Assembler::fsqrts(FloatRegister fb, FloatRegister fc) { ++ f_exclude_same_src_fc(op_fsqrts, fb, fc); ++} ++ ++void Assembler::fsqrtd(FloatRegister fb, FloatRegister fc) { ++ f_exclude_same_src_fc(op_fsqrtd, fb, fc); ++} ++ ++void Assembler::fcmpeq(FloatRegister fa, FloatRegister fb, FloatRegister fc) { ++ f_exclude_same_src_fc(op_fcmpeq, fa, fb, fc); ++} ++ ++void Assembler::fcmple(FloatRegister fa, FloatRegister fb, FloatRegister fc) { ++ f_exclude_same_src_fc(op_fcmple, fa, fb, fc); ++} ++ ++void Assembler::fcmplt(FloatRegister fa, FloatRegister fb, FloatRegister fc) { ++ f_exclude_same_src_fc(op_fcmplt, fa, fb, fc); ++} ++ ++void Assembler::fcmpun(FloatRegister fa, FloatRegister fb, FloatRegister fc) { ++ f_exclude_same_src_fc(op_fcmpun, fa, fb, fc); ++} ++ ++void Assembler::fcvtsd(FloatRegister fb, FloatRegister fc) { ++ f_exclude_same_src_fc(op_fcvtsd, fb, fc); ++} ++ ++void Assembler::fcvtds(FloatRegister fb, FloatRegister fc) { ++ f_exclude_same_src_fc(op_fcvtds, fb, fc); ++} ++ ++void Assembler::fcvtdl_g(FloatRegister fb, FloatRegister fc) { ++ f_exclude_same_src_fc(op_fcvtdl_g, fb, fc); ++} ++ ++void Assembler::fcvtdl_p(FloatRegister fb, FloatRegister fc) { ++ f_exclude_same_src_fc(op_fcvtdl_p, fb, fc); ++} ++ ++void Assembler::fcvtdl_z(FloatRegister fb, FloatRegister fc) { ++ f_exclude_same_src_fc(op_fcvtdl_z, fb, fc); ++} ++ ++void Assembler::fcvtdl_n(FloatRegister fb, FloatRegister fc) { ++ f_exclude_same_src_fc(op_fcvtdl_n, fb, fc); ++} ++ ++void Assembler::fcvtdl(FloatRegister fb, FloatRegister fc) { ++ f_exclude_same_src_fc(op_fcvtdl, fb, fc); ++} ++ ++void Assembler::fcvtwl(FloatRegister fb, FloatRegister fc) { ++ f_exclude_same_src_fc(op_fcvtwl, fb, fc); ++} ++ ++void Assembler::fcvtlw(FloatRegister fb, FloatRegister fc) { ++ f_exclude_same_src_fc(op_fcvtlw, fb, fc); ++} ++ ++void Assembler::fcvtls(FloatRegister fb, FloatRegister fc) { ++ f_exclude_same_src_fc(op_fcvtls, fb, fc); ++} ++ ++void Assembler::fcvtld(FloatRegister fb, FloatRegister fc) { ++ f_exclude_same_src_fc(op_fcvtld, fb, fc); ++} ++ ++void Assembler::fcpys(FloatRegister fa, FloatRegister fb, FloatRegister fc) { ++ GenInstrFR_SW(op_fcpys, fa, fb, fc); ++} ++ ++void Assembler::fcvtsh(FloatRegister Fb, FloatRegister Fc) { ++ DCHECK(Fb.is_valid() && Fc.is_valid()); ++ Instr instr = op_fcvtsh | (Fb.code() << sRbShift) | (Fc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::fcvths(FloatRegister Fb, FloatRegister Fc) { ++ DCHECK(Fb.is_valid() && Fc.is_valid()); ++ Instr instr = op_fcvths | (Fb.code() << sRbShift) | (Fc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::cmovdw(FloatRegister Fb, Register Rc) { ++ DCHECK(Fb.is_valid() && Rc.is_valid()); ++ Instr instr = op_cmovdw | (Fb.code() << sRbShift) | (Rc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::cmovdw_g(FloatRegister Fb, Register Rc) { ++ DCHECK(Fb.is_valid() && Rc.is_valid()); ++ Instr instr = op_cmovdw_g | (Fb.code() << sRbShift) | (Rc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::cmovdw_p(FloatRegister Fb, Register Rc) { ++ DCHECK(Fb.is_valid() && Rc.is_valid()); ++ Instr instr = op_cmovdw_p | (Fb.code() << sRbShift) | (Rc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::cmovdw_z(FloatRegister Fb, Register Rc) { ++ DCHECK(Fb.is_valid() && Rc.is_valid()); ++ Instr instr = op_cmovdw_z | (Fb.code() << sRbShift) | (Rc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::cmovdw_n(FloatRegister Fb, Register Rc) { ++ DCHECK(Fb.is_valid() && Rc.is_valid()); ++ Instr instr = op_cmovdw_n | (Fb.code() << sRbShift) | (Rc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::cmovdwu(FloatRegister Fb, Register Rc) { ++ DCHECK(Fb.is_valid() && Rc.is_valid()); ++ Instr instr = op_cmovdwu | (Fb.code() << sRbShift) | (Rc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::cmovdwu_g(FloatRegister Fb, Register Rc) { ++ DCHECK(Fb.is_valid() && Rc.is_valid()); ++ Instr instr = ++ op_cmovdwu_g | (Fb.code() << sRbShift) | (Rc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::cmovdwu_p(FloatRegister Fb, Register Rc) { ++ DCHECK(Fb.is_valid() && Rc.is_valid()); ++ Instr instr = ++ op_cmovdwu_p | (Fb.code() << sRbShift) | (Rc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::cmovdwu_z(FloatRegister Fb, Register Rc) { ++ DCHECK(Fb.is_valid() && Rc.is_valid()); ++ Instr instr = ++ op_cmovdwu_z | (Fb.code() << sRbShift) | (Rc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::cmovdwu_n(FloatRegister Fb, Register Rc) { ++ DCHECK(Fb.is_valid() && Rc.is_valid()); ++ Instr instr = ++ op_cmovdwu_n | (Fb.code() << sRbShift) | (Rc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::cmovdl(FloatRegister Fb, Register Rc) { ++ DCHECK(Fb.is_valid() && Rc.is_valid()); ++ Instr instr = op_cmovdl | (Fb.code() << sRbShift) | (Rc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::cmovdl_g(FloatRegister Fb, Register Rc) { ++ DCHECK(Fb.is_valid() && Rc.is_valid()); ++ Instr instr = op_cmovdl_g | (Fb.code() << sRbShift) | (Rc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::cmovdl_p(FloatRegister Fb, Register Rc) { ++ DCHECK(Fb.is_valid() && Rc.is_valid()); ++ Instr instr = op_cmovdl_p | (Fb.code() << sRbShift) | (Rc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::cmovdl_z(FloatRegister Fb, Register Rc) { ++ DCHECK(Fb.is_valid() && Rc.is_valid()); ++ Instr instr = op_cmovdl_z | (Fb.code() << sRbShift) | (Rc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::cmovdl_n(FloatRegister Fb, Register Rc) { ++ DCHECK(Fb.is_valid() && Rc.is_valid()); ++ Instr instr = op_cmovdl_n | (Fb.code() << sRbShift) | (Rc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::cmovdlu(FloatRegister Fb, Register Rc) { ++ DCHECK(Fb.is_valid() && Rc.is_valid()); ++ Instr instr = op_cmovdlu | (Fb.code() << sRbShift) | (Rc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::cmovdlu_g(FloatRegister Fb, Register Rc) { ++ DCHECK(Fb.is_valid() && Rc.is_valid()); ++ Instr instr = ++ op_cmovdlu_g | (Fb.code() << sRbShift) | (Rc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::cmovdlu_p(FloatRegister Fb, Register Rc) { ++ DCHECK(Fb.is_valid() && Rc.is_valid()); ++ Instr instr = ++ op_cmovdlu_p | (Fb.code() << sRbShift) | (Rc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::cmovdlu_z(FloatRegister Fb, Register Rc) { ++ DCHECK(Fb.is_valid() && Rc.is_valid()); ++ Instr instr = ++ op_cmovdlu_z | (Fb.code() << sRbShift) | (Rc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::cmovdlu_n(FloatRegister Fb, Register Rc) { ++ DCHECK(Fb.is_valid() && Rc.is_valid()); ++ Instr instr = ++ op_cmovdlu_n | (Fb.code() << sRbShift) | (Rc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::cmovls(Register Rb, FloatRegister Fc) { ++ DCHECK(Rb.is_valid() && Fc.is_valid()); ++ Instr instr = op_cmovls | (Rb.code() << sRbShift) | (Fc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::cmovld(Register Rb, FloatRegister Fc) { ++ DCHECK(Rb.is_valid() && Fc.is_valid()); ++ Instr instr = op_cmovld | (Rb.code() << sRbShift) | (Fc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::cmovuls(Register Rb, FloatRegister Fc) { ++ DCHECK(Rb.is_valid() && Fc.is_valid()); ++ Instr instr = op_cmovuls | (Rb.code() << sRbShift) | (Fc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::cmovuld(Register Rb, FloatRegister Fc) { ++ DCHECK(Rb.is_valid() && Fc.is_valid()); ++ Instr instr = op_cmovuld | (Rb.code() << sRbShift) | (Fc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::cmovws(Register Rb, FloatRegister Fc) { ++ DCHECK(Rb.is_valid() && Fc.is_valid()); ++ Instr instr = op_cmovws | (Rb.code() << sRbShift) | (Fc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::cmovwd(Register Rb, FloatRegister Fc) { ++ DCHECK(Rb.is_valid() && Fc.is_valid()); ++ Instr instr = op_cmovwd | (Rb.code() << sRbShift) | (Fc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::cmovuws(Register Rb, FloatRegister Fc) { ++ DCHECK(Rb.is_valid() && Fc.is_valid()); ++ Instr instr = op_cmovuws | (Rb.code() << sRbShift) | (Fc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::cmovuwd(Register Rb, FloatRegister Fc) { ++ DCHECK(Rb.is_valid() && Fc.is_valid()); ++ Instr instr = op_cmovuwd | (Rb.code() << sRbShift) | (Fc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::fcpyse(FloatRegister fa, FloatRegister fb, FloatRegister fc) { ++ GenInstrFR_SW(op_fcpyse, fa, fb, fc); ++} ++ ++void Assembler::fcpysn(FloatRegister fa, FloatRegister fb, FloatRegister fc) { ++ GenInstrFR_SW(op_fcpysn, fa, fb, fc); ++} ++ ++void Assembler::ifmovs(Register Ra, FloatRegister fc) { ++ DCHECK(Ra.is_valid() && fc.is_valid()); ++ Instr instr = op_ifmovs | (Ra.code() << sRaShift) | (fc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::ifmovd(Register Ra, FloatRegister fc) { ++ DCHECK(Ra.is_valid() && fc.is_valid()); ++ Instr instr = op_ifmovd | (Ra.code() << sRaShift) | (fc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::rfpcr(FloatRegister fa) { ++ DCHECK(fa.is_valid()); ++ Instr instr = op_rfpcr | (fa.code() << sRaShift); ++ emit(instr); ++} ++ ++void Assembler::wfpcr(FloatRegister fa) { ++ DCHECK(fa.is_valid()); ++ Instr instr = op_wfpcr | (fa.code() << sRaShift); ++ emit(instr); ++} ++ ++void Assembler::setfpec0() { ++ // Instr instr = op_setfpec0; ++ // emit(instr); ++} ++ ++void Assembler::setfpec1() { ++ // Instr instr = op_setfpec1; ++ // emit(instr); ++} ++ ++void Assembler::setfpec2() { ++ // Instr instr = op_setfpec2; ++ // emit(instr); ++} ++ ++void Assembler::setfpec3() { ++ // Instr instr = op_setfpec3; ++ // emit(instr); ++} ++ ++void Assembler::frecs(FloatRegister fa, FloatRegister fc) { ++ DCHECK(fa.is_valid() && fc.is_valid()); ++ Instr instr = op_frecs | (fa.code() << sRaShift) | (fc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::frecd(FloatRegister fa, FloatRegister fc) { ++ DCHECK(fa.is_valid() && fc.is_valid()); ++ Instr instr = op_frecd | (fa.code() << sRaShift) | (fc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::fris(FloatRegister fb, FloatRegister fc) { ++ f_exclude_same_src_fc(op_fris, fb, fc); ++} ++ ++void Assembler::fris_g(FloatRegister fb, FloatRegister fc) { ++ f_exclude_same_src_fc(op_fris_g, fb, fc); ++} ++ ++void Assembler::fris_p(FloatRegister fb, FloatRegister fc) { ++ f_exclude_same_src_fc(op_fris_p, fb, fc); ++} ++ ++void Assembler::fris_z(FloatRegister fb, FloatRegister fc) { ++ f_exclude_same_src_fc(op_fris_z, fb, fc); ++} ++ ++void Assembler::fris_n(FloatRegister fb, FloatRegister fc) { ++ f_exclude_same_src_fc(op_fris_n, fb, fc); ++} ++ ++void Assembler::frid(FloatRegister fb, FloatRegister fc) { ++ f_exclude_same_src_fc(op_frid, fb, fc); ++} ++ ++void Assembler::frid_g(FloatRegister fb, FloatRegister fc) { ++ f_exclude_same_src_fc(op_frid_g, fb, fc); ++} ++ ++void Assembler::frid_p(FloatRegister fb, FloatRegister fc) { ++ f_exclude_same_src_fc(op_frid_p, fb, fc); ++} ++ ++void Assembler::frid_z(FloatRegister fb, FloatRegister fc) { ++ f_exclude_same_src_fc(op_frid_z, fb, fc); ++} ++ ++void Assembler::frid_n(FloatRegister fb, FloatRegister fc) { ++ f_exclude_same_src_fc(op_frid_n, fb, fc); ++} ++ ++void Assembler::fmas(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc) { ++ GenInstrFMA_SW(op_fmas, fa, fb, f3, fc); ++} ++ ++void Assembler::fmad(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc) { ++ GenInstrFMA_SW(op_fmad, fa, fb, f3, fc); ++} ++ ++void Assembler::fmss(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc) { ++ GenInstrFMA_SW(op_fmss, fa, fb, f3, fc); ++} ++ ++void Assembler::fmsd(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc) { ++ GenInstrFMA_SW(op_fmsd, fa, fb, f3, fc); ++} ++ ++void Assembler::fnmas(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc) { ++ GenInstrFMA_SW(op_fnmas, fa, fb, f3, fc); ++} ++ ++void Assembler::fnmad(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc) { ++ GenInstrFMA_SW(op_fnmad, fa, fb, f3, fc); ++} ++ ++void Assembler::fnmss(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc) { ++ GenInstrFMA_SW(op_fnmss, fa, fb, f3, fc); ++} ++ ++void Assembler::fnmsd(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc) { ++ GenInstrFMA_SW(op_fnmsd, fa, fb, f3, fc); ++} ++ ++void Assembler::fseleq(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc) { ++ GenInstrFMA_SW(op_fseleq, fa, fb, f3, fc); ++} ++ ++void Assembler::fselne(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc) { ++ GenInstrFMA_SW(op_fselne, fa, fb, f3, fc); ++} ++ ++void Assembler::fsellt(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc) { ++ GenInstrFMA_SW(op_fsellt, fa, fb, f3, fc); ++} ++ ++void Assembler::fselle(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc) { ++ GenInstrFMA_SW(op_fselle, fa, fb, f3, fc); ++} ++ ++void Assembler::fselgt(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc) { ++ GenInstrFMA_SW(op_fselgt, fa, fb, f3, fc); ++} ++ ++void Assembler::fselge(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc) { ++ GenInstrFMA_SW(op_fselge, fa, fb, f3, fc); ++} ++ ++void Assembler::vaddw(FloatRegister fa, FloatRegister fb, FloatRegister fc) { ++ GenInstrFR_SW(op_vaddw, fa, fb, fc); ++} ++ ++void Assembler::vaddw(FloatRegister fa, int lit, FloatRegister fc) { ++ GenInstrFR_SW(op_vaddw_l, fa, lit, fc); ++} ++ ++void Assembler::vsubw(FloatRegister fa, FloatRegister fb, FloatRegister fc) { ++ GenInstrFR_SW(op_vsubw, fa, fb, fc); ++} ++ ++void Assembler::vsubw(FloatRegister fa, int lit, FloatRegister fc) { ++ GenInstrFR_SW(op_vsubw_l, fa, lit, fc); ++} ++ ++void Assembler::vcmpgew(FloatRegister fa, FloatRegister fb, FloatRegister fc) { ++ GenInstrFR_SW(op_vcmpgew, fa, fb, fc); ++} ++ ++void Assembler::vcmpgew(FloatRegister fa, int lit, FloatRegister fc) { ++ GenInstrFR_SW(op_vcmpgew_l, fa, lit, fc); ++} ++ ++void Assembler::vcmpeqw(FloatRegister fa, FloatRegister fb, FloatRegister fc) { ++ GenInstrFR_SW(op_vcmpeqw, fa, fb, fc); ++} ++ ++void Assembler::vcmpeqw(FloatRegister fa, int lit, FloatRegister fc) { ++ GenInstrFR_SW(op_vcmpeqw_l, fa, lit, fc); ++} ++ ++void Assembler::vcmplew(FloatRegister fa, FloatRegister fb, FloatRegister fc) { ++ GenInstrFR_SW(op_vcmplew, fa, fb, fc); ++} ++ ++void Assembler::vcmplew(FloatRegister fa, int lit, FloatRegister fc) { ++ GenInstrFR_SW(op_vcmplew_l, fa, lit, fc); ++} ++ ++void Assembler::vcmpltw(FloatRegister fa, FloatRegister fb, FloatRegister fc) { ++ GenInstrFR_SW(op_vcmpltw, fa, fb, fc); ++} ++ ++void Assembler::vcmpltw(FloatRegister fa, int lit, FloatRegister fc) { ++ GenInstrFR_SW(op_vcmpltw_l, fa, lit, fc); ++} ++ ++void Assembler::vcmpulew(FloatRegister fa, FloatRegister fb, FloatRegister fc) { ++ GenInstrFR_SW(op_vcmpulew, fa, fb, fc); ++} ++ ++void Assembler::vcmpulew(FloatRegister fa, int lit, FloatRegister fc) { ++ GenInstrFR_SW(op_vcmpulew_l, fa, lit, fc); ++} ++ ++void Assembler::vcmpultw(FloatRegister fa, FloatRegister fb, FloatRegister fc) { ++ GenInstrFR_SW(op_vcmpultw, fa, fb, fc); ++} ++ ++void Assembler::vcmpultw(FloatRegister fa, int lit, FloatRegister fc) { ++ GenInstrFR_SW(op_vcmpultw_l, fa, lit, fc); ++} ++ ++void Assembler::vsllw(FloatRegister fa, FloatRegister fb, FloatRegister fc) { ++ GenInstrFR_SW(op_vsllw, fa, fb, fc); ++} ++ ++void Assembler::vsllw(FloatRegister fa, int lit, FloatRegister fc) { ++ GenInstrFR_SW(op_vsllw_l, fa, lit, fc); ++} ++ ++void Assembler::vsrlw(FloatRegister fa, FloatRegister fb, FloatRegister fc) { ++ GenInstrFR_SW(op_vsrlw, fa, fb, fc); ++} ++ ++void Assembler::vsrlw(FloatRegister fa, int lit, FloatRegister fc) { ++ GenInstrFR_SW(op_vsrlw_l, fa, lit, fc); ++} ++ ++void Assembler::vsraw(FloatRegister fa, FloatRegister fb, FloatRegister fc) { ++ GenInstrFR_SW(op_vsraw, fa, fb, fc); ++} ++ ++void Assembler::vsraw(FloatRegister fa, int lit, FloatRegister fc) { ++ GenInstrFR_SW(op_vsraw_l, fa, lit, fc); ++} ++ ++void Assembler::vrolw(FloatRegister fa, FloatRegister fb, FloatRegister fc) { ++ GenInstrFR_SW(op_vrolw, fa, fb, fc); ++} ++ ++void Assembler::vrolw(FloatRegister fa, int lit, FloatRegister fc) { ++ GenInstrFR_SW(op_vrolw_l, fa, lit, fc); ++} ++ ++void Assembler::sllow(FloatRegister fa, FloatRegister fb, FloatRegister fc) { ++ GenInstrFR_SW(op_sllow, fa, fb, fc); ++} ++ ++void Assembler::sllow(FloatRegister fa, int lit, FloatRegister fc) { ++ GenInstrFR_SW(op_sllow_l, fa, lit, fc); ++} ++ ++void Assembler::srlow(FloatRegister fa, FloatRegister fb, FloatRegister fc) { ++ GenInstrFR_SW(op_srlow, fa, fb, fc); ++} ++ ++void Assembler::srlow(FloatRegister fa, int lit, FloatRegister fc) { ++ GenInstrFR_SW(op_srlow_l, fa, lit, fc); ++} ++ ++void Assembler::vaddl(FloatRegister fa, FloatRegister fb, FloatRegister fc) { ++ GenInstrFR_SW(op_vaddl, fa, fb, fc); ++} ++ ++void Assembler::vaddl(FloatRegister fa, int lit, FloatRegister fc) { ++ GenInstrFR_SW(op_vaddl_l, fa, lit, fc); ++} ++ ++void Assembler::vsubl(FloatRegister fa, FloatRegister fb, FloatRegister fc) { ++ GenInstrFR_SW(op_vsubl, fa, fb, fc); ++} ++ ++void Assembler::vsubl(FloatRegister fa, int lit, FloatRegister fc) { ++ GenInstrFR_SW(op_vsubl_l, fa, lit, fc); ++} ++ ++void Assembler::vsllb(FloatRegister fa, FloatRegister fb, FloatRegister fc) { ++ GenInstrFR_SW(op_vsllb, fa, fb, fc); ++} ++ ++void Assembler::vsllb(FloatRegister fa, int lit, FloatRegister fc) { ++ GenInstrFR_SW(op_vsllb_l, fa, lit, fc); ++} ++ ++void Assembler::vsrlb(FloatRegister fa, FloatRegister fb, FloatRegister fc) { ++ GenInstrFR_SW(op_vsrlb, fa, fb, fc); ++} ++ ++void Assembler::vsrlb(FloatRegister fa, int lit, FloatRegister fc) { ++ GenInstrFR_SW(op_vsrlb_l, fa, lit, fc); ++} ++ ++void Assembler::vsrab(FloatRegister fa, FloatRegister fb, FloatRegister fc) { ++ GenInstrFR_SW(op_vsrab, fa, fb, fc); ++} ++ ++void Assembler::vsrab(FloatRegister fa, int lit, FloatRegister fc) { ++ GenInstrFR_SW(op_vsrab_l, fa, lit, fc); ++} ++ ++void Assembler::vrolb(FloatRegister fa, FloatRegister fb, FloatRegister fc) { ++ GenInstrFR_SW(op_vrolb, fa, fb, fc); ++} ++ ++void Assembler::vrolb(FloatRegister fa, int lit, FloatRegister fc) { ++ GenInstrFR_SW(op_vrolb_l, fa, lit, fc); ++} ++ ++void Assembler::vsllh(FloatRegister fa, FloatRegister fb, FloatRegister fc) { ++ GenInstrFR_SW(op_vsllh, fa, fb, fc); ++} ++ ++void Assembler::vsllh(FloatRegister fa, int lit, FloatRegister fc) { ++ GenInstrFR_SW(op_vsllh_l, fa, lit, fc); ++} ++ ++void Assembler::vsrlh(FloatRegister fa, FloatRegister fb, FloatRegister fc) { ++ GenInstrFR_SW(op_vsrlh, fa, fb, fc); ++} ++ ++void Assembler::vsrlh(FloatRegister fa, int lit, FloatRegister fc) { ++ GenInstrFR_SW(op_vsrlh_l, fa, lit, fc); ++} ++ ++void Assembler::vsrah(FloatRegister fa, FloatRegister fb, FloatRegister fc) { ++ GenInstrFR_SW(op_vsrah, fa, fb, fc); ++} ++ ++void Assembler::vsrah(FloatRegister fa, int lit, FloatRegister fc) { ++ GenInstrFR_SW(op_vsrah_l, fa, lit, fc); ++} ++ ++void Assembler::vrolh(FloatRegister fa, FloatRegister fb, FloatRegister fc) { ++ GenInstrFR_SW(op_vrolh, fa, fb, fc); ++} ++ ++void Assembler::vrolh(FloatRegister fa, int lit, FloatRegister fc) { ++ GenInstrFR_SW(op_vrolh_l, fa, lit, fc); ++} ++ ++void Assembler::ctpopow(FloatRegister fa, FloatRegister fc) { ++ DCHECK(fa.is_valid() && fc.is_valid()); ++ Instr instr = op_ctpopow | (fa.code() << sRaShift) | (fc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::ctlzow(FloatRegister fa, FloatRegister fc) { ++ DCHECK(fa.is_valid() && fc.is_valid()); ++ Instr instr = op_ctlzow | (fa.code() << sRaShift) | (fc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::vslll(FloatRegister fa, FloatRegister fb, FloatRegister fc) { ++ GenInstrFR_SW(op_vslll, fa, fb, fc); ++} ++ ++void Assembler::vslll(FloatRegister fa, int lit, FloatRegister fc) { ++ GenInstrFR_SW(op_vslll_l, fa, lit, fc); ++} ++ ++void Assembler::vsrll(FloatRegister fa, FloatRegister fb, FloatRegister fc) { ++ GenInstrFR_SW(op_vsrll, fa, fb, fc); ++} ++ ++void Assembler::vsrll(FloatRegister fa, int lit, FloatRegister fc) { ++ GenInstrFR_SW(op_vsrll_l, fa, lit, fc); ++} ++ ++void Assembler::vsral(FloatRegister fa, FloatRegister fb, FloatRegister fc) { ++ GenInstrFR_SW(op_vsral, fa, fb, fc); ++} ++ ++void Assembler::vsral(FloatRegister fa, int lit, FloatRegister fc) { ++ GenInstrFR_SW(op_vsral_l, fa, lit, fc); ++} ++ ++void Assembler::vroll(FloatRegister fa, FloatRegister fb, FloatRegister fc) { ++ GenInstrFR_SW(op_vroll, fa, fb, fc); ++} ++ ++void Assembler::vroll(FloatRegister fa, int lit, FloatRegister fc) { ++ GenInstrFR_SW(op_vroll_l, fa, lit, fc); ++} ++ ++void Assembler::vmaxb(FloatRegister fa, FloatRegister fb, FloatRegister fc) { ++ GenInstrFR_SW(op_vmaxb, fa, fb, fc); ++} ++ ++void Assembler::vminb(FloatRegister fa, FloatRegister fb, FloatRegister fc) { ++ GenInstrFR_SW(op_vminb, fa, fb, fc); ++} ++ ++void Assembler::vmas(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc) { ++ GenInstrFMA_SW(op_vmas, fa, fb, f3, fc); ++} ++ ++void Assembler::vmad(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc) { ++ GenInstrFMA_SW(op_vmad, fa, fb, f3, fc); ++} ++ ++void Assembler::vmss(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc) { ++ GenInstrFMA_SW(op_vmss, fa, fb, f3, fc); ++} ++ ++void Assembler::vmsd(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc) { ++ GenInstrFMA_SW(op_vmsd, fa, fb, f3, fc); ++} ++ ++void Assembler::vnmas(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc) { ++ GenInstrFMA_SW(op_vnmas, fa, fb, f3, fc); ++} ++ ++void Assembler::vnmad(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc) { ++ GenInstrFMA_SW(op_vnmad, fa, fb, f3, fc); ++} ++ ++void Assembler::vnmss(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc) { ++ GenInstrFMA_SW(op_vnmss, fa, fb, f3, fc); ++} ++ ++void Assembler::vnmsd(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc) { ++ GenInstrFMA_SW(op_vnmsd, fa, fb, f3, fc); ++} ++ ++void Assembler::vfseleq(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc) { ++ GenInstrFMA_SW(op_vfseleq, fa, fb, f3, fc); ++} ++ ++void Assembler::vfsellt(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc) { ++ GenInstrFMA_SW(op_vfsellt, fa, fb, f3, fc); ++} ++ ++void Assembler::vfselle(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc) { ++ GenInstrFMA_SW(op_vfselle, fa, fb, f3, fc); ++} ++ ++void Assembler::vseleqw(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc) { ++ GenInstrFMA_SW(op_vseleqw, fa, fb, f3, fc); ++} ++ ++void Assembler::vseleqw(FloatRegister fa, FloatRegister fb, int fmalit, ++ FloatRegister fc) { ++ GenInstrFMA_SW(op_vseleqw_l, fa, fb, fmalit, fc); ++} ++ ++void Assembler::vsellbcw(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc) { ++ GenInstrFMA_SW(op_vsellbcw, fa, fb, f3, fc); ++} ++ ++void Assembler::vsellbcw(FloatRegister fa, FloatRegister fb, int fmalit, ++ FloatRegister fc) { ++ GenInstrFMA_SW(op_vsellbcw_l, fa, fb, fmalit, fc); ++} ++ ++void Assembler::vselltw(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc) { ++ GenInstrFMA_SW(op_vselltw, fa, fb, f3, fc); ++} ++ ++void Assembler::vselltw(FloatRegister fa, FloatRegister fb, int fmalit, ++ FloatRegister fc) { ++ GenInstrFMA_SW(op_vselltw_l, fa, fb, fmalit, fc); ++} ++ ++void Assembler::vsellew(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc) { ++ GenInstrFMA_SW(op_vsellew, fa, fb, f3, fc); ++} ++ ++void Assembler::vsellew(FloatRegister fa, FloatRegister fb, int fmalit, ++ FloatRegister fc) { ++ GenInstrFMA_SW(op_vsellew_l, fa, fb, fmalit, fc); ++} ++ ++void Assembler::vinsw(FloatRegister fa, FloatRegister fb, int fmalit, ++ FloatRegister fc) { ++ GenInstrFMA_SW(op_vinsw_l, fa, fb, fmalit, fc); ++} ++ ++void Assembler::vinsf(FloatRegister fa, FloatRegister fb, int fmalit, ++ FloatRegister fc) { ++ GenInstrFMA_SW(op_vinsf_l, fa, fb, fmalit, fc); ++} ++ ++void Assembler::vextw(FloatRegister fa, int fmalit, FloatRegister fc) { ++ GenInstrFMA_SW(op_vextw_l, fa, fmalit, fc); ++} ++ ++void Assembler::vextf(FloatRegister fa, int fmalit, FloatRegister fc) { ++ GenInstrFMA_SW(op_vextf_l, fa, fmalit, fc); ++} ++ ++void Assembler::vcpyw(FloatRegister fa, FloatRegister fc) { ++ DCHECK(fa.is_valid() && fc.is_valid()); ++ Instr instr = op_vcpyw | (fa.code() << sRaShift) | (fc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::vcpyf(FloatRegister fa, FloatRegister fc) { ++ DCHECK(fa.is_valid() && fc.is_valid()); ++ Instr instr = op_vcpyf | (fa.code() << sRaShift) | (fc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::vconw(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc) { ++ GenInstrFMA_SW(op_vconw, fa, fb, f3, fc); ++} ++ ++void Assembler::vshfw(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc) { ++ GenInstrFMA_SW(op_vshfw, fa, fb, f3, fc); ++} ++ ++void Assembler::vcons(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc) { ++ GenInstrFMA_SW(op_vcons, fa, fb, f3, fc); ++} ++ ++void Assembler::vcond(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc) { ++ GenInstrFMA_SW(op_vcond, fa, fb, f3, fc); ++} ++ ++void Assembler::vinsb(FloatRegister fa, FloatRegister fb, int fmalit, ++ FloatRegister fc) { ++ GenInstrFMA_SW(op_vinsb_l, fa, fb, fmalit, fc); ++} ++ ++void Assembler::vinsh(FloatRegister fa, FloatRegister fb, int fmalit, ++ FloatRegister fc) { ++ GenInstrFMA_SW(op_vinsh_l, fa, fb, fmalit, fc); ++} ++ ++void Assembler::vinsectlh(FloatRegister fa, FloatRegister fb, ++ FloatRegister fc) { ++ GenInstrFMA_SW(op_vinsectlh, fa, fb, fc); ++} ++ ++void Assembler::vinsectlw(FloatRegister fa, FloatRegister fb, ++ FloatRegister fc) { ++ GenInstrFMA_SW(op_vinsectlw, fa, fb, fc); ++} ++ ++void Assembler::vinsectll(FloatRegister fa, FloatRegister fb, ++ FloatRegister fc) { ++ GenInstrFMA_SW(op_vinsectll, fa, fb, fc); ++} ++ ++void Assembler::vinsectlb(FloatRegister fa, FloatRegister fb, ++ FloatRegister fc) { ++ GenInstrFMA_SW(op_vinsectlb, fa, fb, fc); ++} ++ ++void Assembler::vshfq(FloatRegister fa, FloatRegister fb, int fmalit, ++ FloatRegister fc) { ++ GenInstrFMA_SW(op_vshfq_l, fa, fb, fmalit, fc); ++} ++ ++void Assembler::vshfqb(FloatRegister fa, FloatRegister fb, FloatRegister fc) { ++ GenInstrFMA_SW(op_vshfqb, fa, fb, fc); ++} ++ ++void Assembler::vcpyb(FloatRegister fa, FloatRegister fc) { ++ DCHECK(fa.is_valid() && fc.is_valid()); ++ Instr instr = op_vcpyb | (fa.code() << sRaShift) | (fc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::vcpyh(FloatRegister fa, FloatRegister fc) { ++ DCHECK(fa.is_valid() && fc.is_valid()); ++ Instr instr = op_vcpyh | (fa.code() << sRaShift) | (fc.code() << sRcShift); ++ emit(instr); ++} ++ ++void Assembler::vsm3r(FloatRegister fa, FloatRegister fb, int fmalit, ++ FloatRegister fc) { ++ GenInstrFMA_SW(op_vsm3r_l, fa, fb, fmalit, fc); ++} ++ ++void Assembler::vfcvtsh(FloatRegister fa, FloatRegister fb, int fmalit, ++ FloatRegister fc) { ++ GenInstrFR_SW(op_vfcvtsh_l, fa, fb, fmalit, fc); ++} ++ ++void Assembler::vfcvths(FloatRegister fa, FloatRegister fb, int fmalit, ++ FloatRegister fc) { ++ GenInstrFR_SW(op_vfcvths_l, fa, fb, fmalit, fc); ++} ++ ++void Assembler::vldw_u(FloatRegister fa, int atmdisp, Register Rb) { ++ GenInstrSWSA_SW(op_vldw_u, fa, atmdisp, Rb); ++} ++ ++void Assembler::vstw_u(FloatRegister fa, int atmdisp, Register Rb) { ++ GenInstrSWSA_SW(op_vstw_u, fa, atmdisp, Rb); ++} ++ ++void Assembler::vlds_u(FloatRegister fa, int atmdisp, Register Rb) { ++ GenInstrSWSA_SW(op_vlds_u, fa, atmdisp, Rb); ++} ++ ++void Assembler::vsts_u(FloatRegister fa, int atmdisp, Register Rb) { ++ GenInstrSWSA_SW(op_vsts_u, fa, atmdisp, Rb); ++} ++ ++void Assembler::vldd_u(FloatRegister fa, int atmdisp, Register Rb) { ++ GenInstrSWSA_SW(op_vldd_u, fa, atmdisp, Rb); ++} ++ ++void Assembler::vstd_u(FloatRegister fa, int atmdisp, Register Rb) { ++ GenInstrSWSA_SW(op_vstd_u, fa, atmdisp, Rb); ++} ++ ++void Assembler::vstw_ul(FloatRegister fa, int atmdisp, Register Rb) { ++ GenInstrSWSA_SW(op_vstw_ul, fa, atmdisp, Rb); ++} ++ ++void Assembler::vstw_uh(FloatRegister fa, int atmdisp, Register Rb) { ++ GenInstrSWSA_SW(op_vstw_uh, fa, atmdisp, Rb); ++} ++ ++void Assembler::vsts_ul(FloatRegister fa, int atmdisp, Register Rb) { ++ GenInstrSWSA_SW(op_vsts_ul, fa, atmdisp, Rb); ++} ++ ++void Assembler::vsts_uh(FloatRegister fa, int atmdisp, Register Rb) { ++ GenInstrSWSA_SW(op_vsts_uh, fa, atmdisp, Rb); ++} ++ ++void Assembler::vstd_ul(FloatRegister fa, int atmdisp, Register Rb) { ++ GenInstrSWSA_SW(op_vstd_ul, fa, atmdisp, Rb); ++} ++ ++void Assembler::vstd_uh(FloatRegister fa, int atmdisp, Register Rb) { ++ GenInstrSWSA_SW(op_vstd_uh, fa, atmdisp, Rb); ++} ++ ++void Assembler::lbr(int palfn) { ++ DCHECK(is_int26(palfn)); ++ Instr instr = op_sys_call | (palfn & ((1 << 26) - 1)); ++ emit(instr); ++} ++ ++void Assembler::ldbu_a(Register Ra, int atmdisp, Register Rb) { ++ GenInstrMWithFun_SW(op_ldbu_a, Ra, atmdisp, Rb); ++} ++ ++void Assembler::ldhu_a(Register Ra, int atmdisp, Register Rb) { ++ GenInstrMWithFun_SW(op_ldhu_a, Ra, atmdisp, Rb); ++} ++ ++void Assembler::ldw_a(Register Ra, int atmdisp, Register Rb) { ++ GenInstrMWithFun_SW(op_ldw_a, Ra, atmdisp, Rb); ++} ++ ++void Assembler::ldl_a(Register Ra, int atmdisp, Register Rb) { ++ GenInstrMWithFun_SW(op_ldl_a, Ra, atmdisp, Rb); ++} ++ ++void Assembler::stb_a(Register Ra, int atmdisp, Register Rb) { ++ GenInstrMWithFun_SW(op_stb_a, Ra, atmdisp, Rb); ++} ++ ++void Assembler::sth_a(Register Ra, int atmdisp, Register Rb) { ++ GenInstrMWithFun_SW(op_sth_a, Ra, atmdisp, Rb); ++} ++ ++void Assembler::stw_a(Register Ra, int atmdisp, Register Rb) { ++ GenInstrMWithFun_SW(op_stw_a, Ra, atmdisp, Rb); ++} ++ ++void Assembler::stl_a(Register Ra, int atmdisp, Register Rb) { ++ GenInstrMWithFun_SW(op_stl_a, Ra, atmdisp, Rb); ++} ++ ++void Assembler::flds_a(FloatRegister fa, int atmdisp, Register Rb) { ++ GenInstrSWSA_SW(op_flds_a, fa, atmdisp, Rb); ++} ++ ++void Assembler::fldd_a(FloatRegister fa, int atmdisp, Register Rb) { ++ GenInstrSWSA_SW(op_fldd_a, fa, atmdisp, Rb); ++} ++ ++void Assembler::fsts_a(FloatRegister fa, int atmdisp, Register Rb) { ++ GenInstrSWSA_SW(op_fsts_a, fa, atmdisp, Rb); ++} ++ ++void Assembler::fstd_a(FloatRegister fa, int atmdisp, Register Rb) { ++ GenInstrSWSA_SW(op_fstd_a, fa, atmdisp, Rb); ++} ++ ++void Assembler::dpfhr(int th, int atmdisp, Register Rb) { ++ DCHECK(is_uint5(th) && is_uint11(atmdisp) && Rb.is_valid()); ++ Instr instr = op_dpfhr | ((th << sRaShift) & sRaFieldMask) | ++ ((atmdisp << sImm11Shift) & sImm11Mask) | ++ (Rb.code() << sRbShift); ++ emit(instr); ++} ++ ++void Assembler::dpfhw(int th, int atmdisp, Register Rb) { ++ DCHECK(is_uint5(th) && is_uint11(atmdisp) && Rb.is_valid()); ++ Instr instr = op_dpfhw | ((th << sRaShift) & sRaFieldMask) | ++ ((atmdisp << sImm11Shift) & sImm11Mask) | ++ (Rb.code() << sRbShift); ++ emit(instr); ++} ++ ++// 0x1A.00-0x1c.E SIMD instructions. ++ ++void Assembler::ldbu(Register Ra, int mdisp, Register Rb) { ++ GenInstrM_SW(op_ldbu, Ra, mdisp, Rb); ++} ++ ++void Assembler::ldhu(Register Ra, int mdisp, Register Rb) { ++ GenInstrM_SW(op_ldhu, Ra, mdisp, Rb); ++} ++ ++void Assembler::ldw(Register Ra, int mdisp, Register Rb) { ++ GenInstrM_SW(op_ldw, Ra, mdisp, Rb); ++} ++ ++void Assembler::ldl(Register Ra, int mdisp, Register Rb) { ++ GenInstrM_SW(op_ldl, Ra, mdisp, Rb); ++} ++ ++void Assembler::ldl_u(Register Ra, int mdisp, Register Rb) { ++ GenInstrM_SW(op_ldl_u, Ra, mdisp, Rb); ++} ++ ++void Assembler::pri_ld(Register Ra, int ev6hwdisp, Register Rb) { ++ UNREACHABLE(); ++} ++ ++void Assembler::flds(FloatRegister fa, int mdisp, Register Rb) { ++ GenInstrFM_SW(op_flds, fa, mdisp, Rb); ++} ++ ++void Assembler::fldd(FloatRegister fa, int mdisp, Register Rb) { ++ GenInstrFM_SW(op_fldd, fa, mdisp, Rb); ++} ++ ++void Assembler::stb(Register Ra, int mdisp, Register Rb) { ++ GenInstrM_SW(op_stb, Ra, mdisp, Rb); ++} ++ ++void Assembler::sth(Register Ra, int mdisp, Register Rb) { ++ GenInstrM_SW(op_sth, Ra, mdisp, Rb); ++} ++ ++void Assembler::stw(Register Ra, int mdisp, Register Rb) { ++ GenInstrM_SW(op_stw, Ra, mdisp, Rb); ++} ++ ++void Assembler::stl(Register Ra, int mdisp, Register Rb) { ++ GenInstrM_SW(op_stl, Ra, mdisp, Rb); ++} ++ ++void Assembler::stl_u(Register Ra, int mdisp, Register Rb) { ++ GenInstrM_SW(op_stl_u, Ra, mdisp, Rb); ++} ++ ++void Assembler::pri_st(Register Ra, int ev6hwdisp, Register Rb) { ++ UNREACHABLE(); ++} ++ ++void Assembler::fsts(FloatRegister fa, int mdisp, Register Rb) { ++ GenInstrFM_SW(op_fsts, fa, mdisp, Rb); ++} ++ ++void Assembler::fstd(FloatRegister fa, int mdisp, Register Rb) { ++ GenInstrFM_SW(op_fstd, fa, mdisp, Rb); ++} ++ ++void Assembler::beq(Register Ra, int bdisp) { ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ GenInstrB_SW(op_beq, Ra, bdisp); ++} ++ ++void Assembler::bne(Register Ra, int bdisp) { ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ GenInstrB_SW(op_bne, Ra, bdisp); ++} ++ ++void Assembler::blt(Register Ra, int bdisp) { ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ GenInstrB_SW(op_blt, Ra, bdisp); ++} ++ ++void Assembler::ble(Register Ra, int bdisp) { ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ GenInstrB_SW(op_ble, Ra, bdisp); ++} ++ ++void Assembler::bgt(Register Ra, int bdisp) { ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ GenInstrB_SW(op_bgt, Ra, bdisp); ++} ++ ++void Assembler::bge(Register Ra, int bdisp) { ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ GenInstrB_SW(op_bge, Ra, bdisp); ++} ++ ++void Assembler::blbc(Register Ra, int bdisp) { ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ GenInstrB_SW(op_blbc, Ra, bdisp); ++} ++ ++void Assembler::blbs(Register Ra, int bdisp) { ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ GenInstrB_SW(op_blbs, Ra, bdisp); ++} ++ ++void Assembler::fbeq(FloatRegister fa, int bdisp) { ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ GenInstrFB_SW(op_fbeq, fa, bdisp); ++} ++ ++void Assembler::fbne(FloatRegister fa, int bdisp) { ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ GenInstrFB_SW(op_fbne, fa, bdisp); ++} ++ ++void Assembler::fblt(FloatRegister fa, int bdisp) { ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ GenInstrFB_SW(op_fblt, fa, bdisp); ++} ++ ++void Assembler::fble(FloatRegister fa, int bdisp) { ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ GenInstrFB_SW(op_fble, fa, bdisp); ++} ++ ++void Assembler::fbgt(FloatRegister fa, int bdisp) { ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ GenInstrFB_SW(op_fbgt, fa, bdisp); ++} ++ ++void Assembler::fbge(FloatRegister fa, int bdisp) { ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ GenInstrFB_SW(op_fbge, fa, bdisp); ++} ++ ++void Assembler::ldi(Register Ra, int mdisp, Register Rb) { ++ GenInstrM_SW(op_ldi, Ra, mdisp, Rb); ++} ++ ++void Assembler::ldih(Register Ra, int mdisp, Register Rb) { ++ GenInstrM_SW(op_ldih, Ra, mdisp, Rb); ++} ++ ++// cache control instruction ++void Assembler::s_fillcs(int mdisp, Register Rb) { ldw(zero_reg, mdisp, Rb); } ++ ++void Assembler::s_fillde(int mdisp, Register Rb) { ldl(zero_reg, mdisp, Rb); } ++ ++void Assembler::fillde(int mdisp, Register Rb) { flds(f31, mdisp, Rb); } ++ ++void Assembler::fillde_e(int mdisp, Register Rb) { fldd(f31, mdisp, Rb); } ++ ++void Assembler::fillcs(int mdisp, Register Rb) { ldwe(f31, mdisp, Rb); } ++ ++void Assembler::fillcs_e(int mdisp, Register Rb) { ldde(f31, mdisp, Rb); } ++ ++void Assembler::e_fillcs(int mdisp, Register Rb) { ldse(f31, mdisp, Rb); } ++ ++void Assembler::e_fillde(int mdisp, Register Rb) { ++ vlds(f31 /*V31*/, mdisp, Rb); ++} ++ ++void Assembler::flushd(int mdisp, Register Rb) { ldbu(zero_reg, mdisp, Rb); } ++ ++void Assembler::evictdl(int mdisp, Register Rb) { ldl_u(zero_reg, mdisp, Rb); } ++ ++void Assembler::evictdg(int mdisp, Register Rb) { ldhu(zero_reg, mdisp, Rb); } ++ ++void Assembler::Ldb(Register Ra, const MemOperand& rs) { // sw add ++ ldbu(Ra, rs); ++ sextb(Ra, Ra); ++} ++ ++// Helper for base-reg + offset, when offset is larger than int16. ++void Assembler::SwLoadRegPlusOffsetToAt(const MemOperand& src) { ++ DCHECK(src.rm() != at); ++ DCHECK(is_int32(src.offset_)); ++ ++ int16_t lo_offset = static_cast(src.offset_); ++ int16_t hi_offset = (src.offset_ - (int16_t)src.offset_) >> 16; ++ if (((int32_t)hi_offset == -32768) && ((int32_t)lo_offset < 0)) { ++ // range from 0x7FFF8000 to 0x7FFFFFFF ++ ldih(at, 0x4000, zero_reg); ++ ldih(at, 0x4000, at); ++ if (lo_offset != 0) ldi(at, lo_offset, at); ++ } else { ++ ldih(at, hi_offset, zero_reg); ++ if (lo_offset != 0) ldi(at, lo_offset, at); ++ } ++ addl(src.rm(), at, at); // Add base register. ++} ++ ++void Assembler::ldbu(Register Ra, const MemOperand& rs) { ++ if (is_int16(rs.offset_)) { ++ GenInstrM_SW(op_ldbu, Ra, rs.offset_, rs.rm()); ++ } else { // Offset > 16 bits, use multiple instructions to load. ++ SwLoadRegPlusOffsetToAt(rs); ++ GenInstrM_SW(op_ldbu, Ra, 0, at); ++ } ++} ++ ++void Assembler::Ldh(Register Ra, const MemOperand& rs) { // sw add ++ ldhu(Ra, rs); ++ sexth(Ra, Ra); ++} ++ ++void Assembler::ldhu(Register Ra, const MemOperand& rs) { ++ if (is_int16(rs.offset_)) { ++ GenInstrM_SW(op_ldhu, Ra, rs.offset_, rs.rm()); ++ } else { // Offset > 16 bits, use multiple instructions to load. ++ SwLoadRegPlusOffsetToAt(rs); ++ GenInstrM_SW(op_ldhu, Ra, 0, at); ++ } ++} ++ ++void Assembler::ldw(Register Ra, const MemOperand& rs) { ++ if (is_int16(rs.offset_)) { ++ GenInstrM_SW(op_ldw, Ra, rs.offset_, rs.rm()); ++ } else { // Offset > 16 bits, use multiple instructions to load. ++ SwLoadRegPlusOffsetToAt(rs); ++ GenInstrM_SW(op_ldw, Ra, 0, at); // Equiv to ldw(rd, MemOperand(at, 0)); ++ } ++} ++ ++void Assembler::Ldwu(Register Ra, const MemOperand& rs) { // sw add ++ ldw(Ra, rs); ++ zapnot(Ra, 0xf, Ra); ++} ++ ++void Assembler::ldl(Register Ra, const MemOperand& rs) { ++ if (is_int16(rs.offset_)) { ++ GenInstrM_SW(op_ldl, Ra, rs.offset_, rs.rm()); ++ } else { // Offset > 16 bits, use multiple instructions to load. ++ SwLoadRegPlusOffsetToAt(rs); ++ GenInstrM_SW(op_ldl, Ra, 0, at); ++ } ++} ++ ++void Assembler::flds(FloatRegister fa, const MemOperand& rs) { ++ if (is_int16(rs.offset_)) { ++ GenInstrFM_SW(op_flds, fa, rs.offset_, rs.rm()); ++ } else { // Offset > 16 bits, use multiple instructions to load. ++ SwLoadRegPlusOffsetToAt(rs); ++ GenInstrFM_SW(op_flds, fa, 0, at); ++ } ++} ++ ++void Assembler::fldd(FloatRegister fa, const MemOperand& rs) { ++ if (is_int16(rs.offset_)) { ++ GenInstrFM_SW(op_fldd, fa, rs.offset_, rs.rm()); ++ } else { // Offset > 16 bits, use multiple instructions to load. ++ SwLoadRegPlusOffsetToAt(rs); ++ GenInstrFM_SW(op_fldd, fa, 0, at); ++ } ++} ++ ++void Assembler::stb(Register Ra, const MemOperand& rs) { ++ if (is_int16(rs.offset_)) { ++ GenInstrM_SW(op_stb, Ra, rs.offset_, rs.rm()); ++ } else { // Offset > 16 bits, use multiple instructions to load. ++ SwLoadRegPlusOffsetToAt(rs); ++ GenInstrM_SW(op_stb, Ra, 0, at); ++ } ++} ++ ++void Assembler::sth(Register Ra, const MemOperand& rs) { ++ if (is_int16(rs.offset_)) { ++ GenInstrM_SW(op_sth, Ra, rs.offset_, rs.rm()); ++ } else { // Offset > 16 bits, use multiple instructions to load. ++ SwLoadRegPlusOffsetToAt(rs); ++ GenInstrM_SW(op_sth, Ra, 0, at); ++ } ++} ++ ++void Assembler::stw(Register Ra, const MemOperand& rs) { ++ if (is_int16(rs.offset_)) { ++ GenInstrM_SW(op_stw, Ra, rs.offset_, rs.rm()); ++ } else { // Offset > 16 bits, use multiple instructions to load. ++ SwLoadRegPlusOffsetToAt(rs); ++ GenInstrM_SW(op_stw, Ra, 0, at); ++ } ++} ++ ++void Assembler::stl(Register Ra, const MemOperand& rs) { ++ if (is_int16(rs.offset_)) { ++ GenInstrM_SW(op_stl, Ra, rs.offset_, rs.rm()); ++ } else { // Offset > 16 bits, use multiple instructions to load. ++ SwLoadRegPlusOffsetToAt(rs); ++ GenInstrM_SW(op_stl, Ra, 0, at); ++ } ++} ++ ++void Assembler::fsts(FloatRegister fa, const MemOperand& rs) { ++ if (is_int16(rs.offset_)) { ++ GenInstrFM_SW(op_fsts, fa, rs.offset_, rs.rm()); ++ } else { // Offset > 16 bits, use multiple instructions to load. ++ SwLoadRegPlusOffsetToAt(rs); ++ GenInstrFM_SW(op_fsts, fa, 0, at); ++ } ++} ++ ++void Assembler::fstd(FloatRegister fa, const MemOperand& rs) { ++ if (is_int16(rs.offset_)) { ++ GenInstrFM_SW(op_fstd, fa, rs.offset_, rs.rm()); ++ } else { // Offset > 16 bits, use multiple instructions to load. ++ SwLoadRegPlusOffsetToAt(rs); ++ GenInstrFM_SW(op_fstd, fa, 0, at); ++ } ++} ++ ++int Assembler::RelocateInternalReference(RelocInfo::Mode rmode, Address pc, ++ intptr_t pc_delta) { ++ if (RelocInfo::IsInternalReference(rmode)) { ++ int64_t* p = reinterpret_cast(pc); ++ if (*p == kEndOfJumpChain) { ++ return 0; // Number of instructions patched. ++ } ++ *p += pc_delta; ++ return 2; // Number of instructions patched. ++ } ++ return 0; ++} ++ ++void Assembler::GrowBuffer() { ++ // Compute new buffer size. ++ int old_size = buffer_->size(); ++ int new_size = std::min(2 * old_size, old_size + 1 * MB); ++ ++ // Some internal data structures overflow for very large buffers, ++ // they must ensure that kMaximalBufferSize is not too large. ++ if (new_size > kMaximalBufferSize) { ++ V8::FatalProcessOutOfMemory(nullptr, "Assembler::GrowBuffer"); ++ } ++ ++ // Set up new buffer. ++ std::unique_ptr new_buffer = buffer_->Grow(new_size); ++ DCHECK_EQ(new_size, new_buffer->size()); ++ byte* new_start = new_buffer->start(); ++ ++ // Copy the data. ++ intptr_t pc_delta = new_start - buffer_start_; ++ intptr_t rc_delta = (new_start + new_size) - (buffer_start_ + old_size); ++ size_t reloc_size = (buffer_start_ + old_size) - reloc_info_writer.pos(); ++ MemMove(new_start, buffer_start_, pc_offset()); ++ MemMove(reloc_info_writer.pos() + rc_delta, reloc_info_writer.pos(), ++ reloc_size); ++ ++ // Switch buffers. ++ buffer_ = std::move(new_buffer); ++ buffer_start_ = new_start; ++ pc_ += pc_delta; ++ pc_for_safepoint_ += pc_delta; ++ reloc_info_writer.Reposition(reloc_info_writer.pos() + rc_delta, ++ reloc_info_writer.last_pc() + pc_delta); ++ ++ // Relocate runtime entries. ++ base::Vector instructions{buffer_start_, ++ static_cast(pc_offset())}; ++ base::Vector reloc_info{reloc_info_writer.pos(), reloc_size}; ++ for (RelocIterator it(instructions, reloc_info, 0); !it.done(); it.next()) { ++ RelocInfo::Mode rmode = it.rinfo()->rmode(); ++ if (rmode == RelocInfo::INTERNAL_REFERENCE) { ++ RelocateInternalReference(rmode, it.rinfo()->pc(), pc_delta); ++ } ++ } ++ ++ DCHECK(!overflow()); ++} ++ ++void Assembler::db(uint8_t data) { ++ CheckForEmitInForbiddenSlot(); ++ *reinterpret_cast(pc_) = data; ++ pc_ += sizeof(uint8_t); ++} ++ ++void Assembler::dd(uint32_t data, RelocInfo::Mode rmode) { ++ CheckForEmitInForbiddenSlot(); ++ if (!RelocInfo::IsNoInfo(rmode)) { ++ DCHECK(RelocInfo::IsDataEmbeddedObject(rmode) || ++ RelocInfo::IsLiteralConstant(rmode)); ++ RecordRelocInfo(rmode); ++ } ++ *reinterpret_cast(pc_) = data; ++ pc_ += sizeof(uint32_t); ++} ++ ++void Assembler::dq(uint64_t data, RelocInfo::Mode rmode) { ++ CheckForEmitInForbiddenSlot(); ++ if (!RelocInfo::IsNoInfo(rmode)) { ++ DCHECK(RelocInfo::IsDataEmbeddedObject(rmode) || ++ RelocInfo::IsLiteralConstant(rmode)); ++ RecordRelocInfo(rmode); ++ } ++ *reinterpret_cast(pc_) = data; ++ pc_ += sizeof(uint64_t); ++} ++ ++void Assembler::dd(Label* label) { ++ uint64_t data; ++ CheckForEmitInForbiddenSlot(); ++ if (label->is_bound()) { ++ data = reinterpret_cast(buffer_start_ + label->pos()); ++ } else { ++ data = jump_address(label); ++ unbound_labels_count_++; ++ internal_reference_positions_.insert(label->pos()); ++ } ++ RecordRelocInfo(RelocInfo::INTERNAL_REFERENCE); ++ EmitHelper(data); ++} ++ ++void Assembler::RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data) { ++ if (!ShouldRecordRelocInfo(rmode)) return; ++ // We do not try to reuse pool constants. ++ RelocInfo rinfo(reinterpret_cast
(pc_), rmode, data, Code()); ++ DCHECK_GE(buffer_space(), kMaxRelocSize); // Too late to grow buffer here. ++ reloc_info_writer.Write(&rinfo); ++} ++ ++void Assembler::BlockTrampolinePoolFor(int instructions) { ++ CheckTrampolinePoolQuick(instructions); ++ BlockTrampolinePoolBefore(pc_offset() + instructions * kInstrSize); ++} ++ ++void Assembler::CheckTrampolinePool() { ++ // Some small sequences of instructions must not be broken up by the ++ // insertion of a trampoline pool; such sequences are protected by setting ++ // either trampoline_pool_blocked_nesting_ or no_trampoline_pool_before_, ++ // which are both checked here. Also, recursive calls to CheckTrampolinePool ++ // are blocked by trampoline_pool_blocked_nesting_. ++ if ((trampoline_pool_blocked_nesting_ > 0) || ++ (pc_offset() < no_trampoline_pool_before_)) { ++ // Emission is currently blocked; make sure we try again as soon as ++ // possible. ++ if (trampoline_pool_blocked_nesting_ > 0) { ++ next_buffer_check_ = pc_offset() + kInstrSize; ++ } else { ++ next_buffer_check_ = no_trampoline_pool_before_; ++ } ++ return; ++ } ++ ++ DCHECK(!trampoline_emitted_); ++ DCHECK_GE(unbound_labels_count_, 0); ++ if (unbound_labels_count_ > 0) { ++ // First we emit jump (2 instructions), then we emit trampoline pool. ++ { ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ Label after_pool; ++ br(&after_pool); ++ nop(); ++ ++ int pool_start = pc_offset(); ++ for (int i = 0; i < unbound_labels_count_; i++) { ++ { ++ BlockGrowBufferScope block_buf_growth(this); ++ // Buffer growth (and relocation) must be blocked for internal ++ // references until associated instructions are emitted and available ++ // to be patched. ++ RecordRelocInfo(RelocInfo::INTERNAL_REFERENCE_ENCODED); ++ br(&after_pool); ++ } ++ nop(); ++ } ++ // If unbound_labels_count_ is big enough, label after_pool will ++ // need a trampoline too, so we must create the trampoline before ++ // the bind operation to make sure function 'bind' can get this ++ // information. ++ trampoline_ = Trampoline(pool_start, unbound_labels_count_); ++ bind(&after_pool); ++ ++ trampoline_emitted_ = true; ++ // As we are only going to emit trampoline once, we need to prevent any ++ // further emission. ++ next_buffer_check_ = kMaxInt; ++ } ++ } else { ++ // Number of branches to unbound label at this point is zero, so we can ++ // move next buffer check to maximum. ++ next_buffer_check_ = ++ pc_offset() + kMaxBranchOffset - kTrampolineSlotsSize * 16; ++ } ++ return; ++} ++ ++Address Assembler::target_address_at(Address pc) { ++ Instr instr0 = instr_at(pc); ++#ifdef DEBUG ++ Instr instr1 = instr_at(pc + kInstrSize); ++#endif ++ Instr instr2 = instr_at(pc + 2 * kInstrSize); ++ Instr instr3 = instr_at(pc + 3 * kInstrSize); ++ ++ DCHECK(GetSwOpcodeField(instr0) == op_ldi); ++ DCHECK(GetSwOpcodeAndFunctionField(instr1) == op_slll_l); ++ DCHECK(GetSwOpcodeField(instr2) == op_ldih); ++ DCHECK(GetSwOpcodeField(instr3) == op_ldi); ++ ++ // Interpret 4 instructions generated by set ++ uintptr_t addr; ++ addr = (instr0 << 16) >> 16; ++ addr = addr << 32; ++ addr += (instr2 << 16) + ((instr3 << 16) >> 16); ++ ++ return static_cast
(addr); ++ ++ // We should never get here, force a bad address if we do. ++ UNREACHABLE(); ++} ++ ++// On Sw64, a target address is stored in a 4-instruction sequence: ++// 0: ldi(rd, imm16); ++// 1: slll(rd, 32, rd); ++// 2: ldih(rd, imm16(rd)); ++// 3: ldi(rd, imm16(rd)); ++// ++// Patching the address must replace all the ldi & ldih instructions, ++// and flush the i-cache. ++// ++// There is an optimization below, which emits a nop when the address ++// fits in just 16 bits. This is unlikely to help, and should be benchmarked, ++// and possibly removed. ++void Assembler::set_target_value_at(Address pc, uint64_t target, ++ ICacheFlushMode icache_flush_mode) { ++ // There is an optimization where only 4 instructions are used to load address ++ // in code on SW64 because only 48-bits of address is effectively used. ++ // It relies on fact the upper [63:48] bits are not used for virtual address ++ // translation and they have to be set according to value of bit 47 in order ++ // get canonical address. ++ uint32_t* p = reinterpret_cast(pc); ++ uint64_t itarget = reinterpret_cast(target); ++ ++#ifdef DEBUG ++ // Check we have the result from a li macro-instruction, using instr pair. ++ Instr instr0 = instr_at(pc); ++ Instr instr1 = instr_at(pc + kInstrSize); ++ Instr instr2 = instr_at(pc + 2 * kInstrSize); ++ Instr instr3 = instr_at(pc + 3 * kInstrSize); ++ CHECK(GetSwOpcodeField(instr0) == op_ldi); ++ CHECK(GetSwOpcodeAndFunctionField(instr1) == op_slll_l); ++ CHECK(GetSwOpcodeField(instr2) == op_ldih); ++ CHECK(GetSwOpcodeField(instr3) == op_ldi); ++#endif ++ ++ // Must use 4 instructions to insure patchable code. ++ int32_t lsb32 = (int32_t)(itarget); ++ int32_t msb32 = (int32_t)((itarget - lsb32) >> 32); ++ ++ // Maybe value to "|" is negative, so need set it to 16-bits. ++ *(p + 0) = (*(p + 0) & 0xffff0000) | ((int16_t)(msb32 & 0xffff) & 0xffff); ++ *(p + 2) = ++ (*(p + 2) & 0xffff0000) | (((lsb32 - (int16_t)lsb32) >> 16) & 0xffff); ++ *(p + 3) = (*(p + 3) & 0xffff0000) | ((int16_t)(lsb32 & 0xffff) & 0xffff); ++ ++ if (icache_flush_mode != SKIP_ICACHE_FLUSH) { ++ FlushInstructionCache(pc, 4 * kInstrSize); ++ } ++} ++ ++UseScratchRegisterScope::UseScratchRegisterScope(Assembler* assembler) ++ : available_(assembler->GetScratchRegisterList()), ++ old_available_(*available_) {} ++ ++UseScratchRegisterScope::~UseScratchRegisterScope() { ++ *available_ = old_available_; ++} ++ ++Register UseScratchRegisterScope::Acquire() { ++ DCHECK_NOT_NULL(available_); ++ return available_->PopFirst(); ++} ++ ++bool UseScratchRegisterScope::hasAvailable() const { ++ return !available_->is_empty(); ++} ++ ++// SKTODO ++#if 0 ++LoadStoreLaneParams::LoadStoreLaneParams(MachineRepresentation rep, ++ uint8_t laneidx) { ++ switch (rep) { ++ case MachineRepresentation::kWord8: ++ *this = LoadStoreLaneParams(laneidx, SWSA_B, 16); ++ break; ++ case MachineRepresentation::kWord16: ++ *this = LoadStoreLaneParams(laneidx, SWSA_H, 8); ++ break; ++ case MachineRepresentation::kWord32: ++ *this = LoadStoreLaneParams(laneidx, SWSA_W, 4); ++ break; ++ case MachineRepresentation::kWord64: ++ *this = LoadStoreLaneParams(laneidx, SWSA_D, 2); ++ break; ++ default: ++ UNREACHABLE(); ++ } ++} ++#endif ++} // namespace internal ++} // namespace v8 ++ ++#endif // V8_TARGET_ARCH_SW64 +diff --git a/deps/v8/src/codegen/sw64/assembler-sw64.h b/deps/v8/src/codegen/sw64/assembler-sw64.h +new file mode 100644 +index 00000000..739bfa17 +--- /dev/null ++++ b/deps/v8/src/codegen/sw64/assembler-sw64.h +@@ -0,0 +1,1519 @@ ++// Copyright (c) 1994-2006 Sun Microsystems Inc. ++// All Rights Reserved. ++// ++// Redistribution and use in source and binary forms, with or without ++// modification, are permitted provided that the following conditions are ++// met: ++// ++// - Redistributions of source code must retain the above copyright notice, ++// this list of conditions and the following disclaimer. ++// ++// - Redistribution in binary form must reproduce the above copyright ++// notice, this list of conditions and the following disclaimer in the ++// documentation and/or other materials provided with the distribution. ++// ++// - Neither the name of Sun Microsystems or the names of contributors may ++// be used to endorse or promote products derived from this software without ++// specific prior written permission. ++// ++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ ++// The original source code covered by the above license above has been ++// modified significantly by Google Inc. ++// Copyright 2012 the V8 project authors. All rights reserved. ++ ++#ifndef V8_CODEGEN_SW64_ASSEMBLER_SW64_H_ ++#define V8_CODEGEN_SW64_ASSEMBLER_SW64_H_ ++ ++#include ++ ++#include ++#include ++ ++#include "src/codegen/assembler.h" ++#include "src/codegen/external-reference.h" ++#include "src/codegen/label.h" ++#include "src/codegen/machine-type.h" ++#include "src/codegen/sw64/constants-sw64.h" ++#include "src/codegen/sw64/register-sw64.h" ++#include "src/objects/contexts.h" ++#include "src/objects/smi.h" ++ ++namespace v8 { ++namespace internal { ++ ++class SafepointTableBuilder; ++ ++// ----------------------------------------------------------------------------- ++// Machine instruction Operands. ++constexpr int kSmiShift = kSmiTagSize + kSmiShiftSize; ++constexpr uint64_t kSmiShiftMask = (1UL << kSmiShift) - 1; ++// Class Operand represents a shifter operand in data processing instructions. ++class Operand { ++ public: ++ // Immediate. ++ V8_INLINE explicit Operand(int64_t immediate, ++ RelocInfo::Mode rmode = RelocInfo::NO_INFO) ++ : rm_(no_reg), rmode_(rmode) { ++ value_.immediate = immediate; ++ } ++ V8_INLINE static Operand Zero() { return Operand(static_cast(0)); } ++ V8_INLINE explicit Operand(const ExternalReference& f) ++ : rm_(no_reg), rmode_(RelocInfo::EXTERNAL_REFERENCE) { ++ value_.immediate = static_cast(f.address()); ++ } ++ V8_INLINE explicit Operand(const char* s); ++ explicit Operand(Handle handle); ++ V8_INLINE explicit Operand(Smi value) ++ : rm_(no_reg), rmode_(RelocInfo::NO_INFO) { ++ value_.immediate = static_cast(value.ptr()); ++ } ++ ++ static Operand EmbeddedNumber(double number); // Smi or HeapNumber. ++ static Operand EmbeddedStringConstant(const StringConstantBase* str); ++ ++ // Register. ++ V8_INLINE explicit Operand(Register rm) : rm_(rm) {} ++ ++ // Return true if this is a register operand. ++ V8_INLINE bool is_reg() const; ++ ++ inline int64_t immediate() const; ++ ++ bool IsImmediate() const { return !rm_.is_valid(); } ++ ++ HeapObjectRequest heap_object_request() const { ++ DCHECK(IsHeapObjectRequest()); ++ return value_.heap_object_request; ++ } ++ ++ bool IsHeapObjectRequest() const { ++ DCHECK_IMPLIES(is_heap_object_request_, IsImmediate()); ++ DCHECK_IMPLIES(is_heap_object_request_, ++ rmode_ == RelocInfo::FULL_EMBEDDED_OBJECT || ++ rmode_ == RelocInfo::CODE_TARGET); ++ return is_heap_object_request_; ++ } ++ ++ Register rm() const { return rm_; } ++ ++ RelocInfo::Mode rmode() const { return rmode_; } ++ ++ private: ++ Register rm_; ++ union Value { ++ Value() {} ++ HeapObjectRequest heap_object_request; // if is_heap_object_request_ ++ int64_t immediate; // otherwise ++ } value_; // valid if rm_ == no_reg ++ bool is_heap_object_request_ = false; ++ RelocInfo::Mode rmode_; ++ ++ friend class Assembler; ++ friend class MacroAssembler; ++}; ++ ++// On SW64 we have only one addressing mode with base_reg + offset. ++// Class MemOperand represents a memory operand in load and store instructions. ++class V8_EXPORT_PRIVATE MemOperand : public Operand { ++ public: ++ // Immediate value attached to offset. ++ enum OffsetAddend { offset_minus_one = -1, offset_zero = 0 }; ++ ++ explicit MemOperand(Register rn, int32_t offset = 0); ++ explicit MemOperand(Register rn, int32_t unit, int32_t multiplier, ++ OffsetAddend offset_addend = offset_zero); ++ int32_t offset() const { return offset_; } ++ ++ bool OffsetIsInt16Encodable() const { return is_int16(offset_); } ++ ++ private: ++ int32_t offset_; ++ ++ friend class Assembler; ++}; ++ ++class V8_EXPORT_PRIVATE Assembler : public AssemblerBase { ++ public: ++ // Create an assembler. Instructions and relocation information are emitted ++ // into a buffer, with the instructions starting from the beginning and the ++ // relocation information starting from the end of the buffer. See CodeDesc ++ // for a detailed comment on the layout (globals.h). ++ // ++ // If the provided buffer is nullptr, the assembler allocates and grows its ++ // own buffer. Otherwise it takes ownership of the provided buffer. ++ explicit Assembler(const AssemblerOptions&, ++ std::unique_ptr = {}); ++ ++ virtual ~Assembler() {} ++ ++ // GetCode emits any pending (non-emitted) code and fills the descriptor desc. ++ static constexpr int kNoHandlerTable = 0; ++ static constexpr SafepointTableBuilder* kNoSafepointTable = nullptr; ++ ++ // Convenience wrapper for code without safepoint or handler tables. ++ void GetCode(Isolate* isolate, CodeDesc* desc); ++ void GetCode(Isolate* isolate, CodeDesc* desc, ++ SafepointTableBuilder* safepoint_table_builder, ++ int handler_table_offset); ++ ++ // Unused on this architecture. ++ void MaybeEmitOutOfLineConstantPool() {} ++ ++ // Sw64 uses BlockTrampolinePool to prevent generating trampoline inside a ++ // continuous instruction block. For Call instruction, it prevents generating ++ // trampoline between jalr and delay slot instruction. In the destructor of ++ // BlockTrampolinePool, it must check if it needs to generate trampoline ++ // immediately, if it does not do this, the branch range will go beyond the ++ // max branch offset, that means the pc_offset after call CheckTrampolinePool ++ // may have changed. So we use pc_for_safepoint_ here for safepoint record. ++ int pc_offset_for_safepoint() { ++ return static_cast(pc_for_safepoint_ - buffer_start_); ++ } ++ ++ // Label operations & relative jumps (PPUM Appendix D). ++ // ++ // Takes a branch opcode (cc) and a label (L) and generates ++ // either a backward branch or a forward branch and links it ++ // to the label fixup chain. Usage: ++ // ++ // Label L; // unbound label ++ // j(cc, &L); // forward branch to unbound label ++ // bind(&L); // bind label to the current pc ++ // j(cc, &L); // backward branch to bound label ++ // bind(&L); // illegal: a label may be bound only once ++ // ++ // Note: The same Label can be used for forward and backward branches ++ // but it may be bound only once. ++ void bind(Label* L); // Binds an unbound label L to current code position. ++ // instructions call and jmp have imm16 zone, but it isn't a limit, just a ++ // assisment of branch prediction ++ enum OffsetSize : int { kOffset26 = 26, kOffset21 = 21 }; ++ ++ // 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); ++ ++ int BranchOffset(Instr instr); ++ ++ // Returns the branch offset to the given label from the current code ++ // position. Links the label to the current position if it is still unbound. ++ // Manages the jump elimination optimization if the second parameter is true. ++ int32_t branch_offset_helper(Label* L, OffsetSize bits); ++ inline int32_t branch_offset(Label* L) { ++ return branch_offset_helper(L, OffsetSize::kOffset21); ++ } ++ inline int32_t branch_offset21(Label* L) { ++ return branch_offset_helper(L, OffsetSize::kOffset21); ++ } ++ inline int32_t branch_offset26(Label* L) { ++ return branch_offset_helper(L, OffsetSize::kOffset26); ++ } ++ inline int32_t shifted_branch_offset(Label* L) { ++ return branch_offset(L) >> 2; ++ } ++ inline int32_t shifted_branch_offset21(Label* L) { ++ return branch_offset21(L) >> 2; ++ } ++ inline int32_t shifted_branch_offset26(Label* L) { ++ return branch_offset26(L) >> 2; ++ } ++ uint64_t jump_address(Label* L); ++ uint64_t jump_offset(Label* L); ++ uint64_t branch_long_offset(Label* L); ++ ++ // Puts a labels target address at the given position. ++ // The high 8 bits are set to zero. ++ void label_at_put(Label* L, int at_offset); ++ ++ // Read/Modify the code target address in the branch/call instruction at pc. ++ // The isolate argument is unused (and may be nullptr) when skipping flushing. ++ static Address target_address_at(Address pc); ++ V8_INLINE static void set_target_address_at( ++ Address pc, Address target, ++ ICacheFlushMode icache_flush_mode = FLUSH_ICACHE_IF_NEEDED) { ++ set_target_value_at(pc, target, icache_flush_mode); ++ } ++ // On SW64 there is no Constant Pool so we skip that parameter. ++ V8_INLINE static Address target_address_at(Address pc, ++ Address constant_pool) { ++ return target_address_at(pc); ++ } ++ V8_INLINE static void set_target_address_at( ++ Address pc, Address constant_pool, Address target, ++ ICacheFlushMode icache_flush_mode = FLUSH_ICACHE_IF_NEEDED) { ++ set_target_address_at(pc, target, icache_flush_mode); ++ } ++ ++ static void set_target_value_at( ++ Address pc, uint64_t target, ++ ICacheFlushMode icache_flush_mode = FLUSH_ICACHE_IF_NEEDED); ++ ++ static void JumpLabelToJumpRegister(Address pc); ++ ++ // This sets the branch destination (which gets loaded at the call address). ++ // This is for calls and branches within generated code. The serializer ++ // has already deserialized the ldih/ldi instructions etc. ++ inline static void deserialization_set_special_target_at( ++ Address instruction_payload, Code code, Address target); ++ ++ // Get the size of the special target encoded at 'instruction_payload'. ++ inline static int deserialization_special_target_size( ++ Address instruction_payload); ++ ++ // This sets the internal reference at the pc. ++ inline static void deserialization_set_target_internal_reference_at( ++ Address pc, Address target, ++ RelocInfo::Mode mode = RelocInfo::INTERNAL_REFERENCE); ++ ++ static constexpr int kSpecialTargetSize = 0; ++ ++ // Number of consecutive instructions used to store 32bit/64bit constant. ++ // This constant was used in RelocInfo::target_address_address() function ++ // to tell serializer address of the instruction that follows ++ // ldih/ldi instruction pair. ++ static constexpr int kInstructionsFor64BitConstant = 4; ++ ++ // Difference between address of current opcode and value read from pc ++ // register. ++ static constexpr int kPcLoadDelta = 4; ++ ++ // Max offset for instructions with 21-bit offset field ++ static constexpr int kMaxBranchOffset = (1 << (23 - 1)) - 1; ++ ++ // Max offset for long branch instructions with 26-bit offset field ++ static constexpr int kMaxLongBranchOffset = (1 << (28 - 1)) - 1; ++ ++ static constexpr int kTrampolineSlotsSize = 2 * kInstrSize; ++ ++ RegList* GetScratchRegisterList() { return &scratch_register_list_; } ++ ++ // --------------------------------------------------------------------------- ++ // InstructionStream generation. ++ ++ // Insert the smallest number of nop instructions ++ // possible to align the pc offset to a multiple ++ // of m. m must be a power of 2 (>= 4). ++ void Align(int m); ++ // Insert the smallest number of zero bytes possible to align the pc offset ++ // to a mulitple of m. m must be a power of 2 (>= 2). ++ void DataAlign(int m); ++ // Aligns code to something that's optimal for a jump target for the platform. ++ void CodeTargetAlign(); ++ void LoopHeaderAlign() { CodeTargetAlign(); } ++ ++ // Different nop operations are used by the code generator to detect certain ++ // states of the generated code. ++ enum NopMarkerTypes { ++ NON_MARKING_NOP = 0, ++ DEBUG_BREAK_NOP, ++ // IC markers. ++ PROPERTY_ACCESS_INLINED, ++ PROPERTY_ACCESS_INLINED_CONTEXT, ++ PROPERTY_ACCESS_INLINED_CONTEXT_DONT_DELETE, ++ // Helper values. ++ LAST_CODE_MARKER, ++ FIRST_IC_MARKER = PROPERTY_ACCESS_INLINED, ++ }; ++ ++ // Type == 0 is the default non-marking nop. For sw64 this is a ++ // ldi(zero_reg, 0, zero_reg). ++ void nop(unsigned int type = 0) { ++ DCHECK_EQ(type, 0); ++ ldi(zero_reg, 0, zero_reg); ++ } ++ ++ // --------Branch-and-jump-instructions---------- ++ // We don't use likely variant of instructions. ++ // HYQ TODO add lbr ++ void br(int32_t offset); ++ inline void br(Label* L) { br(shifted_branch_offset21(L)); } ++ void bsr(int32_t offset); ++ inline void bsr(Label* L) { bsr(shifted_branch_offset21(L)); } ++ void br(Register Ra, int bdisp); ++ void bsr(Register Ra, int bdisp); ++ // HYQ TODO add Label arg ++ void beq(Register Ra, int bdisp); ++ void bne(Register Ra, int bdisp); ++ void blt(Register Ra, int bdisp); ++ void ble(Register Ra, int bdisp); ++ void bgt(Register Ra, int bdisp); ++ void bge(Register Ra, int bdisp); ++ void blbc(Register Ra, int bdisp); ++ void blbs(Register Ra, int bdisp); ++ void fbeq(FloatRegister fa, int bdisp); ++ void fbne(FloatRegister fa, int bdisp); ++ void fblt(FloatRegister fa, int bdisp); ++ void fble(FloatRegister fa, int bdisp); ++ void fbgt(FloatRegister fa, int bdisp); ++ void fbge(FloatRegister fa, int bdisp); ++ ++ void call(Register Ra, Register Rb, int jmphint); ++ void ret(Register Ra, Register Rb, int rethint); ++ void jmp(Register Ra, Register Rb, int jmphint); ++ ++ void sys_call_b(int palfn); ++ void sys_call(int palfn); ++ ++ // -------Data-processing-instructions--------- ++ ++ // Arithmetic. ++ void addw(Register Ra, Register Rb, Register Rc); ++ void addw(Register Ra, int lit, Register Rc); ++ void subw(Register Ra, Register Rb, Register Rc); ++ void subw(Register Ra, int lit, Register Rc); ++ void s4addw(Register Ra, Register Rb, Register Rc); ++ void s4addw(Register Ra, int lit, Register Rc); ++ void s4subw(Register Ra, Register Rb, Register Rc); ++ void s4subw(Register Ra, int lit, Register Rc); ++ void s8addw(Register Ra, Register Rb, Register Rc); ++ void s8addw(Register Ra, int lit, Register Rc); ++ void s8subw(Register Ra, Register Rb, Register Rc); ++ void s8subw(Register Ra, int lit, Register Rc); ++ void addl(Register Ra, Register Rb, Register Rc); ++ void addl(Register Ra, int lit, Register Rc); ++ void subl(Register Ra, Register Rb, Register Rc); ++ void subl(Register Ra, int lit, Register Rc); ++ void s4addl(Register Ra, Register Rb, Register Rc); ++ void s4addl(Register Ra, int lit, Register Rc); ++ void s4subl(Register Ra, Register Rb, Register Rc); ++ void s4subl(Register Ra, int lit, Register Rc); ++ void s8addl(Register Ra, Register Rb, Register Rc); ++ void s8addl(Register Ra, int lit, Register Rc); ++ void s8subl(Register Ra, Register Rb, Register Rc); ++ void s8subl(Register Ra, int lit, Register Rc); ++ void mulw(Register Ra, Register Rb, Register Rc); ++ void mulw(Register Ra, int lit, Register Rc); ++ void divw(Register Ra, Register Rb, Register Rc); // SW8A ++ void udivw(Register Ra, Register Rb, Register Rc); // SW8A ++ void remw(Register Ra, Register Rb, Register Rc); // SW8A ++ void uremw(Register Ra, Register Rb, Register Rc); // SW8A ++ void mull(Register Ra, Register Rb, Register Rc); ++ void mull(Register Ra, int lit, Register Rc); ++ void umulh(Register Ra, Register Rb, Register Rc); ++ void umulh(Register Ra, int lit, Register Rc); ++ void divl(Register Ra, Register Rb, Register Rc); // SW8A ++ void udivl(Register Ra, Register Rb, Register Rc); // SW8A ++ void reml(Register Ra, Register Rb, Register Rc); // SW8A ++ void ureml(Register Ra, Register Rb, Register Rc); // SW8A ++ void addpi(int apint, Register Rc); // SW8A ++ void addpis(int apint, Register Rc); // SW8A ++ ++ // Logical. ++ void and_(Register Ra, Register Rb, Register Rc); ++ void and_(Register Ra, int lit, Register Rc); ++ void bis(Register Ra, Register Rb, Register Rc); ++ void bis(Register Ra, int lit, Register Rc); ++ void or_(Register Ra, Register Rb, Register Rc); // alias bis ++ void or_(Register Ra, int lit, Register Rc); // alias bis ++ void bic(Register Ra, Register Rb, Register Rc); ++ void bic(Register Ra, int lit, Register Rc); ++ void andnot(Register Ra, Register Rb, Register Rc); // alias bic ++ void andnot(Register Ra, int lit, Register Rc); // alias bic ++ void ornot(Register Ra, Register Rb, Register Rc); ++ void ornot(Register Ra, int lit, Register Rc); ++ void xor_(Register Ra, Register Rb, Register Rc); ++ void xor_(Register Ra, int lit, Register Rc); ++ void eqv(Register Ra, Register Rb, Register Rc); ++ void eqv(Register Ra, int lit, Register Rc); ++ ++ // Shifts. ++ void slll(Register Ra, Register Rb, Register Rc); ++ void slll(Register Ra, int lit, Register Rc); ++ void srll(Register Ra, Register Rb, Register Rc); ++ void srll(Register Ra, int lit, Register Rc); ++ void sral(Register Ra, Register Rb, Register Rc); ++ void sral(Register Ra, int lit, Register Rc); ++ void roll(Register Ra, Register Rb, Register Rc); // SW8A ++ void roll(Register Ra, int lit, Register Rc); // SW8A ++ void sllw(Register Ra, Register Rb, Register Rc); // SW8A ++ void sllw(Register Ra, int lit, Register Rc); // SW8A ++ void srlw(Register Ra, Register Rb, Register Rc); // SW8A ++ void srlw(Register Ra, int lit, Register Rc); // SW8A ++ void sraw(Register Ra, Register Rb, Register Rc); // SW8A ++ void sraw(Register Ra, int lit, Register Rc); // SW8A ++ void rolw(Register Ra, Register Rb, Register Rc); // SW8A ++ void rolw(Register Ra, int lit, Register Rc); // SW8A ++ ++ // 0x10.50-0x10.57 EXT[0-7]B ++ void extlb(Register Ra, Register Rb, Register Rc); ++ void extlb(Register Ra, int lit, Register Rc); ++ void extlh(Register Ra, Register Rb, Register Rc); ++ void extlh(Register Ra, int lit, Register Rc); ++ void extlw(Register Ra, Register Rb, Register Rc); ++ void extlw(Register Ra, int lit, Register Rc); ++ void extll(Register Ra, Register Rb, Register Rc); ++ void extll(Register Ra, int lit, Register Rc); ++ void exthb(Register Ra, Register Rb, Register Rc); ++ void exthb(Register Ra, int lit, Register Rc); ++ void exthh(Register Ra, Register Rb, Register Rc); ++ void exthh(Register Ra, int lit, Register Rc); ++ void exthw(Register Ra, Register Rb, Register Rc); ++ void exthw(Register Ra, int lit, Register Rc); ++ void exthl(Register Ra, Register Rb, Register Rc); ++ void exthl(Register Ra, int lit, Register Rc); ++ ++ void ctpop(Register Rb, Register Rc); ++ void ctlz(Register Rb, Register Rc); ++ void cttz(Register Rb, Register Rc); ++ void revbh(Register Rb, Register Rc); // SW8A ++ void revbw(Register Rb, Register Rc); // SW8A ++ void revbl(Register Rb, Register Rc); // SW8A ++ void casw(Register Ra, Register Rb, Register Rc); // SW8A ++ void casl(Register Ra, Register Rb, Register Rc); // SW8A ++ ++ // 0x10.60-0x10.67 MASK[0-7]B ++ void masklb(Register Ra, Register Rb, Register Rc); ++ void masklb(Register Ra, int lit, Register Rc); ++ void masklh(Register Ra, Register Rb, Register Rc); ++ void masklh(Register Ra, int lit, Register Rc); ++ void masklw(Register Ra, Register Rb, Register Rc); ++ void masklw(Register Ra, int lit, Register Rc); ++ void maskll(Register Ra, Register Rb, Register Rc); ++ void maskll(Register Ra, int lit, Register Rc); ++ void maskhb(Register Ra, Register Rb, Register Rc); ++ void maskhb(Register Ra, int lit, Register Rc); ++ void maskhh(Register Ra, Register Rb, Register Rc); ++ void maskhh(Register Ra, int lit, Register Rc); ++ void maskhw(Register Ra, Register Rb, Register Rc); ++ void maskhw(Register Ra, int lit, Register Rc); ++ void maskhl(Register Ra, Register Rb, Register Rc); ++ void maskhl(Register Ra, int lit, Register Rc); ++ ++ void zap(Register Ra, Register Rb, Register Rc); ++ void zap(Register Ra, int lit, Register Rc); ++ void zapnot(Register Ra, Register Rb, Register Rc); ++ void zapnot(Register Ra, int lit, Register Rc); ++ void sextb(Register Rb, Register Rc); ++ void sextb(int lit, Register Rc); ++ void sexth(Register Rb, Register Rc); ++ void sexth(int lit, Register Rc); ++ ++ void fmovd(FPURegister fs, FPURegister fd); ++ void fmovs(FPURegister fs, FPURegister fd); ++ ++ // Conversion. ++ void fcvtsw(FPURegister fs, FPURegister fd); ++ void fcvtdw(FPURegister fs, FPURegister fd); ++ void ftruncsw(FPURegister fs, FPURegister fd); ++ void ftruncdw(FPURegister fs, FPURegister fd); ++ void froundsw(FPURegister fs, FPURegister fd); ++ void frounddw(FPURegister fs, FPURegister fd); ++ void ffloorsw(FPURegister fs, FPURegister fd); ++ void ffloordw(FPURegister fs, FPURegister fd); ++ void fceilsw(FPURegister fs, FPURegister fd); ++ void fceildw(FPURegister fs, FPURegister fd); ++ ++ void fcvtsl(FPURegister fs, FPURegister fd); ++ void ftruncsl(FPURegister fs, FPURegister fd); ++ void ftruncdl(FPURegister fs, FPURegister fd); ++ void froundsl(FPURegister fs, FPURegister fd); ++ void frounddl(FPURegister fs, FPURegister fd); ++ void ffloorsl(FPURegister fs, FPURegister fd); ++ void ffloordl(FPURegister fs, FPURegister fd); ++ void fceilsl(FPURegister fs, FPURegister fd); ++ void fceildl(FPURegister fs, FPURegister fd); ++ ++ void fcvtws(FPURegister fs, FPURegister fd); ++ void fcvtls_(FPURegister fs, FPURegister fd); ++ void fcvtds_(FPURegister fs, FPURegister fd); ++ ++ void fcvtwd(FPURegister fs, FPURegister fd); ++ void fcvtld_(FPURegister fs, FPURegister fd); ++ void fcvtsd_(FPURegister fs, FPURegister fd); ++ ++ void sld_b(SWSARegister wd, SWSARegister ws, Register rt); ++ void sld_h(SWSARegister wd, SWSARegister ws, Register rt); ++ void sld_w(SWSARegister wd, SWSARegister ws, Register rt); ++ void sld_d(SWSARegister wd, SWSARegister ws, Register rt); ++ void splat_b(SWSARegister wd, SWSARegister ws, Register rt); ++ void splat_h(SWSARegister wd, SWSARegister ws, Register rt); ++ void splat_w(SWSARegister wd, SWSARegister ws, Register rt); ++ void splat_d(SWSARegister wd, SWSARegister ws, Register rt); ++ ++ void memb(void); ++ void imemb(void); ++ void wmemb(void); // SW8A ++ // void rtc ( Register Ra ); ++ void rtc(Register Ra, Register Rb); ++ void rcid(Register Ra); ++ void halt(void); ++ ++ void rd_f(Register Ra); // SW2F ++ void wr_f(Register Ra); // SW2F ++ ++ void rtid(Register Ra); ++ void csrrs(Register Ra, int rpiindex); // SW8A ++ void csrrc(Register Ra, int rpiindex); // SW8A ++ void csrr(Register Ra, int rpiindex); ++ void csrw(Register Ra, int rpiindex); ++ // void pri_rcsr ( Register Ra, int rpiindex ); ++ // void pri_wcsr ( Register Ra, int rpiindex ); ++ void pri_ret(Register Ra); ++ // void pri_ret_b ( Register Ra ); ++ ++ void lldw(Register Ra, int atmdisp, Register Rb); ++ void lldl(Register Ra, int atmdisp, Register Rb); ++ void ldw_inc(Register Ra, int atmdisp, Register Rb); // SW2F ++ void ldl_inc(Register Ra, int atmdisp, Register Rb); // SW2F ++ void ldw_dec(Register Ra, int atmdisp, Register Rb); // SW2F ++ void ldl_dec(Register Ra, int atmdisp, Register Rb); // SW2F ++ void ldw_set(Register Ra, int atmdisp, Register Rb); // SW2F ++ void ldl_set(Register Ra, int atmdisp, Register Rb); // SW2F ++ void lstw(Register Ra, int atmdisp, Register Rb); ++ void lstl(Register Ra, int atmdisp, Register Rb); ++ void ldw_nc(Register Ra, int atmdisp, Register Rb); ++ void ldl_nc(Register Ra, int atmdisp, Register Rb); ++ void ldd_nc(Register Ra, int atmdisp, Register Rb); ++ void stw_nc(Register Ra, int atmdisp, Register Rb); ++ void stl_nc(Register Ra, int atmdisp, Register Rb); ++ void std_nc(Register Ra, int atmdisp, Register Rb); ++ ++ // TODO: 0x8.a-0x8.f *_NC instructions ++ ++ // --------Load/Store-instructions----------------- ++ void ldwe(FloatRegister fa, int mdisp, Register Rb); ++ void ldse(FloatRegister fa, int mdisp, Register Rb); ++ void ldde(FloatRegister fa, int mdisp, Register Rb); ++ void vlds(FloatRegister fa, int mdisp, Register Rb); ++ void vldd(FloatRegister fa, int mdisp, Register Rb); ++ void vsts(FloatRegister fa, int mdisp, Register Rb); ++ void vstd(FloatRegister fa, int mdisp, Register Rb); ++ ++ void cmpeq(Register Ra, Register Rb, Register Rc); ++ void cmpeq(Register Ra, int lit, Register Rc); ++ void cmplt(Register Ra, Register Rb, Register Rc); ++ void cmplt(Register Ra, int lit, Register Rc); ++ void cmple(Register Ra, Register Rb, Register Rc); ++ void cmple(Register Ra, int lit, Register Rc); ++ void cmpult(Register Ra, Register Rb, Register Rc); ++ void cmpult(Register Ra, int lit, Register Rc); ++ void cmpule(Register Ra, Register Rb, Register Rc); ++ void cmpule(Register Ra, int lit, Register Rc); ++ void sbt(Register Ra, Register Rb, Register Rc); // SW8A ++ void sbt(Register Ra, int lit, Register Rc); // SW8A ++ void cbt(Register Ra, Register Rb, Register Rc); // SW8A ++ void cbt(Register Ra, int lit, Register Rc); // SW8A ++ ++ // 0x10.40-0x10.47 INS[0-7]B ++ void inslb(Register Ra, Register Rb, Register Rc); ++ void inslb(Register Ra, int lit, Register Rc); ++ void inslh(Register Ra, Register Rb, Register Rc); ++ void inslh(Register Ra, int lit, Register Rc); ++ void inslw(Register Ra, Register Rb, Register Rc); ++ void inslw(Register Ra, int lit, Register Rc); ++ void insll(Register Ra, Register Rb, Register Rc); ++ void insll(Register Ra, int lit, Register Rc); ++ void inshb(Register Ra, Register Rb, Register Rc); ++ void inshb(Register Ra, int lit, Register Rc); ++ void inshh(Register Ra, Register Rb, Register Rc); ++ void inshh(Register Ra, int lit, Register Rc); ++ void inshw(Register Ra, Register Rb, Register Rc); ++ void inshw(Register Ra, int lit, Register Rc); ++ void inshl(Register Ra, Register Rb, Register Rc); ++ void inshl(Register Ra, int lit, Register Rc); ++ ++ // 0x10.6c CMPGEB ++ void cmpgeb(Register Ra, Register Rb, Register Rc); ++ void cmpgeb(Register Ra, int lit, Register Rc); ++ ++ void fimovs(FloatRegister fa, Register Rc); ++ void fimovd(FloatRegister fa, Register Rc); ++ ++ void seleq(Register Ra, Register Rb, Register R3, Register Rc); ++ void seleq(Register Ra, int lit, Register R3, Register Rc); ++ void selge(Register Ra, Register Rb, Register R3, Register Rc); ++ void selge(Register Ra, int lit, Register R3, Register Rc); ++ void selgt(Register Ra, Register Rb, Register R3, Register Rc); ++ void selgt(Register Ra, int lit, Register R3, Register Rc); ++ void selle(Register Ra, Register Rb, Register R3, Register Rc); ++ void selle(Register Ra, int lit, Register R3, Register Rc); ++ void sellt(Register Ra, Register Rb, Register R3, Register Rc); ++ void sellt(Register Ra, int lit, Register R3, Register Rc); ++ void selne(Register Ra, Register Rb, Register R3, Register Rc); ++ void selne(Register Ra, int lit, Register R3, Register Rc); ++ void sellbc(Register Ra, Register Rb, Register R3, Register Rc); ++ void sellbc(Register Ra, int lit, Register R3, Register Rc); ++ void sellbs(Register Ra, Register Rb, Register R3, Register Rc); ++ void sellbs(Register Ra, int lit, Register R3, Register Rc); ++ ++ void vlog(int vlog, FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc); ++ ++ void f_exclude_same_src_fc(Opcode_ops_fp opcode, FloatRegister fa, ++ FloatRegister fb, FloatRegister fc); ++ void f_exclude_same_src_fc(Opcode_ops_fp opcode, FloatRegister fb, ++ FloatRegister fc); ++ void vbisw(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void vxorw(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void vandw(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void veqvw(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void vornotw(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void vbicw(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void fadds(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void faddd(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void fsubs(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void fsubd(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void fmuls(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void fmuld(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void fdivs(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void fdivd(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void fsqrts(FloatRegister fb, FloatRegister fc); ++ void fsqrtd(FloatRegister fb, FloatRegister fc); ++ void fcmpeq(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void fcmple(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void fcmplt(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void fcmpun(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void fcvtsd(FloatRegister fb, FloatRegister fc); ++ void fcvtds(FloatRegister fb, FloatRegister fc); ++ void fcvtdl_g(FloatRegister fb, FloatRegister fc); ++ void fcvtdl_p(FloatRegister fb, FloatRegister fc); ++ void fcvtdl_z(FloatRegister fb, FloatRegister fc); ++ void fcvtdl_n(FloatRegister fb, FloatRegister fc); ++ void fcvtdl(FloatRegister fb, FloatRegister fc); ++ void fcvtwl(FloatRegister fb, FloatRegister fc); ++ void fcvtlw(FloatRegister fb, FloatRegister fc); ++ void fcvtls(FloatRegister fb, FloatRegister fc); ++ void fcvtld(FloatRegister fb, FloatRegister fc); ++ void fcvths(FloatRegister Fb, FloatRegister Fc); ++ void fcvtsh(FloatRegister Fb, FloatRegister Fc); ++ void cmovdw(FloatRegister fa, Register Rc); ++ void cmovdw_g(FloatRegister fa, Register Rc); ++ void cmovdw_p(FloatRegister fa, Register Rc); ++ void cmovdw_z(FloatRegister fa, Register Rc); ++ void cmovdw_n(FloatRegister fa, Register Rc); ++ void cmovdwu(FloatRegister fa, Register Rc); ++ void cmovdwu_g(FloatRegister fa, Register Rc); ++ void cmovdwu_p(FloatRegister fa, Register Rc); ++ void cmovdwu_z(FloatRegister fa, Register Rc); ++ void cmovdwu_n(FloatRegister fa, Register Rc); ++ void cmovdl(FloatRegister fa, Register Rc); ++ void cmovdl_g(FloatRegister fa, Register Rc); ++ void cmovdl_p(FloatRegister fa, Register Rc); ++ void cmovdl_z(FloatRegister fa, Register Rc); ++ void cmovdl_n(FloatRegister fa, Register Rc); ++ void cmovdlu(FloatRegister fa, Register Rc); ++ void cmovdlu_g(FloatRegister fa, Register Rc); ++ void cmovdlu_p(FloatRegister fa, Register Rc); ++ void cmovdlu_z(FloatRegister fa, Register Rc); ++ void cmovdlu_n(FloatRegister fa, Register Rc); ++ void cmovls(Register Rb, FloatRegister Rc); ++ void cmovld(Register Rb, FloatRegister Rc); ++ void cmovuls(Register Rb, FloatRegister Rc); ++ void cmovuld(Register Rb, FloatRegister Rc); ++ void cmovws(Register Rb, FloatRegister Rc); ++ void cmovwd(Register Rb, FloatRegister Rc); ++ void cmovuws(Register Rb, FloatRegister Rc); ++ void cmovuwd(Register Rb, FloatRegister Rc); ++ void fcpys(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void fcpyse(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void fcpysn(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void ifmovs(Register Ra, FloatRegister fc); ++ void ifmovd(Register Ra, FloatRegister fc); ++ void rfpcr(FloatRegister fa); ++ void wfpcr(FloatRegister fa); ++ void setfpec0(); ++ void setfpec1(); ++ void setfpec2(); ++ void setfpec3(); ++ ++ void frecs(FloatRegister fa, FloatRegister fc); // SW8A ++ void frecd(FloatRegister fa, FloatRegister fc); // SW8A ++ void fris(FloatRegister fb, FloatRegister fc); // SW8A ++ void fris_g(FloatRegister fb, FloatRegister fc); // SW8A ++ void fris_p(FloatRegister fb, FloatRegister fc); // SW8A ++ void fris_z(FloatRegister fb, FloatRegister fc); // SW8A ++ void fris_n(FloatRegister fb, FloatRegister fc); // SW8A ++ void frid(FloatRegister fb, FloatRegister fc); // SW8A ++ void frid_g(FloatRegister fb, FloatRegister fc); // SW8A ++ void frid_p(FloatRegister fb, FloatRegister fc); // SW8A ++ void frid_z(FloatRegister fb, FloatRegister fc); // SW8A ++ void frid_n(FloatRegister fb, FloatRegister fc); // SW8A ++ void fmas(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc); ++ void fmad(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc); ++ void fmss(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc); ++ void fmsd(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc); ++ void fnmas(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc); ++ void fnmad(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc); ++ void fnmss(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc); ++ void fnmsd(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc); ++ ++ void fseleq(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc); ++ void fselne(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc); ++ void fsellt(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc); ++ void fselle(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc); ++ void fselgt(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc); ++ void fselge(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc); ++ ++ void vaddw(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void vaddw(FloatRegister fa, int lit, FloatRegister fc); ++ void vsubw(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void vsubw(FloatRegister fa, int lit, FloatRegister fc); ++ void vcmpgew(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void vcmpgew(FloatRegister fa, int lit, FloatRegister fc); ++ void vcmpeqw(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void vcmpeqw(FloatRegister fa, int lit, FloatRegister fc); ++ void vcmplew(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void vcmplew(FloatRegister fa, int lit, FloatRegister fc); ++ void vcmpltw(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void vcmpltw(FloatRegister fa, int lit, FloatRegister fc); ++ void vcmpulew(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void vcmpulew(FloatRegister fa, int lit, FloatRegister fc); ++ void vcmpultw(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void vcmpultw(FloatRegister fa, int lit, FloatRegister fc); ++ void vsllw(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void vsllw(FloatRegister fa, int lit, FloatRegister fc); ++ void vsrlw(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void vsrlw(FloatRegister fa, int lit, FloatRegister fc); ++ void vsraw(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void vsraw(FloatRegister fa, int lit, FloatRegister fc); ++ void vrolw(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void vrolw(FloatRegister fa, int lit, FloatRegister fc); ++ void sllow(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void sllow(FloatRegister fa, int lit, FloatRegister fc); ++ void srlow(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void srlow(FloatRegister fa, int lit, FloatRegister fc); ++ void vaddl(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void vaddl(FloatRegister fa, int lit, FloatRegister fc); ++ void vsubl(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void vsubl(FloatRegister fa, int lit, FloatRegister fc); ++ void vsllb(FloatRegister fa, FloatRegister fb, FloatRegister fc); // SW8A ++ void vsllb(FloatRegister fa, int lit, FloatRegister fc); // SW8A ++ void vsrlb(FloatRegister fa, FloatRegister fb, FloatRegister fc); // SW8A ++ void vsrlb(FloatRegister fa, int lit, FloatRegister fc); // SW8A ++ void vsrab(FloatRegister fa, FloatRegister fb, FloatRegister fc); // SW8A ++ void vsrab(FloatRegister fa, int lit, FloatRegister fc); // SW8A ++ void vrolb(FloatRegister fa, FloatRegister fb, FloatRegister fc); // SW8A ++ void vrolb(FloatRegister fa, int lit, FloatRegister fc); // SW8A ++ void vsllh(FloatRegister fa, FloatRegister fb, FloatRegister fc); // SW8A ++ void vsllh(FloatRegister fa, int lit, FloatRegister fc); // SW8A ++ void vsrlh(FloatRegister fa, FloatRegister fb, FloatRegister fc); // SW8A ++ void vsrlh(FloatRegister fa, int lit, FloatRegister fc); // SW8A ++ void vsrah(FloatRegister fa, FloatRegister fb, FloatRegister fc); // SW8A ++ void vsrah(FloatRegister fa, int lit, FloatRegister fc); // SW8A ++ void vrolh(FloatRegister fa, FloatRegister fb, FloatRegister fc); // SW8A ++ void vrolh(FloatRegister fa, int lit, FloatRegister fc); // SW8A ++ void ctpopow(FloatRegister fa, FloatRegister fc); ++ void ctlzow(FloatRegister fa, FloatRegister fc); ++ void vslll(FloatRegister fa, FloatRegister fb, FloatRegister fc); // SW8A ++ void vslll(FloatRegister fa, int lit, FloatRegister fc); // SW8A ++ void vsrll(FloatRegister fa, FloatRegister fb, FloatRegister fc); // SW8A ++ void vsrll(FloatRegister fa, int lit, FloatRegister fc); // SW8A ++ void vsral(FloatRegister fa, FloatRegister fb, FloatRegister fc); // SW8A ++ void vsral(FloatRegister fa, int lit, FloatRegister fc); // SW8A ++ void vroll(FloatRegister fa, FloatRegister fb, FloatRegister fc); // SW8A ++ void vroll(FloatRegister fa, int lit, FloatRegister fc); // SW8A ++ void vmaxb(FloatRegister fa, FloatRegister fb, FloatRegister fc); // SW8A ++ void vminb(FloatRegister fa, FloatRegister fb, FloatRegister fc); // SW8A ++ ++ // some unimplemented SIMD ++ void vucaddw(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void vucaddw(FloatRegister fa, int lit, FloatRegister fc); ++ void vucsubw(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void vucsubw(FloatRegister fa, int lit, FloatRegister fc); ++ void vucaddh(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void vucaddh(FloatRegister fa, int lit, FloatRegister fc); ++ void vucsubh(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void vucsubh(FloatRegister fa, int lit, FloatRegister fc); ++ void vucaddb(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void vucaddb(FloatRegister fa, int lit, FloatRegister fc); ++ void vucsubb(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void vucsubb(FloatRegister fa, int lit, FloatRegister fc); ++ void sraow(FloatRegister fa, FloatRegister fb, FloatRegister fc); // SW8A ++ void sraow(FloatRegister fa, int lit, FloatRegister fc); // SW8A ++ void vsumw(FloatRegister fa, FloatRegister fc); // SW8A ++ void vsuml(FloatRegister fa, FloatRegister fc); // SW8A ++ void vcmpueqb(FloatRegister fa, FloatRegister fb, FloatRegister fc); // SW8A ++ void vcmpueqb(FloatRegister fa, int lit, FloatRegister fc); // SW8A ++ void vcmpugtb(FloatRegister fa, FloatRegister fb, FloatRegister fc); // SW8A ++ void vcmpugtb(FloatRegister fa, int lit, FloatRegister fc); // SW8A ++ void vmaxh(FloatRegister fa, FloatRegister fb, FloatRegister fc); // SW8A ++ void vminh(FloatRegister fa, FloatRegister fb, FloatRegister fc); // SW8A ++ void vmaxw(FloatRegister fa, FloatRegister fb, FloatRegister fc); // SW8A ++ void vminw(FloatRegister fa, FloatRegister fb, FloatRegister fc); // SW8A ++ void vmaxl(FloatRegister fa, FloatRegister fb, FloatRegister fc); // SW8A ++ void vminl(FloatRegister fa, FloatRegister fb, FloatRegister fc); // SW8A ++ void vumaxb(FloatRegister fa, FloatRegister fb, FloatRegister fc); // SW8A ++ void vuminb(FloatRegister fa, FloatRegister fb, FloatRegister fc); // SW8A ++ void vumaxh(FloatRegister fa, FloatRegister fb, FloatRegister fc); // SW8A ++ void vuminh(FloatRegister fa, FloatRegister fb, FloatRegister fc); // SW8A ++ void vumaxw(FloatRegister fa, FloatRegister fb, FloatRegister fc); // SW8A ++ void vuminw(FloatRegister fa, FloatRegister fb, FloatRegister fc); // SW8A ++ void vumaxl(FloatRegister fa, FloatRegister fb, FloatRegister fc); // SW8A ++ void vuminl(FloatRegister fa, FloatRegister fb, FloatRegister fc); // SW8A ++ ++ void vsm3msw(FloatRegister fa, FloatRegister fb, FloatRegister fc); // SW8A ++ void vsm4key(FloatRegister fa, int lit, FloatRegister fc); // SW8A ++ void vsm4r(FloatRegister fa, FloatRegister fb, FloatRegister fc); // SW8A ++ void vbinvw(FloatRegister fb, FloatRegister fc); // SW8A ++ ++ void vadds(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void vaddd(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void vsubs(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void vsubd(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void vmuls(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void vmuld(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void vdivs(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void vdivd(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void vsqrts(FloatRegister fb, FloatRegister fc); ++ void vsqrtd(FloatRegister fb, FloatRegister fc); ++ void vfcmpeq(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void vfcmple(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void vfcmplt(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void vfcmpun(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void vcpys(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void vfmov(FloatRegister fa, FloatRegister fc); ++ void vcpyse(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void vcpysn(FloatRegister fa, FloatRegister fb, FloatRegister fc); ++ void vsums(FloatRegister fa, FloatRegister fc); // SW8A ++ void vsumd(FloatRegister fa, FloatRegister fc); // SW8A ++ void vfrecs(FloatRegister fa, FloatRegister fc); // SW8A ++ void vfrecd(FloatRegister fa, FloatRegister fc); // SW8A ++ void vfcvtsd(FloatRegister fb, FloatRegister fc); // SW8A ++ void vfcvtds(FloatRegister fb, FloatRegister fc); // SW8A ++ void vfcvtls(FloatRegister fb, FloatRegister fc); // SW8A ++ void vfcvtld(FloatRegister fb, FloatRegister fc); // SW8A ++ void vfcvtdl(FloatRegister fb, FloatRegister fc); // SW8A ++ void vfcvtdl_g(FloatRegister fb, FloatRegister fc); // SW8A ++ void vfcvtdl_p(FloatRegister fb, FloatRegister fc); // SW8A ++ void vfcvtdl_z(FloatRegister fb, FloatRegister fc); // SW8A ++ void vfcvtdl_n(FloatRegister fb, FloatRegister fc); // SW8A ++ void vfris(FloatRegister fb, FloatRegister fc); // SW8A ++ void vfris_g(FloatRegister fb, FloatRegister fc); // SW8A ++ void vfris_p(FloatRegister fb, FloatRegister fc); // SW8A ++ void vfris_z(FloatRegister fb, FloatRegister fc); // SW8A ++ void vfris_n(FloatRegister fb, FloatRegister fc); // SW8A ++ void vfrid(FloatRegister fb, FloatRegister fc); // SW8A ++ void vfrid_g(FloatRegister fb, FloatRegister fc); // SW8A ++ void vfrid_p(FloatRegister fb, FloatRegister fc); // SW8A ++ void vfrid_z(FloatRegister fb, FloatRegister fc); // SW8A ++ void vfrid_n(FloatRegister fb, FloatRegister fc); // SW8A ++ void vmaxs(FloatRegister fa, FloatRegister fb, FloatRegister fc); // SW8A ++ void vmins(FloatRegister fa, FloatRegister fb, FloatRegister fc); // SW8A ++ void vmaxd(FloatRegister fa, FloatRegister fb, FloatRegister fc); // SW8A ++ void vmind(FloatRegister fa, FloatRegister fb, FloatRegister fc); // SW8A ++ // end of unimplemented SIMD ++ ++ void vmas(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc); ++ void vmad(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc); ++ void vmss(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc); ++ void vmsd(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc); ++ void vnmas(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc); ++ void vnmad(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc); ++ void vnmss(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc); ++ void vnmsd(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc); ++ void vfseleq(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc); ++ void vfsellt(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc); ++ void vfselle(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc); ++ void vseleqw(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc); ++ void vseleqw(FloatRegister fa, FloatRegister fb, int fmalit, ++ FloatRegister fc); ++ void vsellbcw(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc); ++ void vsellbcw(FloatRegister fa, FloatRegister fb, int fmalit, ++ FloatRegister fc); ++ void vselltw(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc); ++ void vselltw(FloatRegister fa, FloatRegister fb, int fmalit, ++ FloatRegister fc); ++ void vsellew(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc); ++ void vsellew(FloatRegister fa, FloatRegister fb, int fmalit, ++ FloatRegister fc); ++ void vinsw(FloatRegister fa, FloatRegister fb, int fmalit, FloatRegister fc); ++ void vinsf(FloatRegister fa, FloatRegister fb, int fmalit, FloatRegister fc); ++ void vextw(FloatRegister fa, int fmalit, FloatRegister fc); ++ void vextf(FloatRegister fa, int fmalit, FloatRegister fc); ++ void vcpyw(FloatRegister fa, FloatRegister fc); ++ void vcpyf(FloatRegister fa, FloatRegister fc); ++ void vconw(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc); ++ void vshfw(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc); ++ void vcons(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc); ++ void vcond(FloatRegister fa, FloatRegister fb, FloatRegister f3, ++ FloatRegister fc); ++ void vinsb(FloatRegister fa, FloatRegister fb, int fmalit, ++ FloatRegister fc); // SW8A ++ void vinsh(FloatRegister fa, FloatRegister fb, int fmalit, ++ FloatRegister fc); // SW8A ++ void vinsectlh(FloatRegister fa, FloatRegister fb, FloatRegister fc); // SW8A ++ void vinsectlw(FloatRegister fa, FloatRegister fb, FloatRegister fc); // SW8A ++ void vinsectll(FloatRegister fa, FloatRegister fb, FloatRegister fc); // SW8A ++ void vinsectlb(FloatRegister fa, FloatRegister fb, FloatRegister fc); // SW8A ++ void vshfq(FloatRegister fa, FloatRegister fb, int fmalit, ++ FloatRegister fc); // SW8A ++ void vshfqb(FloatRegister fa, FloatRegister fb, FloatRegister fc); // SW8A ++ void vcpyb(FloatRegister fa, FloatRegister fc); // SW8A ++ void vcpyh(FloatRegister fa, FloatRegister fc); // SW8A ++ void vsm3r(FloatRegister fa, FloatRegister fb, int fmalit, ++ FloatRegister fc); // SW8A ++ void vfcvtsh(FloatRegister fa, FloatRegister fb, int fmalit, ++ FloatRegister fc); // SW8A ++ void vfcvths(FloatRegister fa, FloatRegister fb, int fmalit, ++ FloatRegister fc); // SW8A ++ ++ void vldw_u(FloatRegister fa, int atmdisp, Register Rb); ++ void vstw_u(FloatRegister fa, int atmdisp, Register Rb); ++ void vlds_u(FloatRegister fa, int atmdisp, Register Rb); ++ void vsts_u(FloatRegister fa, int atmdisp, Register Rb); ++ void vldd_u(FloatRegister fa, int atmdisp, Register Rb); ++ void vstd_u(FloatRegister fa, int atmdisp, Register Rb); ++ void vstw_ul(FloatRegister fa, int atmdisp, Register Rb); ++ void vstw_uh(FloatRegister fa, int atmdisp, Register Rb); ++ void vsts_ul(FloatRegister fa, int atmdisp, Register Rb); ++ void vsts_uh(FloatRegister fa, int atmdisp, Register Rb); ++ void vstd_ul(FloatRegister fa, int atmdisp, Register Rb); ++ void vstd_uh(FloatRegister fa, int atmdisp, Register Rb); ++ void lbr(int palfn); // SW8A ++ void ldbu_a(Register Ra, int atmdisp, Register Rb); // SW8A ++ void ldhu_a(Register Ra, int atmdisp, Register Rb); // SW8A ++ void ldw_a(Register Ra, int atmdisp, Register Rb); // SW8A ++ void ldl_a(Register Ra, int atmdisp, Register Rb); // SW8A ++ void stb_a(Register Ra, int atmdisp, Register Rb); // SW8A ++ void sth_a(Register Ra, int atmdisp, Register Rb); // SW8A ++ void stw_a(Register Ra, int atmdisp, Register Rb); // SW8A ++ void stl_a(Register Ra, int atmdisp, Register Rb); // SW8A ++ void flds_a(FloatRegister fa, int atmdisp, Register Rb); // SW8A ++ void fldd_a(FloatRegister fa, int atmdisp, Register Rb); // SW8A ++ void fsts_a(FloatRegister fa, int atmdisp, Register Rb); // SW8A ++ void fstd_a(FloatRegister fa, int atmdisp, Register Rb); // SW8A ++ void dpfhr(int th, int atmdisp, Register Rb); // SW8A ++ void dpfhw(int th, int atmdisp, Register Rb); // SW8A ++ ++ // TODO: 0x1A.00-0x1c.E SIMD instructions. ++ ++ void ldbu(Register Ra, int mdisp, Register Rb); ++ void ldhu(Register Ra, int mdisp, Register Rb); ++ void ldw(Register Ra, int mdisp, Register Rb); ++ void ldl(Register Ra, int mdisp, Register Rb); ++ void ldl_u(Register Ra, int mdisp, Register Rb); ++ void pri_ld(Register Ra, int ev6hwdisp, Register Rb); ++ void flds(FloatRegister fa, int mdisp, Register Rb); ++ void fldd(FloatRegister fa, int mdisp, Register Rb); ++ ++ void stb(Register Ra, int mdisp, Register Rb); ++ void sth(Register Ra, int mdisp, Register Rb); ++ void stw(Register Ra, int mdisp, Register Rb); ++ void stl(Register Ra, int mdisp, Register Rb); ++ void stl_u(Register Ra, int mdisp, Register Rb); ++ void pri_st(Register Ra, int ev6hwdisp, Register Rb); ++ void fsts(FloatRegister fa, int mdisp, Register Rb); ++ void fstd(FloatRegister fa, int mdisp, Register Rb); ++ ++ void ldi(Register Ra, int mdisp, Register Rb); ++ void ldih(Register Ra, int mdisp, Register Rb); ++ ++ // cache control instruction ++ void s_fillcs(int mdisp, Register Rb); ++ void s_fillde(int mdisp, Register Rb); ++ void fillde(int mdisp, Register Rb); ++ void fillde_e(int mdisp, Register Rb); ++ void fillcs(int mdisp, Register Rb); ++ void fillcs_e(int mdisp, Register Rb); ++ void e_fillcs(int mdisp, Register Rb); ++ void e_fillde(int mdisp, Register Rb); ++ void flushd(int mdisp, Register Rb); ++ void evictdl(int mdisp, Register Rb); ++ void evictdg(int mdisp, Register Rb); ++ ++ // ------------Memory-instructions------------- ++ void Ldb(Register Ra, const MemOperand& rs); // sw add ++ void ldbu(Register Ra, const MemOperand& rs); ++ void Ldh(Register Ra, const MemOperand& rs); // sw add ++ void ldhu(Register Ra, const MemOperand& rs); ++ void ldw(Register Ra, const MemOperand& rs); ++ void Ldwu(Register Ra, const MemOperand& rs); // sw add ++ void ldl(Register Ra, const MemOperand& rs); ++ void flds(FloatRegister fa, const MemOperand& rs); ++ void fldd(FloatRegister fa, const MemOperand& rs); ++ ++ void stb(Register Ra, const MemOperand& rs); ++ void sth(Register Ra, const MemOperand& rs); ++ void stw(Register Ra, const MemOperand& rs); ++ void stl(Register Ra, const MemOperand& rs); ++ void fsts(FloatRegister fa, const MemOperand& rs); ++ void fstd(FloatRegister fa, const MemOperand& rs); ++ ++ void beq(Register Ra, Label* L) { beq(Ra, branch_offset(L) >> 2); } ++ void bne(Register Ra, Label* L) { bne(Ra, branch_offset(L) >> 2); } ++ void blt(Register Ra, Label* L) { blt(Ra, branch_offset(L) >> 2); } ++ void ble(Register Ra, Label* L) { ble(Ra, branch_offset(L) >> 2); } ++ void bgt(Register Ra, Label* L) { bgt(Ra, branch_offset(L) >> 2); } ++ void bge(Register Ra, Label* L) { bge(Ra, branch_offset(L) >> 2); } ++ ++ void fbr(Label* L) { fbeq(f31, branch_offset(L) >> 2); } ++ void fbeq(FloatRegister fa, Label* L) { fbeq(fa, branch_offset(L) >> 2); } ++ void fbne(FloatRegister fa, Label* L) { fbne(fa, branch_offset(L) >> 2); } ++ void fblt(FloatRegister fa, Label* L) { fblt(fa, branch_offset(L) >> 2); } ++ void fble(FloatRegister fa, Label* L) { fble(fa, branch_offset(L) >> 2); } ++ void fbgt(FloatRegister fa, Label* L) { fbgt(fa, branch_offset(L) >> 2); } ++ void fbge(FloatRegister fa, Label* L) { fbge(fa, branch_offset(L) >> 2); } ++ void fmov(FPURegister rs, FPURegister rd) { fcpys(rs, rs, rd); } ++ ++ // Check the code size generated from label to here. ++ int SizeOfCodeGeneratedSince(Label* label) { ++ return pc_offset() - label->pos(); ++ } ++ ++ // Check the number of instructions generated from label to here. ++ int InstructionsGeneratedSince(Label* label) { ++ return SizeOfCodeGeneratedSince(label) / kInstrSize; ++ } ++ ++ // Class for scoping postponing the trampoline pool generation. ++ class V8_NODISCARD BlockTrampolinePoolScope { ++ public: ++ explicit BlockTrampolinePoolScope(Assembler* assem) : assem_(assem) { ++ assem_->StartBlockTrampolinePool(); ++ } ++ ~BlockTrampolinePoolScope() { assem_->EndBlockTrampolinePool(); } ++ ++ private: ++ Assembler* assem_; ++ ++ DISALLOW_IMPLICIT_CONSTRUCTORS(BlockTrampolinePoolScope); ++ }; ++ ++ // Class for postponing the assembly buffer growth. Typically used for ++ // sequences of instructions that must be emitted as a unit, before ++ // buffer growth (and relocation) can occur. ++ // This blocking scope is not nestable. ++ class V8_NODISCARD BlockGrowBufferScope { ++ public: ++ explicit BlockGrowBufferScope(Assembler* assem) : assem_(assem) { ++ assem_->StartBlockGrowBuffer(); ++ } ++ ~BlockGrowBufferScope() { assem_->EndBlockGrowBuffer(); } ++ ++ private: ++ Assembler* assem_; ++ ++ DISALLOW_IMPLICIT_CONSTRUCTORS(BlockGrowBufferScope); ++ }; ++ ++ // Record a deoptimization reason that can be used by a log or cpu profiler. ++ // Use --trace-deopt to enable. ++ void RecordDeoptReason(DeoptimizeReason reason, uint32_t node_id, ++ SourcePosition position, int id); ++ ++ static int RelocateInternalReference(RelocInfo::Mode rmode, Address pc, ++ intptr_t pc_delta); ++ ++ // Writes a single byte or word of data in the code stream. Used for ++ // inline tables, e.g., jump-tables. ++ void db(uint8_t data); ++ void dd(uint32_t data, RelocInfo::Mode rmode = RelocInfo::NO_INFO); ++ void dq(uint64_t data, RelocInfo::Mode rmode = RelocInfo::NO_INFO); ++ void dp(uintptr_t data, RelocInfo::Mode rmode = RelocInfo::NO_INFO) { ++ dq(data, rmode); ++ } ++ void dd(Label* label); ++ ++ // Postpone the generation of the trampoline pool for the specified number of ++ // instructions. ++ void BlockTrampolinePoolFor(int instructions); ++ ++ // Check if there is less than kGap bytes available in the buffer. ++ // If this is the case, we need to grow the buffer before emitting ++ // an instruction or relocation information. ++ inline bool overflow() const { return pc_ >= reloc_info_writer.pos() - kGap; } ++ ++ // Get the number of bytes available in the buffer. ++ inline intptr_t available_space() const { ++ return reloc_info_writer.pos() - pc_; ++ } ++ ++ // Read/patch instructions. ++ static Instr instr_at(Address pc) { return *reinterpret_cast(pc); } ++ static void instr_at_put(Address pc, Instr instr) { ++ *reinterpret_cast(pc) = instr; ++ } ++ Instr instr_at(int pos) { ++ return *reinterpret_cast(buffer_start_ + pos); ++ } ++ void instr_at_put(int pos, Instr instr) { ++ *reinterpret_cast(buffer_start_ + pos) = instr; ++ } ++ ++ // Check if an instruction is a branch of some kind. ++ static bool IsLdih(Instr instr); ++ static bool IsLdi(Instr instr); ++ static bool IsBranch(Instr instr); ++ ++ static bool IsBeq(Instr instr); ++ static bool IsBne(Instr instr); ++ ++ static uint32_t GetLabelConst(Instr instr); ++ ++ static uint32_t GetSwRa(Instr instr); ++ static uint32_t GetSwRb(Instr instr); ++ static uint32_t GetSwRc(Instr instr); ++ ++ static int32_t GetSwOpcodeField(Instr instr); ++ static int32_t GetSwOpcodeAndFunctionField(Instr instr); ++ static uint32_t GetSwImmediate8(Instr instr); ++ static uint32_t GetSwImmediate16(Instr instr); ++ ++ static bool IsAddImmediate(Instr instr); ++ ++ static bool IsAndImmediate(Instr instr); ++ static bool IsEmittedConstant(Instr instr); ++ ++ void CheckTrampolinePool(); ++ ++ inline int UnboundLabelsCount() { return unbound_labels_count_; } ++ ++ bool is_trampoline_emitted() const { return trampoline_emitted_; } ++ ++ protected: ++ // Readable constants for base and offset adjustment helper, these indicate if ++ // aside from offset, another value like offset + 4 should fit into int16. ++ enum class OffsetAccessType : bool { ++ SINGLE_ACCESS = false, ++ TWO_ACCESSES = true ++ }; ++ ++ // Helper function for memory load/store using base register and offset. ++ void AdjustBaseAndOffset( ++ MemOperand* src, ++ OffsetAccessType access_type = OffsetAccessType::SINGLE_ACCESS, ++ int second_access_add_to_offset = 4); ++ ++ inline static void set_target_internal_reference_encoded_at(Address pc, ++ Address target); ++ ++ int64_t buffer_space() const { return reloc_info_writer.pos() - pc_; } ++ ++ // Decode branch instruction at pos and return branch target pos. ++ int target_at(int pos, bool is_internal); ++ ++ // Patch branch instruction at pos to branch to given branch target pos. ++ void target_at_put(int pos, int target_pos, bool is_internal); ++ ++ // Say if we need to relocate with this mode. ++ bool MustUseReg(RelocInfo::Mode rmode); ++ ++ // Record reloc info for current pc_. ++ void RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data = 0); ++ ++ // Block the emission of the trampoline pool before pc_offset. ++ void BlockTrampolinePoolBefore(int pc_offset) { ++ if (no_trampoline_pool_before_ < pc_offset) ++ no_trampoline_pool_before_ = pc_offset; ++ } ++ ++ void StartBlockTrampolinePool() { trampoline_pool_blocked_nesting_++; } ++ ++ void EndBlockTrampolinePool() { ++ trampoline_pool_blocked_nesting_--; ++ if (trampoline_pool_blocked_nesting_ == 0) { ++ CheckTrampolinePoolQuick(1); ++ } ++ } ++ ++ bool is_trampoline_pool_blocked() const { ++ return trampoline_pool_blocked_nesting_ > 0; ++ } ++ ++ bool has_exception() const { return internal_trampoline_exception_; } ++ ++ // Temporarily block automatic assembly buffer growth. ++ void StartBlockGrowBuffer() { ++ DCHECK(!block_buffer_growth_); ++ block_buffer_growth_ = true; ++ } ++ ++ void EndBlockGrowBuffer() { ++ DCHECK(block_buffer_growth_); ++ block_buffer_growth_ = false; ++ } ++ ++ bool is_buffer_growth_blocked() const { return block_buffer_growth_; } ++ ++ void CheckTrampolinePoolQuick(int extra_instructions = 0) { ++ if (pc_offset() >= next_buffer_check_ - extra_instructions * kInstrSize) { ++ CheckTrampolinePool(); ++ } ++ } ++ ++ void set_pc_for_safepoint() { pc_for_safepoint_ = pc_; } ++ ++ private: ++ // Avoid overflows for displacements etc. ++ static const int kMaximalBufferSize = 512 * MB; ++ ++ // Buffer size and constant pool distance are checked together at regular ++ // intervals of kBufferCheckInterval emitted bytes. ++ static constexpr int kBufferCheckInterval = 1 * KB / 2; ++ ++ // InstructionStream generation. ++ // The relocation writer's position is at least kGap bytes below the end of ++ // the generated instructions. This is so that multi-instruction sequences do ++ // not have to check for overflow. The same is true for writes of large ++ // relocation info entries. ++ static constexpr int kGap = 64; ++ STATIC_ASSERT(AssemblerBase::kMinimalBufferSize >= 2 * kGap); ++ ++ // Repeated checking whether the trampoline pool should be emitted is rather ++ // expensive. By default we only check again once a number of instructions ++ // has been generated. ++ static constexpr int kCheckConstIntervalInst = 32; ++ static constexpr int kCheckConstInterval = ++ kCheckConstIntervalInst * kInstrSize; ++ ++ int next_buffer_check_; // pc offset of next buffer check. ++ ++ // Emission of the trampoline pool may be blocked in some code sequences. ++ int trampoline_pool_blocked_nesting_; // Block emission if this is not zero. ++ int no_trampoline_pool_before_; // Block emission before this pc offset. ++ ++ // Keep track of the last emitted pool to guarantee a maximal distance. ++ int last_trampoline_pool_end_; // pc offset of the end of the last pool. ++ ++ // Automatic growth of the assembly buffer may be blocked for some sequences. ++ bool block_buffer_growth_; // Block growth when true. ++ ++ // Relocation information generation. ++ // Each relocation is encoded as a variable size value. ++ static constexpr int kMaxRelocSize = RelocInfoWriter::kMaxSize; ++ RelocInfoWriter reloc_info_writer; ++ ++ // The bound position, before this we cannot do instruction elimination. ++ int last_bound_pos_; ++ ++ // InstructionStream emission. ++ inline void CheckBuffer(); ++ void GrowBuffer(); ++ inline void CheckForEmitInForbiddenSlot(); ++ template ++ inline void EmitHelper(T x); ++ inline void EmitHelper(Instr x); ++ inline void EmitHelper(uint8_t x); ++ inline void emit(Instr x); ++ inline void emit(uint64_t x); ++ ++ // Instruction generation. ++ // We have 3 different kind of encoding layout on SW64. ++ // However due to many different types of objects encoded in the same fields ++ // we have quite a few aliases for each mode. ++ // Using the same structure to refer to Register and FPURegister would spare a ++ // few aliases, but mixing both does not look clean to me. ++ // Anyway we could surely implement this differently. ++ ++ void GenInstrB_SW(Opcode_ops_bra opcode, Register Ra, int32_t disp); ++ ++ void GenInstrFB_SW(Opcode_ops_bra opcode, FloatRegister fa, int32_t disp); ++ ++ void GenInstrM_SW(Opcode_ops_mem opcode, Register Ra, int16_t disp, ++ Register Rb); ++ ++ void GenInstrFM_SW(Opcode_ops_mem opcode, FloatRegister fa, int16_t disp, ++ Register Rb); ++ ++ void GenInstrMWithFun_SW(Opcode_ops_atmem opcode, Register Ra, int16_t disp, ++ Register Rb); ++ ++ void GenInstrR_SW(Opcode_ops_opr opcode, Register Ra, Register Rb, ++ Register Rc); ++ ++ void GenInstrI_SW(Opcode_ops_oprl opcode, Register Ra, int16_t imm, ++ Register Rc); ++ ++ void GenInstrFR_SW(Opcode_ops_fp opcode, FloatRegister fa, FloatRegister fb, ++ FloatRegister fc); ++ ++ void GenInstrFR_SW(Opcode_ops_fp opcode, FloatRegister fb, FloatRegister fc); ++ ++ void GenInstrFR_SW(Opcode_ops_fpl opcode, FloatRegister fa, int16_t imm, ++ FloatRegister fc); ++ ++ void GenInstrFR_SW(Opcode_ops_fpl opcode, FloatRegister fa, FloatRegister fb, ++ int16_t fmalit, FloatRegister fc); ++ ++ void GenInstrFMA_SW(Opcode_ops_fmal opcode, FloatRegister fa, ++ FloatRegister fb, int16_t fmalit, FloatRegister fc); ++ ++ void GenInstrFMA_SW(Opcode_ops_fmal opcode, FloatRegister fa, int16_t fmalit, ++ FloatRegister fc); ++ ++ void GenInstrFMA_SW(Opcode_ops_fma opcode, FloatRegister fa, FloatRegister fb, ++ FloatRegister f3, FloatRegister fc); ++ ++ void GenInstrFMA_SW(Opcode_ops_fma opcode, FloatRegister fa, FloatRegister fb, ++ FloatRegister fc); ++ ++ void GenInstrSWSA_SW(Opcode_ops_atmem opcode, FloatRegister fa, ++ int16_t atmdisp, Register Rb); ++ ++ void GenInstrSelR_SW(Opcode_ops_sel opcode, Register Ra, Register Rb, ++ Register R3, Register Rc); ++ ++ void GenInstrSelI_SW(Opcode_ops_sel_l opcode, Register Ra, int32_t imm, ++ Register R3, Register Rc); ++ ++ // Helpers. ++ void SwLoadRegPlusOffsetToAt(const MemOperand& src); ++ ++ // Labels. ++ void print(const Label* L); ++ void bind_to(Label* L, int pos); ++ void next(Label* L, bool is_internal); ++ ++ // One trampoline consists of: ++ // - space for trampoline slots, ++ // - space for labels. ++ // ++ // Space for trampoline slots is equal to slot_count * 2 * kInstrSize. ++ // Space for trampoline slots precedes space for labels. Each label is of one ++ // instruction size, so total amount for labels is equal to ++ // label_count * kInstrSize. ++ class Trampoline { ++ public: ++ Trampoline() { ++ start_ = 0; ++ next_slot_ = 0; ++ free_slot_count_ = 0; ++ end_ = 0; ++ } ++ Trampoline(int start, int slot_count) { ++ start_ = start; ++ next_slot_ = start; ++ free_slot_count_ = slot_count; ++ end_ = start + slot_count * kTrampolineSlotsSize; ++ } ++ int start() { return start_; } ++ int end() { return end_; } ++ int take_slot() { ++ int trampoline_slot = kInvalidSlotPos; ++ if (free_slot_count_ <= 0) { ++ // We have run out of space on trampolines. ++ // Make sure we fail in debug mode, so we become aware of each case ++ // when this happens. ++ DCHECK(0); ++ // Internal exception will be caught. ++ } else { ++ trampoline_slot = next_slot_; ++ free_slot_count_--; ++ next_slot_ += kTrampolineSlotsSize; ++ } ++ return trampoline_slot; ++ } ++ ++ private: ++ int start_; ++ int end_; ++ int next_slot_; ++ int free_slot_count_; ++ }; ++ ++ int32_t get_trampoline_entry(int32_t pos); ++ int unbound_labels_count_; ++ // After trampoline is emitted, long branches are used in generated code for ++ // the forward branches whose target offsets could be beyond reach of branch ++ // instruction. We use this information to trigger different mode of ++ // branch instruction generation, where we use jump instructions rather ++ // than regular branch instructions. ++ bool trampoline_emitted_; ++ static constexpr int kInvalidSlotPos = -1; ++ ++ // Internal reference positions, required for unbounded internal reference ++ // labels. ++ std::set internal_reference_positions_; ++ bool is_internal_reference(Label* L) { ++ return internal_reference_positions_.find(L->pos()) != ++ internal_reference_positions_.end(); ++ } ++ ++ Trampoline trampoline_; ++ bool internal_trampoline_exception_; ++ ++ // Keep track of the last Call's position to ensure that safepoint can get the ++ // correct information even if there is a trampoline immediately after the ++ // Call. ++ byte* pc_for_safepoint_; ++ ++ RegList scratch_register_list_; ++ ++ private: ++ void AllocateAndInstallRequestedHeapObjects(Isolate* isolate); ++ ++ int WriteCodeComments(); ++ ++ friend class RegExpMacroAssemblerSW64; ++ friend class RelocInfo; ++ friend class BlockTrampolinePoolScope; ++ friend class EnsureSpace; ++}; ++ ++class EnsureSpace { ++ public: ++ explicit inline EnsureSpace(Assembler* assembler); ++}; ++ ++class V8_EXPORT_PRIVATE V8_NODISCARD UseScratchRegisterScope { ++ public: ++ explicit UseScratchRegisterScope(Assembler* assembler); ++ ~UseScratchRegisterScope(); ++ ++ Register Acquire(); ++ bool hasAvailable() const; ++ ++ void Include(const RegList& list) { *available_ |= list; } ++ void Exclude(const RegList& list) { available_->clear(list); } ++ void Include(const Register& reg1, const Register& reg2 = no_reg) { ++ RegList list({reg1, reg2}); ++ Include(list); ++ } ++ void Exclude(const Register& reg1, const Register& reg2 = no_reg) { ++ RegList list({reg1, reg2}); ++ Exclude(list); ++ } ++ ++ private: ++ RegList* available_; ++ RegList old_available_; ++}; ++ ++} // namespace internal ++} // namespace v8 ++ ++#endif // V8_CODEGEN_SW64_ASSEMBLER_SW64_H_ +diff --git a/deps/v8/src/codegen/sw64/constants-sw64.cc b/deps/v8/src/codegen/sw64/constants-sw64.cc +new file mode 100644 +index 00000000..7209a261 +--- /dev/null ++++ b/deps/v8/src/codegen/sw64/constants-sw64.cc +@@ -0,0 +1,143 @@ ++// 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. ++ ++#if V8_TARGET_ARCH_SW64 ++ ++#include "src/codegen/sw64/constants-sw64.h" ++ ++namespace v8 { ++namespace internal { ++ ++// ----------------------------------------------------------------------------- ++// Registers. ++ ++// These register names are defined in a way to match the native disassembler ++// formatting. See for example the command "objdump -d ". ++const char* Registers::names_[kNumRegisters] = { ++ "v0", "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "s0", "s1", ++ "s2", "s3", "s4", "s5", "fp", "a0", "a1", "a2", "a3", "a4", "a5", ++ "t8", "t9", "t10", "t11", "ra", "t12", "at", "gp", "sp", "zero_reg"}; ++ ++// List of alias names which can be used when referring to SW64 registers. ++const Registers::RegisterAlias Registers::aliases_[] = { ++ {14, "cp"}, ++ {15, "s6"}, ++ {15, "s6_fp"}, ++ {31, "zero_reg"}, ++ {kInvalidRegister, nullptr}}; ++ ++const char* Registers::Name(int reg) { ++ const char* result; ++ if ((0 <= reg) && (reg < kNumRegisters)) { ++ result = names_[reg]; ++ } else { ++ result = "noreg"; ++ } ++ return result; ++} ++ ++int Registers::Number(const char* name) { ++ // Look through the canonical names. ++ for (int i = 0; i < kNumRegisters; i++) { ++ if (strcmp(names_[i], name) == 0) { ++ return i; ++ } ++ } ++ ++ // Look through the alias names. ++ int i = 0; ++ while (aliases_[i].reg != kInvalidRegister) { ++ if (strcmp(aliases_[i].name, name) == 0) { ++ return aliases_[i].reg; ++ } ++ i++; ++ } ++ ++ // No register with the reguested name found. ++ return kInvalidRegister; ++} ++ ++const char* FPURegisters::names_[kNumFPURegisters] = { ++ "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"}; ++ ++// List of alias names which can be used when referring to SW64 registers. ++const FPURegisters::RegisterAlias FPURegisters::aliases_[] = { ++ {kInvalidRegister, nullptr}}; ++ ++const char* FPURegisters::Name(int creg) { ++ const char* result; ++ if ((0 <= creg) && (creg < kNumFPURegisters)) { ++ result = names_[creg]; ++ } else { ++ result = "nocreg"; ++ } ++ return result; ++} ++ ++int FPURegisters::Number(const char* name) { ++ // Look through the canonical names. ++ for (int i = 0; i < kNumFPURegisters; i++) { ++ if (strcmp(names_[i], name) == 0) { ++ return i; ++ } ++ } ++ ++ // Look through the alias names. ++ int i = 0; ++ while (aliases_[i].creg != kInvalidRegister) { ++ if (strcmp(aliases_[i].name, name) == 0) { ++ return aliases_[i].creg; ++ } ++ i++; ++ } ++ ++ // No Cregister with the reguested name found. ++ return kInvalidFPURegister; ++} ++ ++const char* SWSARegisters::names_[kNumSWSARegisters] = { ++ "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7", "v8", "v9", "v10", ++ "v11", "v12", "v13", "v14", "v15", "v16", "v17", "v18", "v19", "v20", "v21", ++ "v22", "v23", "v24", "v25", "v26", "v27", "v28", "v29", "v30", "v31"}; ++ ++const SWSARegisters::RegisterAlias SWSARegisters::aliases_[] = { ++ {kInvalidRegister, nullptr}}; ++ ++const char* SWSARegisters::Name(int creg) { ++ const char* result; ++ if ((0 <= creg) && (creg < kNumSWSARegisters)) { ++ result = names_[creg]; ++ } else { ++ result = "nocreg"; ++ } ++ return result; ++} ++ ++int SWSARegisters::Number(const char* name) { ++ // Look through the canonical names. ++ for (int i = 0; i < kNumSWSARegisters; i++) { ++ if (strcmp(names_[i], name) == 0) { ++ return i; ++ } ++ } ++ ++ // Look through the alias names. ++ int i = 0; ++ while (aliases_[i].creg != kInvalidRegister) { ++ if (strcmp(aliases_[i].name, name) == 0) { ++ return aliases_[i].creg; ++ } ++ i++; ++ } ++ ++ // No Cregister with the reguested name found. ++ return kInvalidSWSARegister; ++} ++ ++} // namespace internal ++} // namespace v8 ++ ++#endif // V8_TARGET_ARCH_SW64 +diff --git a/deps/v8/src/codegen/sw64/constants-sw64.h b/deps/v8/src/codegen/sw64/constants-sw64.h +new file mode 100644 +index 00000000..d6f1702b +--- /dev/null ++++ b/deps/v8/src/codegen/sw64/constants-sw64.h +@@ -0,0 +1,1625 @@ ++// Copyright 2012 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef V8_CODEGEN_SW64_CONSTANTS_SW64_H_ ++#define V8_CODEGEN_SW64_CONSTANTS_SW64_H_ ++ ++#include "src/base/logging.h" ++#include "src/base/macros.h" ++#include "src/common/globals.h" ++ ++// UNIMPLEMENTED_ macro for SW64. ++#ifdef DEBUG ++#define UNIMPLEMENTED_SW64() \ ++ v8::internal::PrintF("%s, \tline %d: \tfunction %s not implemented. \n", \ ++ __FILE__, __LINE__, __func__) ++#else ++#define UNIMPLEMENTED_SW64() ++#endif ++ ++#define UNSUPPORTED_SW64() v8::internal::PrintF("Unsupported instruction.\n") ++ ++// TODO(plind): consider renaming these ... ++#if (defined(__sw64_hard_float) && __sw64_hard_float != 0) ++// Use floating-point coprocessor instructions. This flag is raised when ++// -mhard-float is passed to the compiler. ++const bool IsSw64SoftFloatABI = false; ++#elif (defined(__sw64_soft_float) && __sw64_soft_float != 0) ++// This flag is raised when -msoft-float is passed to the compiler. ++// Although FPU is a base requirement for v8, soft-float ABI is used ++// on soft-float systems with FPU kernel emulation. ++const bool IsSw64SoftFloatABI = true; ++#else ++const bool IsSw64SoftFloatABI = true; ++#endif ++ ++const uint32_t kSwLwrOffset = 0; ++const uint32_t kSwLwlOffset = 3; ++const uint32_t kSwSwrOffset = 0; ++const uint32_t kSwSwlOffset = 3; ++const uint32_t kSwLdrOffset = 0; ++const uint32_t kSwLdlOffset = 7; ++const uint32_t kSwSdrOffset = 0; ++const uint32_t kSwSdlOffset = 7; ++ ++#ifndef __STDC_FORMAT_MACROS ++#define __STDC_FORMAT_MACROS ++#endif ++#include ++ ++// Defines constants and accessor classes to assemble, disassemble ++// ++ ++namespace v8 { ++namespace internal { ++ ++// TODO(sigurds): Change this value once we use relative jumps. ++constexpr size_t kMaxPCRelativeCodeRangeInMB = 0; ++ ++// ----------------------------------------------------------------------------- ++// Registers and FPURegisters. ++ ++// Number of general purpose registers. ++const int kNumRegisters = 32; ++const int kInvalidRegister = -1; ++ ++// Number of registers with HI, LO, and pc. ++const int kNumSimuRegisters = 35; ++ ++// In the simulator, the PC register is simulated as the 34th register. ++const int kPCRegister = 34; ++ ++// Number coprocessor registers. ++const int kNumFPURegisters = 32; ++const int kInvalidFPURegister = -1; ++ ++// Number of SWSA registers ++const int kNumSWSARegisters = 32; ++const int kInvalidSWSARegister = -1; ++ ++const int kInvalidSWSAControlRegister = -1; ++const int kSWSAIRRegister = 0; ++const int kSWSACSRRegister = 1; ++const int kSWSARegSize = 128; ++const int kSWSALanesByte = kSWSARegSize / 8; ++const int kSWSALanesHalf = kSWSARegSize / 16; ++const int kSWSALanesWord = kSWSARegSize / 32; ++const int kSWSALanesDword = kSWSARegSize / 64; ++ ++// FPU (coprocessor 1) control registers. Currently only FPCR is implemented. ++const int kFPCRRegister = 31; ++const int kInvalidFPUControlRegister = -1; ++const uint32_t kFPUInvalidResult = static_cast(1u << 31) - 1; ++const int32_t kFPUInvalidResultNegative = static_cast(1u << 31); ++const uint64_t kFPU64InvalidResult = ++ static_cast(static_cast(1) << 63) - 1; ++const int64_t kFPU64InvalidResultNegative = ++ static_cast(static_cast(1) << 63); ++ ++// FPCR constants. ++const uint32_t kFPCRInexactFlagBit = 2; ++const uint32_t kFPCRUnderflowFlagBit = 3; ++const uint32_t kFPCROverflowFlagBit = 4; ++const uint32_t kFPCRDivideByZeroFlagBit = 5; ++const uint32_t kFPCRInvalidOpFlagBit = 6; ++const uint32_t kFPCRNaN2008FlagBit = 18; ++ ++const uint32_t kFPCRInexactFlagMask = 1 << kFPCRInexactFlagBit; ++const uint32_t kFPCRUnderflowFlagMask = 1 << kFPCRUnderflowFlagBit; ++const uint32_t kFPCROverflowFlagMask = 1 << kFPCROverflowFlagBit; ++const uint32_t kFPCRDivideByZeroFlagMask = 1 << kFPCRDivideByZeroFlagBit; ++const uint32_t kFPCRInvalidOpFlagMask = 1 << kFPCRInvalidOpFlagBit; ++const uint32_t kFPCRNaN2008FlagMask = 1 << kFPCRNaN2008FlagBit; ++ ++const uint32_t kFPCRFlagMask = ++ kFPCRInexactFlagMask | kFPCRUnderflowFlagMask | kFPCROverflowFlagMask | ++ kFPCRDivideByZeroFlagMask | kFPCRInvalidOpFlagMask; ++ ++const uint32_t kFPCRExceptionFlagMask = kFPCRFlagMask ^ kFPCRInexactFlagMask; ++// SKTODO ++const uint32_t kFPCRInexactCauseBit = 12; ++const uint32_t kFPCRUnderflowCauseBit = 13; ++const uint32_t kFPCROverflowCauseBit = 14; ++const uint32_t kFPCRDivideByZeroCauseBit = 15; ++const uint32_t kFPCRInvalidOpCauseBit = 16; ++const uint32_t kFPCRUnimplementedOpCauseBit = 17; ++ ++const uint32_t kFPCRInexactCauseMask = 1 << kFPCRInexactCauseBit; ++const uint32_t kFPCRUnderflowCauseMask = 1 << kFPCRUnderflowCauseBit; ++const uint32_t kFPCROverflowCauseMask = 1 << kFPCROverflowCauseBit; ++const uint32_t kFPCRDivideByZeroCauseMask = 1 << kFPCRDivideByZeroCauseBit; ++const uint32_t kFPCRInvalidOpCauseMask = 1 << kFPCRInvalidOpCauseBit; ++const uint32_t kFPCRUnimplementedOpCauseMask = 1 ++ << kFPCRUnimplementedOpCauseBit; ++ ++const uint32_t kFPCRCauseMask = ++ kFPCRInexactCauseMask | kFPCRUnderflowCauseMask | kFPCROverflowCauseMask | ++ kFPCRDivideByZeroCauseMask | kFPCRInvalidOpCauseMask | ++ kFPCRUnimplementedOpCauseBit; ++ ++// SW64 FPCR constants. ++const uint64_t sFPCROverflowIntegerFlagBit = 57; ++const uint64_t sFPCRInexactFlagBit = 56; ++const uint64_t sFPCRUnderflowFlagBit = 55; ++const uint64_t sFPCROverflowFlagBit = 54; ++const uint64_t sFPCRDivideByZeroFlagBit = 53; ++const uint64_t sFPCRInvalidOpFlagBit = 52; ++ ++const uint64_t sFPCRInexactControlBit = 62; ++const uint64_t sFPCRUnderflowControlBit = 61; ++const uint64_t sFPCROverflowControlBit = 51; ++const uint64_t sFPCRDivideByZeroControlBit = 50; ++const uint64_t sFPCRInvalidOpControlBit = 49; ++ ++const uint64_t sFPCRRound0Bit = 58; ++const uint64_t sFPCRRound1Bit = 59; ++ ++const uint64_t sFPCRFloatOverflowControlBit = 45; // in 8a ++const uint64_t sFPCRunnormalizedControlBit = 47; // in 8a ++const uint64_t sFPCRunnormalizedEnableBit = 48; // in 8a ++ ++const uint64_t sFPCRControlMask = (0x1UL << sFPCRInexactControlBit) | ++ (0x1UL << sFPCRUnderflowControlBit) | ++ (0x1UL << sFPCROverflowControlBit) | ++ (0x1UL << sFPCRDivideByZeroControlBit) | ++ (0x1UL << sFPCRInvalidOpControlBit); ++ ++const uint64_t sFPCR_8aControlMask = ++ (0x1UL << sFPCRInexactControlBit) | (0x1UL << sFPCRUnderflowControlBit) | ++ (0x1UL << sFPCROverflowControlBit) | ++ (0x1UL << sFPCRDivideByZeroControlBit) | ++ // (0x1UL << sFPCRFloatOverflowControlBit)| ++ (0x1UL << sFPCRunnormalizedControlBit) | ++ (0x1UL << sFPCRunnormalizedEnableBit) | (0x1UL << sFPCRInvalidOpControlBit); ++ ++const uint64_t sFPCRRound0Mask = 0x1UL << sFPCRRound0Bit; ++const uint64_t sFPCRRound1Mask = 0x1UL << sFPCRRound1Bit; ++ ++const uint64_t sFPCROverflowIntegerFlagMask = 0x1UL ++ << sFPCROverflowIntegerFlagBit; ++const uint64_t sFPCRInexactFlagMask = 0x1UL << sFPCRInexactFlagBit; ++const uint64_t sFPCRUnderflowFlagMask = 0x1UL << sFPCRUnderflowFlagBit; ++const uint64_t sFPCROverflowFlagMask = 0x1UL << sFPCROverflowFlagBit; ++const uint64_t sFPCRDivideByZeroFlagMask = 0x1UL << sFPCRDivideByZeroFlagBit; ++const uint64_t sFPCRInvalidOpFlagMask = 0x1UL << sFPCRInvalidOpFlagBit; ++ ++const uint64_t sFPCRFlagMask = ++ sFPCROverflowIntegerFlagMask | sFPCRInexactFlagMask | ++ sFPCRUnderflowFlagMask | sFPCROverflowFlagMask | sFPCRDivideByZeroFlagMask | ++ sFPCRInvalidOpFlagMask; ++ ++const uint64_t sFPCRExceptionFlagMask = sFPCRFlagMask ^ sFPCRInexactFlagMask; ++ ++// 'pref' instruction hints ++const int32_t kPrefHintLoad = 0; ++const int32_t kPrefHintStore = 1; ++const int32_t kPrefHintLoadStreamed = 4; ++const int32_t kPrefHintStoreStreamed = 5; ++const int32_t kPrefHintLoadRetained = 6; ++const int32_t kPrefHintStoreRetained = 7; ++const int32_t kPrefHintWritebackInvalidate = 25; ++const int32_t kPrefHintPrepareForStore = 30; ++ ++// Actual value of root register is offset from the root array's start ++// to take advantage of negative displacement values. ++constexpr int kRootRegisterBias = 256; ++ ++// Helper functions for converting between register numbers and names. ++class Registers { ++ public: ++ // Return the name of the register. ++ static const char* Name(int reg); ++ ++ // Lookup the register number for the name provided. ++ static int Number(const char* name); ++ ++ struct RegisterAlias { ++ int reg; ++ const char* name; ++ }; ++ ++ static const int64_t kMaxValue = 0x7fffffffffffffffl; ++ static const int64_t kMinValue = 0x8000000000000000l; ++ ++ private: ++ static const char* names_[kNumRegisters]; ++ static const RegisterAlias aliases_[]; ++}; ++ ++// Helper functions for converting between register numbers and names. ++class FPURegisters { ++ public: ++ // Return the name of the register. ++ static const char* Name(int reg); ++ ++ // Lookup the register number for the name provided. ++ static int Number(const char* name); ++ ++ struct RegisterAlias { ++ int creg; ++ const char* name; ++ }; ++ ++ private: ++ static const char* names_[kNumFPURegisters]; ++ static const RegisterAlias aliases_[]; ++}; ++ ++// Helper functions for converting between register numbers and names. ++class SWSARegisters { ++ public: ++ // Return the name of the register. ++ static const char* Name(int reg); ++ ++ // Lookup the register number for the name provided. ++ static int Number(const char* name); ++ ++ struct RegisterAlias { ++ int creg; ++ const char* name; ++ }; ++ ++ private: ++ static const char* names_[kNumSWSARegisters]; ++ static const RegisterAlias aliases_[]; ++}; ++ ++// SWSA sizes. ++enum SWSASize { SWSA_B = 0x0, SWSA_H = 0x1, SWSA_W = 0x2, SWSA_D = 0x3 }; ++ ++// SWSA data type, top bit set for unsigned data types. ++enum SWSADataType { ++ SWSAS8 = 0, ++ SWSAS16 = 1, ++ SWSAS32 = 2, ++ SWSAS64 = 3, ++ SWSAU8 = 4, ++ SWSAU16 = 5, ++ SWSAU32 = 6, ++ SWSAU64 = 7 ++}; ++ ++// ----------------------------------------------------------------------------- ++// Instructions encoding constants. ++ ++// On SW64 all instructions are 32 bits. ++using Instr = int32_t; ++ ++// Special Software Interrupt codes when used in the presence of the SW64 ++// simulator. ++enum SoftwareInterruptCodes { ++ // Transition to C code. ++ call_rt_redirected = 0xfffff ++}; ++ ++// On SW64 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 kMaxWatchpointCode = 31; ++const uint32_t kMaxStopCode = 127; ++STATIC_ASSERT(kMaxWatchpointCode < kMaxStopCode); ++ ++// ----- Fields offset and length. ++const int kOpcodeShift = 26; ++const int kOpcodeBits = 6; ++const int kRsShift = 21; ++const int kRsBits = 5; ++const int kRtShift = 16; ++const int kRtBits = 5; ++const int kRdShift = 11; ++const int kRdBits = 5; ++const int kSaShift = 6; ++const int kSaBits = 5; ++const int kFunctionShift = 0; ++const int kFunctionBits = 6; ++const int kLuiShift = 16; ++const int kBp2Shift = 6; ++const int kBp2Bits = 2; ++const int kBp3Shift = 6; ++const int kBp3Bits = 3; ++const int kBaseShift = 21; ++const int kBaseBits = 5; ++const int kBit6Shift = 6; ++const int kBit6Bits = 1; ++ ++const int kImm9Shift = 7; ++const int kImm9Bits = 9; ++const int kImm16Shift = 0; ++const int kImm16Bits = 16; ++const int kImm18Shift = 0; ++const int kImm18Bits = 18; ++const int kImm19Shift = 0; ++const int kImm19Bits = 19; ++const int kImm21Shift = 0; ++const int kImm21Bits = 21; ++const int kImm26Shift = 0; ++const int kImm26Bits = 26; ++const int kImm28Shift = 0; ++const int kImm28Bits = 28; ++const int kImm32Shift = 0; ++const int kImm32Bits = 32; ++const int kSwsaImm8Shift = 16; ++const int kSwsaImm8Bits = 8; ++const int kSwsaImm5Shift = 16; ++const int kSwsaImm5Bits = 5; ++const int kSwsaImm10Shift = 11; ++const int kSwsaImm10Bits = 10; ++const int kSwsaImmMI10Shift = 16; ++const int kSwsaImmMI10Bits = 10; ++ ++// In branches and jumps immediate fields point to words, not bytes, ++// and are therefore shifted by 2. ++const int kImmFieldShift = 2; ++ ++const int kFrBits = 5; ++const int kFrShift = 21; ++const int kFsShift = 11; ++const int kFsBits = 5; ++const int kFtShift = 16; ++const int kFtBits = 5; ++const int kFdShift = 6; ++const int kFdBits = 5; ++const int kFCccShift = 8; ++const int kFCccBits = 3; ++const int kFBccShift = 18; ++const int kFBccBits = 3; ++const int kFBtrueShift = 16; ++const int kFBtrueBits = 1; ++const int kWtBits = 5; ++const int kWtShift = 16; ++const int kWsBits = 5; ++const int kWsShift = 11; ++const int kWdBits = 5; ++const int kWdShift = 6; ++ ++// ----- Miscellaneous useful masks. ++// Instruction bit masks. ++const int kOpcodeMask = ((1 << kOpcodeBits) - 1) << kOpcodeShift; ++const int kImm9Mask = ((1 << kImm9Bits) - 1) << kImm9Shift; ++const int kImm16Mask = ((1 << kImm16Bits) - 1) << kImm16Shift; ++const int kImm18Mask = ((1 << kImm18Bits) - 1) << kImm18Shift; ++const int kImm19Mask = ((1 << kImm19Bits) - 1) << kImm19Shift; ++const int kImm21Mask = ((1 << kImm21Bits) - 1) << kImm21Shift; ++const int kImm26Mask = ((1 << kImm26Bits) - 1) << kImm26Shift; ++const int kImm28Mask = ((1 << kImm28Bits) - 1) << kImm28Shift; ++const int kImm5Mask = ((1 << 5) - 1); ++const int kImm8Mask = ((1 << 8) - 1); ++const int kImm10Mask = ((1 << 10) - 1); ++const int kSwsaI5I10Mask = ((7U << 23) | ((1 << 6) - 1)); ++const int kSwsaI8Mask = ((3U << 24) | ((1 << 6) - 1)); ++const int kSwsaI5Mask = ((7U << 23) | ((1 << 6) - 1)); ++const int kSwsaMI10Mask = (15U << 2); ++const int kSwsaBITMask = ((7U << 23) | ((1 << 6) - 1)); ++const int kSwsaELMMask = (15U << 22); ++const int kSwsaLongerELMMask = kSwsaELMMask | (63U << 16); ++const int kSwsa3RMask = ((7U << 23) | ((1 << 6) - 1)); ++const int kSwsa3RFMask = ((15U << 22) | ((1 << 6) - 1)); ++const int kSwsaVECMask = (23U << 21); ++const int kSwsa2RMask = (7U << 18); ++const int kSwsa2RFMask = (15U << 17); ++const int kRsFieldMask = ((1 << kRsBits) - 1) << kRsShift; ++const int kRtFieldMask = ((1 << kRtBits) - 1) << kRtShift; ++const int kRdFieldMask = ((1 << kRdBits) - 1) << kRdShift; ++const int kSaFieldMask = ((1 << kSaBits) - 1) << kSaShift; ++const int kFunctionFieldMask = ((1 << kFunctionBits) - 1) << kFunctionShift; ++// Misc masks. ++const int kHiMaskOf32 = 0xffff << 16; // Only to be used with 32-bit values ++const int kLoMaskOf32 = 0xffff; ++const int kSignMaskOf32 = 0x80000000; // Only to be used with 32-bit values ++const int kJumpAddrMask = (1 << (kImm26Bits + kImmFieldShift)) - 1; ++const int64_t kTop16MaskOf64 = (int64_t)0xffff << 48; ++const int64_t kHigher16MaskOf64 = (int64_t)0xffff << 32; ++const int64_t kUpper16MaskOf64 = (int64_t)0xffff << 16; ++const int32_t kJalRawMark = 0x00000000; ++const int32_t kJRawMark = 0xf0000000; ++const int32_t kJumpRawMask = 0xf0000000; ++ ++// ----- Fields offset and length for sw64 ++const int sOpcodeShift = 26; // 31-26 ++const int sOpcodeBits = 6; ++const int sRaShift = 21; // 25-21 ++const int sRaBits = 5; ++const int sRbShift = 16; // 20-16 ++const int sRbBits = 5; ++const int sFunctionShift = 5; // 12- 5 ++const int sFunctionBits = 8; ++const int sRcShift = 0; // 4- 0 ++const int sRcBits = 5; ++const int sR3Shift = 5; // 9- 5 ++const int sR3Bits = 5; ++const int sRdShift = 0; // jzy 20150317 ++const int sRdBits = 5; ++ ++// ----- 21-bits disp(20-0) for SYS_CALL ++const int sImm21Shift = 0; ++const int sImm21Bits = 21; ++ ++// ----- 16-bits disp(15-0) for M ++const int sImm16Shift = 0; ++const int sImm16Bits = 16; ++ ++// ----- 12-bits disp(11-0) for MWithFun ++const int sImm12Shift = 0; ++const int sImm12Bits = 12; ++ ++// ----- 8-bits(20-13) imm for ALU_I & complex interger ALU ++const int sImm8Shift = 13; ++const int sImm8Bits = 8; ++ ++// ----- 5-bits(9-5) imm for complex-float ALU ++const int sImm5Shift = 5; ++const int sImm5Bits = 5; ++ ++//----- 13-bits(25-13) imm for ALU ++const int sImm13Shift = 13; ++const int sImm13Bits = 13; ++ ++//----- 11-bits disp(10-0) for SIMD ++const int sImm11Shift = 0; ++const int sImm11Bits = 11; ++ ++//----- 8-bits disp(7-0) for CSR ++const int RpiShift = 0; ++const int RpiBits = 8; ++ ++// Instruction bit masks. ++const int sOpcodeMask = ((1 << sOpcodeBits) - 1) << sOpcodeShift; ++const int sFunctionMask = ((1 << sFunctionBits) - 1) << sFunctionShift; ++const int sImm5Mask = ((1 << sImm5Bits) - 1) << sImm5Shift; ++const int sImm8Mask = ((1 << sImm8Bits) - 1) << sImm8Shift; ++const int sImm12Mask = ((1 << sImm12Bits) - 1) << sImm12Shift; ++const int sImm16Mask = ((1 << sImm16Bits) - 1) << sImm16Shift; ++const int sImm21Mask = ((1 << sImm21Bits) - 1) << sImm21Shift; ++const int sRaFieldMask = ((1 << sRaBits) - 1) << sRaShift; ++const int sRbFieldMask = ((1 << sRbBits) - 1) << sRbShift; ++const int sRcFieldMask = ((1 << sRcBits) - 1) << sRcShift; ++const int sR3FieldMask = ((1 << sR3Bits) - 1) << sR3Shift; ++const int sRdFieldMask = ((1 << sRdBits) - 1) << sRdShift; ++const int sImm13Mask = ((1 << sImm13Bits) - 1) << sImm13Shift; ++const int sImm11Mask = ((1 << sImm11Bits) - 1) << sImm11Shift; ++const int sRpiMask = ((1 << RpiBits) - 1) << RpiShift; ++ ++#define OP(x) (((x)&0x3F) << 26) ++#define PCD(oo) (OP(oo)) ++#define OPMEM(oo) (OP(oo)) ++#define BRA(oo) (OP(oo)) ++ ++#define OFP(oo, ff) (OP(oo) | (((ff)&0xFF) << 5)) ++#define FMA(oo, ff) (OP(oo) | (((ff)&0x3F) << 10)) ++#define MFC(oo, ff) (OP(oo) | ((ff)&0xFFFF)) ++#define MBR(oo, h) (OP(oo) | (((h)&3) << 14)) ++#define OPR(oo, ff) (OP(oo) | (((ff)&0xFF) << 5)) ++#define OPRL(oo, ff) (OP(oo) | (((ff)&0xFF) << 5)) ++#define TOPR(oo, ff) (OP(oo) | (((ff)&0x07) << 10)) ++#define TOPRL(oo, ff) (OP(oo) | (((ff)&0x07) << 10)) ++ ++#define ATMEM(oo, h) (OP(oo) | (((h)&0xF) << 12)) ++#define PRIRET(oo, h) (OP(oo) | (((h)&0x1) << 20)) ++#define SPCD(oo, ff) (OP(oo) | ((ff)&0x3FFFFFF)) ++#define EV6HWMEM(oo, ff) (OP(oo) | (((ff)&0xF) << 12)) ++#define CSR(oo, ff) (OP(oo) | (((ff)&0xFF) << 8)) ++ ++#define LOGX(oo, ff) (OP(oo) | (((ff)&0x3F) << 10)) ++#define PSE_LOGX(oo, ff) \ ++ (OP(oo) | (((ff)&0x3F) << 10) | (((ff) >> 0x6) << 26) | 0x3E0) ++ ++enum Opcode : uint32_t {}; ++ ++enum Opcode_ops_mem { ++ op_call = OPMEM(0x01), ++ op_ret = OPMEM(0x02), ++ op_jmp = OPMEM(0x03), ++ op_ldwe = OPMEM(0x09), ++ op_fillcs = op_ldwe, ++ op_ldse = OPMEM(0x0A), ++ op_e_fillcs = op_ldse, ++ op_ldde = OPMEM(0x0B), ++ op_fillcs_e = op_ldde, ++ op_vlds = OPMEM(0x0C), ++ op_e_fillde = op_vlds, ++ op_vldd = OPMEM(0x0D), ++ op_vsts = OPMEM(0x0E), ++ op_vstd = OPMEM(0x0F), ++ op_ldbu = OPMEM(0x20), ++ op_flushd = op_ldbu, ++ op_ldhu = OPMEM(0x21), ++ op_evictdg = op_ldhu, ++ op_ldw = OPMEM(0x22), ++ op_s_fillcs = op_ldw, ++ op_ldl = OPMEM(0x23), ++ op_s_fillde = op_ldl, ++ op_ldl_u = OPMEM(0x24), ++ op_evictdl = op_ldl_u, ++ op_flds = OPMEM(0x26), ++ op_fillde = op_flds, ++ op_fldd = OPMEM(0x27), ++ op_fillde_e = op_fldd, ++ op_stb = OPMEM(0x28), ++ op_sth = OPMEM(0x29), ++ op_stw = OPMEM(0x2A), ++ op_stl = OPMEM(0x2B), ++ op_stl_u = OPMEM(0x2C), ++ op_fsts = OPMEM(0x2E), ++ op_fstd = OPMEM(0x2F), ++ op_ldi = OPMEM(0x3E), ++ op_ldih = OPMEM(0x3F) ++ // unop = OPMEM(0x3F) | (30 << 16), ++}; ++ ++enum Opcode_ops_atmem { ++ op_lldw = ATMEM(0x08, 0x0), ++ op_lldl = ATMEM(0x08, 0x1), ++ op_ldw_inc = ATMEM(0x08, 0x2), // SW2F ++ op_ldl_inc = ATMEM(0x08, 0x3), // SW2F ++ op_ldw_dec = ATMEM(0x08, 0x4), // SW2F ++ op_ldl_dec = ATMEM(0x08, 0x5), // SW2F ++ op_ldw_set = ATMEM(0x08, 0x6), // SW2F ++ op_ldl_set = ATMEM(0x08, 0x7), // SW2F ++ op_lstw = ATMEM(0x08, 0x8), ++ op_lstl = ATMEM(0x08, 0x9), ++ op_ldw_nc = ATMEM(0x08, 0xA), ++ op_ldl_nc = ATMEM(0x08, 0xB), ++ op_ldd_nc = ATMEM(0x08, 0xC), ++ op_stw_nc = ATMEM(0x08, 0xD), ++ op_stl_nc = ATMEM(0x08, 0xE), ++ op_std_nc = ATMEM(0x08, 0xF), ++ op_vldw_u = ATMEM(0x1C, 0x0), ++ op_vstw_u = ATMEM(0x1C, 0x1), ++ op_vlds_u = ATMEM(0x1C, 0x2), ++ op_vsts_u = ATMEM(0x1C, 0x3), ++ op_vldd_u = ATMEM(0x1C, 0x4), ++ op_vstd_u = ATMEM(0x1C, 0x5), ++ op_vstw_ul = ATMEM(0x1C, 0x8), ++ op_vstw_uh = ATMEM(0x1C, 0x9), ++ op_vsts_ul = ATMEM(0x1C, 0xA), ++ op_vsts_uh = ATMEM(0x1C, 0xB), ++ op_vstd_ul = ATMEM(0x1C, 0xC), ++ op_vstd_uh = ATMEM(0x1C, 0xD), ++ op_vldd_nc = ATMEM(0x1C, 0xE), ++ op_vstd_nc = ATMEM(0x1C, 0xF), ++ op_ldbu_a = ATMEM(0x1E, 0x0), // SW8A ++ op_ldhu_a = ATMEM(0x1E, 0x1), // SW8A ++ op_ldw_a = ATMEM(0x1E, 0x2), // SW8A ++ op_ldl_a = ATMEM(0x1E, 0x3), // SW8A ++ op_flds_a = ATMEM(0x1E, 0x4), // SW8A ++ op_fldd_a = ATMEM(0x1E, 0x5), // SW8A ++ op_stb_a = ATMEM(0x1E, 0x6), // SW8A ++ op_sth_a = ATMEM(0x1E, 0x7), // SW8A ++ op_stw_a = ATMEM(0x1E, 0x8), // SW8A ++ op_stl_a = ATMEM(0x1E, 0x9), // SW8A ++ op_fsts_a = ATMEM(0x1E, 0xA), // SW8A ++ op_fstd_a = ATMEM(0x1E, 0xB) // SW8A ++}; ++ ++enum Opcode_ops_ev6hwmem { ++ op_pri_ld = EV6HWMEM(0x25, 0x0), ++ op_pri_st = EV6HWMEM(0x2D, 0x0), ++}; ++ ++enum Opcode_ops_opr { ++ op_addw = OPR(0x10, 0x00), ++ op_subw = OPR(0x10, 0x01), ++ op_s4addw = OPR(0x10, 0x02), ++ op_s4subw = OPR(0x10, 0x03), ++ op_s8addw = OPR(0x10, 0x04), ++ op_s8subw = OPR(0x10, 0x05), ++ op_addl = OPR(0x10, 0x08), ++ op_subl = OPR(0x10, 0x09), ++ op_s4addl = OPR(0x10, 0x0A), ++ op_s4subl = OPR(0x10, 0x0B), ++ op_s8addl = OPR(0x10, 0x0C), ++ op_s8subl = OPR(0x10, 0x0D), ++ op_mulw = OPR(0x10, 0x10), ++ op_divw = OPR(0x10, 0x11), // SW8A ++ op_udivw = OPR(0x10, 0x12), // SW8A ++ op_remw = OPR(0x10, 0x13), // SW8A ++ op_uremw = OPR(0x10, 0x14), // SW8A ++ op_mull = OPR(0x10, 0x18), ++ op_umulh = OPR(0x10, 0x19), ++ op_divl = OPR(0x10, 0x1A), // SW8A ++ op_udivl = OPR(0x10, 0x1B), // SW8A ++ op_reml = OPR(0x10, 0x1C), // SW8A ++ op_ureml = OPR(0x10, 0x1D), // SW8A ++ op_addpi = OPR(0x10, 0x1E), // SW8A ++ op_addpis = OPR(0x10, 0x1F), // SW8A ++ op_cmpeq = OPR(0x10, 0x28), ++ op_cmplt = OPR(0x10, 0x29), ++ op_cmple = OPR(0x10, 0x2A), ++ op_cmpult = OPR(0x10, 0x2B), ++ op_cmpule = OPR(0x10, 0x2C), ++ op_sbt = OPR(0x10, 0x2D), // SW8A ++ op_cbt = OPR(0x10, 0x2E), // SW8A ++ op_and = OPR(0x10, 0x38), ++ op_bic = OPR(0x10, 0x39), ++ op_bis = OPR(0x10, 0x3A), ++ op_ornot = OPR(0x10, 0x3B), ++ op_xor = OPR(0x10, 0x3C), ++ op_eqv = OPR(0x10, 0x3D), ++ op_inslb = OPR(0x10, 0x40), // 0x10.40~0x10.47 ++ op_inslh = OPR(0x10, 0x41), ++ op_inslw = OPR(0x10, 0x42), ++ op_insll = OPR(0x10, 0x43), ++ op_inshb = OPR(0x10, 0x44), ++ op_inshh = OPR(0x10, 0x45), ++ op_inshw = OPR(0x10, 0x46), ++ op_inshl = OPR(0x10, 0x47), ++ op_slll = OPR(0x10, 0x48), ++ op_srll = OPR(0x10, 0x49), ++ op_sral = OPR(0x10, 0x4A), ++ op_roll = OPR(0x10, 0x4B), // SW8A ++ op_sllw = OPR(0x10, 0x4C), // SW8A ++ op_srlw = OPR(0x10, 0x4D), // SW8A ++ op_sraw = OPR(0x10, 0x4E), // SW8A ++ op_rolw = OPR(0x10, 0x4F), // SW8A ++ op_extlb = OPR(0x10, 0x50), // 0x10.50~0x10.57 ++ op_extlh = OPR(0x10, 0x51), ++ op_extlw = OPR(0x10, 0x52), ++ op_extll = OPR(0x10, 0x53), ++ op_exthb = OPR(0x10, 0x54), ++ op_exthh = OPR(0x10, 0x55), ++ op_exthw = OPR(0x10, 0x56), ++ op_exthl = OPR(0x10, 0x57), ++ op_ctpop = OPR(0x10, 0x58), ++ op_ctlz = OPR(0x10, 0x59), ++ op_cttz = OPR(0x10, 0x5A), ++ op_revbh = OPR(0x10, 0x5B), // SW8A ++ op_revbw = OPR(0x10, 0x5C), // SW8A ++ op_revbl = OPR(0x10, 0x5D), // SW8A ++ op_casw = OPR(0x10, 0x5E), // SW8A ++ op_casl = OPR(0x10, 0x5F), // SW8A ++ op_masklb = OPR(0x10, 0x60), // 0x10.60~0x10.67 ++ op_masklh = OPR(0x10, 0x61), ++ op_masklw = OPR(0x10, 0x62), ++ op_maskll = OPR(0x10, 0x63), ++ op_maskhb = OPR(0x10, 0x64), ++ op_maskhh = OPR(0x10, 0x65), ++ op_maskhw = OPR(0x10, 0x66), ++ op_maskhl = OPR(0x10, 0x67), ++ op_zap = OPR(0x10, 0x68), ++ op_zapnot = OPR(0x10, 0x69), ++ op_sextb = OPR(0x10, 0x6A), ++ op_sexth = OPR(0x10, 0x6B), ++ op_cmpgeb = OPR(0x10, 0x6C), // 0x10.6C ++ op_fimovs = OPR(0x10, 0x70), ++ op_fimovd = OPR(0x10, 0x78), ++}; ++ ++enum Opcode_ops_sel { ++ op_seleq = TOPR(0x11, 0x0), ++ op_selge = TOPR(0x11, 0x1), ++ op_selgt = TOPR(0x11, 0x2), ++ op_selle = TOPR(0x11, 0x3), ++ op_sellt = TOPR(0x11, 0x4), ++ op_selne = TOPR(0x11, 0x5), ++ op_sellbc = TOPR(0x11, 0x6), ++ op_sellbs = TOPR(0x11, 0x7) ++}; ++ ++enum Opcode_ops_oprl { ++ op_addw_l = OPRL(0x12, 0x00), ++ op_subw_l = OPRL(0x12, 0x01), ++ op_s4addw_l = OPRL(0x12, 0x02), ++ op_s4subw_l = OPRL(0x12, 0x03), ++ op_s8addw_l = OPRL(0x12, 0x04), ++ op_s8subw_l = OPRL(0x12, 0x05), ++ op_addl_l = OPRL(0x12, 0x08), ++ op_subl_l = OPRL(0x12, 0x09), ++ op_s4addl_l = OPRL(0x12, 0x0A), ++ op_s4subl_l = OPRL(0x12, 0x0B), ++ op_s8addl_l = OPRL(0x12, 0x0C), ++ op_s8subl_l = OPRL(0x12, 0x0D), ++ op_mulw_l = OPRL(0x12, 0x10), ++ op_mull_l = OPRL(0x12, 0x18), ++ op_umulh_l = OPRL(0x12, 0x19), ++ op_cmpeq_l = OPRL(0x12, 0x28), ++ op_cmplt_l = OPRL(0x12, 0x29), ++ op_cmple_l = OPRL(0x12, 0x2A), ++ op_cmpult_l = OPRL(0x12, 0x2B), ++ op_cmpule_l = OPRL(0x12, 0x2C), ++ op_sbt_l = OPRL(0x12, 0x2D), // SW8A ++ op_cbt_l = OPRL(0x12, 0x2E), // SW8A ++ op_and_l = OPRL(0x12, 0x38), ++ op_bic_l = OPRL(0x12, 0x39), ++ op_bis_l = OPRL(0x12, 0x3A), ++ op_ornot_l = OPRL(0x12, 0x3B), ++ op_xor_l = OPRL(0x12, 0x3C), ++ op_eqv_l = OPRL(0x12, 0x3D), ++ op_inslb_l = OPRL(0x12, 0x40), // 0x12.40~0x12.47 ++ op_inslh_l = OPRL(0x12, 0x41), ++ op_inslw_l = OPRL(0x12, 0x42), ++ op_insll_l = OPRL(0x12, 0x43), ++ op_inshb_l = OPRL(0x12, 0x44), ++ op_inshh_l = OPRL(0x12, 0x45), ++ op_inshw_l = OPRL(0x12, 0x46), ++ op_inshl_l = OPRL(0x12, 0x47), ++ op_slll_l = OPRL(0x12, 0x48), ++ op_srll_l = OPRL(0x12, 0x49), ++ op_sral_l = OPRL(0x12, 0x4A), ++ op_roll_l = OPRL(0x12, 0x4B), // SW8A ++ op_sllw_l = OPRL(0x12, 0x4C), ++ op_srlw_l = OPRL(0x12, 0x4D), ++ op_sraw_l = OPRL(0x12, 0x4E), ++ op_rolw_l = OPRL(0x12, 0x4F), // SW8A ++ op_extlb_l = OPRL(0x12, 0x50), // 0x12.50~0x12.57 ++ op_extlh_l = OPRL(0x12, 0x51), ++ op_extlw_l = OPRL(0x12, 0x52), ++ op_extll_l = OPRL(0x12, 0x53), ++ op_exthb_l = OPRL(0x12, 0x54), ++ op_exthh_l = OPRL(0x12, 0x55), ++ op_exthw_l = OPRL(0x12, 0x56), ++ op_exthl_l = OPRL(0x12, 0x57), ++ op_masklb_l = OPRL(0x12, 0x60), // 0x12.60~0x12.67 ++ op_masklh_l = OPRL(0x12, 0x61), ++ op_masklw_l = OPRL(0x12, 0x62), ++ op_maskll_l = OPRL(0x12, 0x63), ++ op_maskhb_l = OPRL(0x12, 0x64), ++ op_maskhh_l = OPRL(0x12, 0x65), ++ op_maskhw_l = OPRL(0x12, 0x66), ++ op_maskhl_l = OPRL(0x12, 0x67), ++ op_zap_l = OPRL(0x12, 0x68), ++ op_zapnot_l = OPRL(0x12, 0x69), ++ op_sextb_l = OPRL(0x12, 0x6A), ++ op_sexth_l = OPRL(0x12, 0x6B), ++ op_cmpgeb_l = OPRL(0x12, 0x6C), // 0x12.6C ++}; ++ ++enum Opcode_ops_sel_l { ++ op_seleq_l = TOPRL(0x13, 0x0), ++ op_selge_l = TOPRL(0x13, 0x1), ++ op_selgt_l = TOPRL(0x13, 0x2), ++ op_selle_l = TOPRL(0x13, 0x3), ++ op_sellt_l = TOPRL(0x13, 0x4), ++ op_selne_l = TOPRL(0x13, 0x5), ++ op_sellbc_l = TOPRL(0x13, 0x6), ++ op_sellbs_l = TOPRL(0x13, 0x7) ++}; ++ ++enum Opcode_ops_bra { ++ op_br = BRA(0x04), ++ op_bsr = BRA(0x05), ++ op_beq = BRA(0x30), ++ op_bne = BRA(0x31), ++ op_blt = BRA(0x32), ++ op_ble = BRA(0x33), ++ op_bgt = BRA(0x34), ++ op_bge = BRA(0x35), ++ op_blbc = BRA(0x36), ++ op_blbs = BRA(0x37), ++ op_fbeq = BRA(0x38), ++ op_fbne = BRA(0x39), ++ op_fblt = BRA(0x3A), ++ op_fble = BRA(0x3B), ++ op_fbgt = BRA(0x3C), ++ op_fbge = BRA(0x3D) ++}; ++ ++enum Opcode_ops_fp { ++ op_fadds = OFP(0x18, 0x00), ++ op_faddd = OFP(0x18, 0x01), ++ op_fsubs = OFP(0x18, 0x02), ++ op_fsubd = OFP(0x18, 0x03), ++ op_fmuls = OFP(0x18, 0x04), ++ op_fmuld = OFP(0x18, 0x05), ++ op_fdivs = OFP(0x18, 0x06), ++ op_fdivd = OFP(0x18, 0x07), ++ op_fsqrts = OFP(0x18, 0x08), ++ op_fsqrtd = OFP(0x18, 0x09), ++ op_fcmpeq = OFP(0x18, 0x10), ++ op_fcmple = OFP(0x18, 0x11), ++ op_fcmplt = OFP(0x18, 0x12), ++ op_fcmpun = OFP(0x18, 0x13), ++ op_fcvtsd = OFP(0x18, 0x20), ++ op_fcvtds = OFP(0x18, 0x21), ++ op_fcvtdl_g = OFP(0x18, 0x22), // lx_fcvtdl ++ op_fcvtdl_p = OFP(0x18, 0x23), ++ op_fcvtdl_z = OFP(0x18, 0x24), ++ op_fcvtdl_n = OFP(0x18, 0x25), // lx_fcvtdl ++ op_fcvtdl = OFP(0x18, 0x27), ++ op_fcvtwl = OFP(0x18, 0x28), ++ op_fcvtlw = OFP(0x18, 0x29), ++ op_fcvtls = OFP(0x18, 0x2D), ++ op_fcvtld = OFP(0x18, 0x2F), ++ op_fcvths = OFP(0x18, 0x2E), ++ op_fcvtsh = OFP(0x1B, 0x37), ++ op_cmovdl = OFP(0x10, 0x72), ++ op_cmovdl_g = OFP(0x10, 0x74), ++ op_cmovdl_p = OFP(0x10, 0x7A), ++ op_cmovdl_z = OFP(0x10, 0x7C), ++ op_cmovdl_n = OFP(0x10, 0x80), ++ op_cmovdlu = OFP(0x10, 0x81), ++ op_cmovdlu_g = OFP(0x10, 0x82), ++ op_cmovdlu_p = OFP(0x10, 0x83), ++ op_cmovdlu_z = OFP(0x10, 0x84), ++ op_cmovdlu_n = OFP(0x10, 0x85), ++ op_cmovdwu = OFP(0x10, 0x86), ++ op_cmovdwu_g = OFP(0x10, 0x87), ++ op_cmovdwu_p = OFP(0x10, 0x88), ++ op_cmovdwu_z = OFP(0x10, 0x89), ++ op_cmovdwu_n = OFP(0x10, 0x8A), ++ op_cmovdw = OFP(0x10, 0x8B), ++ op_cmovdw_g = OFP(0x10, 0x8C), ++ op_cmovdw_p = OFP(0x10, 0x8D), ++ op_cmovdw_z = OFP(0x10, 0x8E), ++ op_cmovdw_n = OFP(0x10, 0x8F), ++ op_cmovls = OFP(0x18, 0x48), ++ op_cmovws = OFP(0x18, 0x49), ++ op_cmovld = OFP(0x18, 0x4a), ++ op_cmovwd = OFP(0x18, 0x4b), ++ op_cmovuls = OFP(0x18, 0x4c), ++ op_cmovuws = OFP(0x18, 0x4d), ++ op_cmovuld = OFP(0x18, 0x4e), ++ op_cmovuwd = OFP(0x18, 0x4f), ++ op_fcpys = OFP(0x18, 0x30), ++ op_fcpyse = OFP(0x18, 0x31), ++ op_fcpysn = OFP(0x18, 0x32), ++ op_ifmovs = OFP(0x18, 0x40), ++ op_ifmovd = OFP(0x18, 0x41), ++ op_rfpcr = OFP(0x18, 0x50), ++ op_wfpcr = OFP(0x18, 0x51), ++ op_setfpec0 = OFP(0x18, 0x54), ++ op_setfpec1 = OFP(0x18, 0x55), ++ op_setfpec2 = OFP(0x18, 0x56), ++ op_setfpec3 = OFP(0x18, 0x57), ++ op_frecs = OFP(0x18, 0x58), // SW8A ++ op_frecd = OFP(0x18, 0x59), // SW8A ++ op_fris = OFP(0x18, 0x5A), // SW8A ++ op_fris_g = OFP(0x18, 0x5B), // SW8A ++ op_fris_p = OFP(0x18, 0x5C), // SW8A ++ op_fris_z = OFP(0x18, 0x5D), // SW8A ++ op_fris_n = OFP(0x18, 0x5F), // SW8A ++ op_frid = OFP(0x18, 0x60), // SW8A ++ op_frid_g = OFP(0x18, 0x61), // SW8A ++ op_frid_p = OFP(0x18, 0x62), // SW8A ++ op_frid_z = OFP(0x18, 0x63), // SW8A ++ op_frid_n = OFP(0x18, 0x64), // SW8A ++ op_vaddw = OFP(0x1A, 0x00), ++ op_vsubw = OFP(0x1A, 0x01), ++ op_vcmpgew = OFP(0x1A, 0x02), ++ op_vcmpeqw = OFP(0x1A, 0x03), ++ op_vcmplew = OFP(0x1A, 0x04), ++ op_vcmpltw = OFP(0x1A, 0x05), ++ op_vcmpulew = OFP(0x1A, 0x06), ++ op_vcmpultw = OFP(0x1A, 0x07), ++ op_vsllw = OFP(0x1A, 0x08), ++ op_vsrlw = OFP(0x1A, 0x09), ++ op_vsraw = OFP(0x1A, 0x0A), ++ op_vrolw = OFP(0x1A, 0x0B), ++ op_sllow = OFP(0x1A, 0x0C), ++ op_srlow = OFP(0x1A, 0x0D), ++ op_vaddl = OFP(0x1A, 0x0E), ++ op_vsubl = OFP(0x1A, 0x0F), ++ op_vsllb = OFP(0x1A, 0x10), // SW8A ++ op_vsrlb = OFP(0x1A, 0x11), // SW8A ++ op_vsrab = OFP(0x1A, 0x12), // SW8A ++ op_vrolb = OFP(0x1A, 0x13), // SW8A ++ op_vsllh = OFP(0x1A, 0x14), // SW8A ++ op_vsrlh = OFP(0x1A, 0x15), // SW8A ++ op_vsrah = OFP(0x1A, 0x16), // SW8A ++ op_vrolh = OFP(0x1A, 0x17), // SW8A ++ op_ctpopow = OFP(0x1A, 0x18), ++ op_ctlzow = OFP(0x1A, 0x19), ++ op_vslll = OFP(0x1A, 0x1A), // SW8A ++ op_vsrll = OFP(0x1A, 0x1B), // SW8A ++ op_vsral = OFP(0x1A, 0x1C), // SW8A ++ op_vroll = OFP(0x1A, 0x1D), // SW8A ++ op_vmaxb = OFP(0x1A, 0x1E), // SW8A ++ op_vminb = OFP(0x1A, 0x1F), // SW8A ++ op_vucaddw = OFP(0x1A, 0x40), ++ op_vucsubw = OFP(0x1A, 0x41), ++ op_vucaddh = OFP(0x1A, 0x42), ++ op_vucsubh = OFP(0x1A, 0x43), ++ op_vucaddb = OFP(0x1A, 0x44), ++ op_vucsubb = OFP(0x1A, 0x45), ++ op_sraow = OFP(0x1A, 0x46), // SW8A ++ op_vsumw = OFP(0x1A, 0x47), // SW8A ++ op_vsuml = OFP(0x1A, 0x48), // SW8A ++ op_vcmpueqb = OFP(0x1A, 0x4B), // SW8A ++ op_vcmpugtb = OFP(0x1A, 0x4C), // SW8A ++ op_vmaxh = OFP(0x1A, 0x50), // SW8A ++ op_vminh = OFP(0x1A, 0x51), // SW8A ++ op_vmaxw = OFP(0x1A, 0x52), // SW8A ++ op_vminw = OFP(0x1A, 0x53), // SW8A ++ op_vmaxl = OFP(0x1A, 0x54), // SW8A ++ op_vminl = OFP(0x1A, 0x55), // SW8A ++ op_vumaxb = OFP(0x1A, 0x56), // SW8A ++ op_vuminb = OFP(0x1A, 0x57), // SW8A ++ op_vumaxh = OFP(0x1A, 0x58), // SW8A ++ op_vuminh = OFP(0x1A, 0x59), // SW8A ++ op_vumaxw = OFP(0x1A, 0x5A), // SW8A ++ op_vuminw = OFP(0x1A, 0x5B), // SW8A ++ op_vumaxl = OFP(0x1A, 0x5C), // SW8A ++ op_vuminl = OFP(0x1A, 0x5D), // SW8A ++ op_vsm3msw = OFP(0x1A, 0x67), // SW8A, ENCRYPT ++ op_vsm4r = OFP(0x1A, 0x69), // SW8A, ENCRYPT ++ op_vbinvw = OFP(0x1A, 0x6A), // SW8A, ENCRYPT ++ op_vadds = OFP(0x1A, 0x80), ++ op_vaddd = OFP(0x1A, 0x81), ++ op_vsubs = OFP(0x1A, 0x82), ++ op_vsubd = OFP(0x1A, 0x83), ++ op_vmuls = OFP(0x1A, 0x84), ++ op_vmuld = OFP(0x1A, 0x85), ++ op_vdivs = OFP(0x1A, 0x86), ++ op_vdivd = OFP(0x1A, 0x87), ++ op_vsqrts = OFP(0x1A, 0x88), ++ op_vsqrtd = OFP(0x1A, 0x89), ++ op_vfcmpeq = OFP(0x1A, 0x8C), ++ op_vfcmple = OFP(0x1A, 0x8D), ++ op_vfcmplt = OFP(0x1A, 0x8E), ++ op_vfcmpun = OFP(0x1A, 0x8F), ++ op_vcpys = OFP(0x1A, 0x90), ++ op_vcpyse = OFP(0x1A, 0x91), ++ op_vcpysn = OFP(0x1A, 0x92), ++ op_vsums = OFP(0x1A, 0x93), // SW8A ++ op_vsumd = OFP(0x1A, 0x94), // SW8A ++ op_vfcvtsd = OFP(0x1A, 0x95), // SW8A ++ op_vfcvtds = OFP(0x1A, 0x96), // SW8A ++ op_vfcvtls = OFP(0x1A, 0x99), // SW8A ++ op_vfcvtld = OFP(0x1A, 0x9A), // SW8A ++ op_vfcvtdl = OFP(0x1A, 0x9B), // SW8A ++ op_vfcvtdl_g = OFP(0x1A, 0x9C), // SW8A ++ op_vfcvtdl_p = OFP(0x1A, 0x9D), // SW8A ++ op_vfcvtdl_z = OFP(0x1A, 0x9E), // SW8A ++ op_vfcvtdl_n = OFP(0x1A, 0x9F), // SW8A ++ op_vfris = OFP(0x1A, 0xA0), // SW8A ++ op_vfris_g = OFP(0x1A, 0xA1), // SW8A ++ op_vfris_p = OFP(0x1A, 0xA2), // SW8A ++ op_vfris_z = OFP(0x1A, 0xA3), // SW8A ++ op_vfris_n = OFP(0x1A, 0xA4), // SW8A ++ op_vfrid = OFP(0x1A, 0xA5), // SW8A ++ op_vfrid_g = OFP(0x1A, 0xA6), // SW8A ++ op_vfrid_p = OFP(0x1A, 0xA7), // SW8A ++ op_vfrid_z = OFP(0x1A, 0xA8), // SW8A ++ op_vfrid_n = OFP(0x1A, 0xA9), // SW8A ++ op_vfrecs = OFP(0x1A, 0xAA), // SW8A ++ op_vfrecd = OFP(0x1A, 0xAB), // SW8A ++ op_vmaxs = OFP(0x1A, 0xAC), // SW8A ++ op_vmins = OFP(0x1A, 0xAD), // SW8A ++ op_vmaxd = OFP(0x1A, 0xAE), // SW8A ++ op_vmind = OFP(0x1A, 0xAF), // SW8A ++ ++ op_vbisw = PSE_LOGX(0x14, 0x30), ++ op_vxorw = PSE_LOGX(0x14, 0x3c), ++ op_vandw = PSE_LOGX(0x14, 0xc0), ++ op_veqvw = PSE_LOGX(0x14, 0xc3), ++ op_vornotw = PSE_LOGX(0x14, 0xf3), ++ op_vbicw = PSE_LOGX(0x14, 0xfc) ++}; ++ ++enum Opcode_ops_fpl { ++ op_vaddw_l = OFP(0x1A, 0x20), ++ op_vsubw_l = OFP(0x1A, 0x21), ++ op_vcmpgew_l = OFP(0x1A, 0x22), ++ op_vcmpeqw_l = OFP(0x1A, 0x23), ++ op_vcmplew_l = OFP(0x1A, 0x24), ++ op_vcmpltw_l = OFP(0x1A, 0x25), ++ op_vcmpulew_l = OFP(0x1A, 0x26), ++ op_vcmpultw_l = OFP(0x1A, 0x27), ++ op_vsllw_l = OFP(0x1A, 0x28), ++ op_vsrlw_l = OFP(0x1A, 0x29), ++ op_vsraw_l = OFP(0x1A, 0x2A), ++ op_vrolw_l = OFP(0x1A, 0x2B), ++ op_sllow_l = OFP(0x1A, 0x2C), ++ op_srlow_l = OFP(0x1A, 0x2D), ++ op_vaddl_l = OFP(0x1A, 0x2E), ++ op_vsubl_l = OFP(0x1A, 0x2F), ++ op_vsllb_l = OFP(0x1A, 0x30), // SW8A ++ op_vsrlb_l = OFP(0x1A, 0x31), // SW8A ++ op_vsrab_l = OFP(0x1A, 0x32), // SW8A ++ op_vrolb_l = OFP(0x1A, 0x33), // SW8A ++ op_vsllh_l = OFP(0x1A, 0x34), // SW8A ++ op_vsrlh_l = OFP(0x1A, 0x35), // SW8A ++ op_vsrah_l = OFP(0x1A, 0x36), // SW8A ++ op_vrolh_l = OFP(0x1A, 0x37), // SW8A ++ op_vslll_l = OFP(0x1A, 0x3A), // SW8A ++ op_vsrll_l = OFP(0x1A, 0x3B), // SW8A ++ op_vsral_l = OFP(0x1A, 0x3C), // SW8A ++ op_vroll_l = OFP(0x1A, 0x3D), // SW8A ++ op_vucaddw_l = OFP(0x1A, 0x60), ++ op_vucsubw_l = OFP(0x1A, 0x61), ++ op_vucaddh_l = OFP(0x1A, 0x62), ++ op_vucsubh_l = OFP(0x1A, 0x63), ++ op_vucaddb_l = OFP(0x1A, 0x64), ++ op_vucsubb_l = OFP(0x1A, 0x65), ++ op_sraow_l = OFP(0x1A, 0x66), // SW8A ++ op_vsm4key_l = OFP(0x1A, 0x68), // SW8A, ENCRYPT ++ op_vcmpueqb_l = OFP(0x1A, 0x6B), // SW8A ++ op_vcmpugtb_l = OFP(0x1A, 0x6C), // SW8A ++ op_vfcvtsh_l = OFP(0x1B, 0x35), // SW8A ++ op_vfcvths_l = OFP(0x1B, 0x36) // SW8A ++}; ++ ++enum Opcode_ops_fma { ++ op_fmas = FMA(0x19, 0x00), ++ op_fmad = FMA(0x19, 0x01), ++ op_fmss = FMA(0x19, 0x02), ++ op_fmsd = FMA(0x19, 0x03), ++ op_fnmas = FMA(0x19, 0x04), ++ op_fnmad = FMA(0x19, 0x05), ++ op_fnmss = FMA(0x19, 0x06), ++ op_fnmsd = FMA(0x19, 0x07), ++ op_fseleq = FMA(0x19, 0x10), ++ op_fselne = FMA(0x19, 0x11), ++ op_fsellt = FMA(0x19, 0x12), ++ op_fselle = FMA(0x19, 0x13), ++ op_fselgt = FMA(0x19, 0x14), ++ op_fselge = FMA(0x19, 0x15), ++ op_vmas = FMA(0x1B, 0x00), ++ op_vmad = FMA(0x1B, 0x01), ++ op_vmss = FMA(0x1B, 0x02), ++ op_vmsd = FMA(0x1B, 0x03), ++ op_vnmas = FMA(0x1B, 0x04), ++ op_vnmad = FMA(0x1B, 0x05), ++ op_vnmss = FMA(0x1B, 0x06), ++ op_vnmsd = FMA(0x1B, 0x07), ++ op_vfseleq = FMA(0x1B, 0x10), ++ op_vfsellt = FMA(0x1B, 0x12), ++ op_vfselle = FMA(0x1B, 0x13), ++ op_vseleqw = FMA(0x1B, 0x18), ++ op_vsellbcw = FMA(0x1B, 0x19), ++ op_vselltw = FMA(0x1B, 0x1A), ++ op_vsellew = FMA(0x1B, 0x1B), ++ op_vcpyw = FMA(0x1B, 0x24), ++ op_vcpyf = FMA(0x1B, 0x25), ++ op_vconw = FMA(0x1B, 0x26), ++ op_vshfw = FMA(0x1B, 0x27), ++ op_vcons = FMA(0x1B, 0x28), ++ op_vcond = FMA(0x1B, 0x29), ++ op_vinsectlh = FMA(0x1B, 0x2C), // SW8A ++ op_vinsectlw = FMA(0x1B, 0x2D), // SW8A ++ op_vinsectll = FMA(0x1B, 0x2E), // SW8A ++ op_vinsectlb = FMA(0x1B, 0x2F), // SW8A ++ op_vshfqb = FMA(0x1B, 0x31), // SW8A ++ op_vcpyh = FMA(0x1B, 0x32), // SW8A ++ op_vcpyb = FMA(0x1B, 0x33) // SW8A ++}; ++ ++enum Opcode_ops_fmal { ++ op_vinsw_l = FMA(0x1B, 0x20), ++ op_vinsf_l = FMA(0x1B, 0x21), ++ op_vextw_l = FMA(0x1B, 0x22), ++ op_vextf_l = FMA(0x1B, 0x23), ++ op_vinsb_l = FMA(0x1B, 0x2A), // SW8A ++ op_vinsh_l = FMA(0x1B, 0x2B), // SW8A ++ op_vshfq_l = FMA(0x1B, 0x30), // SW8A ++ op_vsm3r_l = FMA(0x1B, 0x34), // SW8A, ENCRYPT ++ op_vseleqw_l = FMA(0x1B, 0x38), ++ op_vsellbcw_l = FMA(0x1B, 0x39), ++ op_vselltw_l = FMA(0x1B, 0x3A), ++ op_vsellew_l = FMA(0x1B, 0x3B) ++}; ++ ++enum Opcode_ops_extra { ++ op_sys_call = PCD(0x00), ++ op_memb = MFC(0x06, 0x0000), ++ op_imemb = MFC(0x06, 0x0001), // SW8A ++ op_wmemb = MFC(0x06, 0x0002), // SW8A ++ op_rtc = MFC(0x06, 0x0020), ++ op_rcid = MFC(0x06, 0x0040), ++ op_halt = MFC(0x06, 0x0080), ++ op_rd_f = MFC(0x06, 0x1000), // SW2F ++ op_wr_f = MFC(0x06, 0x1020), // SW2F ++ op_rtid = MFC(0x06, 0x1040), ++ op_csrrs = CSR(0x06, 0xFC), // SW8A ++ op_csrrc = CSR(0x06, 0xFD), // SW8A ++ op_csrr = CSR(0x06, 0xFE), ++ op_csrw = CSR(0x06, 0xFF), ++ op_pri_ret = PRIRET(0x07, 0x0), ++ op_vlog = LOGX(0x14, 0x00), ++ op_lbr = PCD(0x1D), // SW8A ++ op_dpfhr = ATMEM(0x1E, 0xE), // SW8A ++ op_dpfhw = ATMEM(0x1E, 0xF), // SW8A ++}; ++ ++enum Opcode_ops_simulator { op_trap = PCD(0x1F) }; ++ ++enum TrapCode : uint32_t { BREAK = 0, REDIRECT = 1 }; ++ ++// ----- Emulated conditions. ++// On SW64 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 Condition { ++ // Any value < 0 is considered no_condition. ++ kNoCondition = -1, ++ overflow = 0, ++ no_overflow = 1, ++ Uless = 2, ++ Ugreater_equal = 3, ++ Uless_equal = 4, ++ Ugreater = 5, ++ equal = 6, ++ not_equal = 7, // Unordered or Not Equal. ++ negative = 8, ++ positive = 9, ++ parity_even = 10, ++ parity_odd = 11, ++ less = 12, ++ greater_equal = 13, ++ less_equal = 14, ++ greater = 15, ++ ueq = 16, // Unordered or Equal. ++ ogl = 17, // Ordered and Not Equal. ++ cc_always = 18, ++ ++ // Aliases. ++ carry = Uless, ++ not_carry = Ugreater_equal, ++ zero = equal, ++ eq = equal, ++ not_zero = not_equal, ++ ne = not_equal, ++ nz = not_equal, ++ sign = negative, ++ not_sign = positive, ++ mi = negative, ++ pl = positive, ++ hi = Ugreater, ++ ls = Uless_equal, ++ ge = greater_equal, ++ lt = less, ++ gt = greater, ++ le = less_equal, ++ hs = Ugreater_equal, ++ lo = Uless, ++ al = cc_always, ++ ult = Uless, ++ uge = Ugreater_equal, ++ ule = Uless_equal, ++ ugt = Ugreater, ++ cc_default = kNoCondition ++}; ++ ++// Returns the equivalent of !cc. ++// Negation of the default kNoCondition (-1) results in a non-default ++// no_condition value (-2). As long as tests for no_condition check ++// for condition < 0, this will work as expected. ++inline Condition NegateCondition(Condition cc) { ++ DCHECK(cc != cc_always); ++ return static_cast(cc ^ 1); ++} ++ ++inline Condition NegateFpuCondition(Condition cc) { ++ DCHECK(cc != cc_always); ++ switch (cc) { ++ case ult: ++ return ge; ++ case ugt: ++ return le; ++ case uge: ++ return lt; ++ case ule: ++ return gt; ++ case lt: ++ return uge; ++ case gt: ++ return ule; ++ case ge: ++ return ult; ++ case le: ++ return ugt; ++ case eq: ++ return ne; ++ case ne: ++ return eq; ++ case ueq: ++ return ogl; ++ case ogl: ++ return ueq; ++ default: ++ return cc; ++ } ++} ++ ++// ----- Coprocessor conditions. ++enum FPUCondition { ++ kNoFPUCondition = -1, ++ ++ F = 0x00, // False. ++ UN = 0x01, // Unordered. ++ EQ = 0x02, // Equal. ++ UEQ = 0x03, // Unordered or Equal. ++ OLT = 0x04, // Ordered or Less Than, on Sw64 release < 3. ++ LT = 0x04, // Ordered or Less Than, on Sw64 release >= 3. ++ ULT = 0x05, // Unordered or Less Than. ++ OLE = 0x06, // Ordered or Less Than or Equal, on Sw64 release < 3. ++ LE = 0x06, // Ordered or Less Than or Equal, on Sw64 release >= 3. ++ ULE = 0x07, // Unordered or Less Than or Equal. ++ ++ // Following constants are available on Sw64 release >= 3 only. ++ ORD = 0x11, // Ordered, on Sw64 release >= 3. ++ UNE = 0x12, // Not equal, on Sw64 release >= 3. ++ NE = 0x13, // Ordered Greater Than or Less Than. on Sw64 >= 3 only. ++}; ++ ++// FPU rounding modes. ++enum FPURoundingMode { ++ RN = 0b00, // Round to Nearest. ++ RZ = 0b01, // Round towards zero. ++ RP = 0b10, // Round towards Plus Infinity. ++ RM = 0b11, // Round towards Minus Infinity. ++ ++ // Aliases. ++ kRoundToNearest = RN, ++ kRoundToZero = RZ, ++ kRoundToPlusInf = RP, ++ kRoundToMinusInf = RM, ++ ++ mode_round = RN, ++ mode_ceil = RP, ++ mode_floor = RM, ++ mode_trunc = RZ ++}; ++ ++const uint32_t kFPURoundingModeMask = 3; ++ ++enum CheckForInexactConversion { ++ kCheckForInexactConversion, ++ kDontCheckForInexactConversion ++}; ++ ++enum class MaxMinKind : int { kMin = 0, kMax = 1 }; ++ ++// ----------------------------------------------------------------------------- ++// Hints. ++ ++// Branch hints are not used on the SW64. They are defined so that they can ++// appear in shared function signatures, but will be ignored in SW64 ++// implementations. ++enum Hint { no_hint = 0 }; ++ ++inline Hint NegateHint(Hint hint) { return no_hint; } ++ ++// simulator custom instruction ++// const Instr rtCallRedirInstr = op_trap | REDIRECT; ++const Instr rtCallRedirInstr = BREAK | call_rt_redirected << 6; ++// A nop instruction. (Encoding of sll 0 0 0). ++const Instr nopInstr = op_sllw; ++ ++constexpr uint8_t kInstrSize = 4; ++constexpr uint8_t kInstrSizeLog2 = 2; ++ ++class InstructionBase { ++ public: ++ // Instruction type. ++ enum Type { ++ kRegisterType, ++ kImmediateType, ++ kJumpType, ++ kSwSyscallType, ++ kSwTransferanceType, ++ kSwStorageType, ++ kSwSimpleCalculationType, ++ kSwCompositeCalculationType, ++ kSwExtendType, ++ kSwSimulatorTrap, ++ kUnsupported = -1 ++ }; ++ ++ // Get the raw instruction bits. ++ inline Instr InstructionBits() const { ++ 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 SW64 encoding. ++ inline Opcode OpcodeValue() const { ++ return static_cast( ++ Bits(kOpcodeShift + kOpcodeBits - 1, kOpcodeShift)); ++ } ++ ++ inline int FunctionFieldRaw() const { ++ return InstructionBits() & kFunctionFieldMask; ++ } ++ ++ // Return the fields at their original place in the instruction encoding. ++ inline Opcode OpcodeFieldRaw() const { ++ return static_cast(InstructionBits() & kOpcodeMask); ++ } ++ ++ inline int OpcodeFieldValue() const { ++ return static_cast(InstructionBits() & kOpcodeMask); ++ } ++ ++ // Get the encoding type of the instruction. ++ inline Type InstructionType() const; ++ ++ protected: ++ InstructionBase() {} ++}; ++ ++template ++class InstructionGetters : public T { ++ public: ++ inline int RsValue() const { ++ return this->Bits(kRsShift + kRsBits - 1, kRsShift); ++ } ++ ++ inline int RtValue() const { ++ return this->Bits(kRtShift + kRtBits - 1, kRtShift); ++ } ++ ++ inline int RdValue() const { ++ return this->Bits(kRdShift + kRdBits - 1, kRdShift); ++ } ++ ++ inline int BaseValue() const { ++ DCHECK_EQ(this->InstructionType(), InstructionBase::kImmediateType); ++ return this->Bits(kBaseShift + kBaseBits - 1, kBaseShift); ++ } ++ ++ inline int SaValue() const { ++ DCHECK_EQ(this->InstructionType(), InstructionBase::kRegisterType); ++ return this->Bits(kSaShift + kSaBits - 1, kSaShift); ++ } ++ ++ inline int FunctionValue() const { ++ DCHECK(this->InstructionType() == InstructionBase::kRegisterType || ++ this->InstructionType() == InstructionBase::kImmediateType); ++ return this->Bits(kFunctionShift + kFunctionBits - 1, kFunctionShift); ++ } ++ ++ inline int FdValue() const { ++ return this->Bits(kFdShift + kFdBits - 1, kFdShift); ++ } ++ ++ inline int FsValue() const { ++ return this->Bits(kFsShift + kFsBits - 1, kFsShift); ++ } ++ ++ inline int FtValue() const { ++ return this->Bits(kFtShift + kFtBits - 1, kFtShift); ++ } ++ ++ inline int FrValue() const { ++ return this->Bits(kFrShift + kFrBits - 1, kFrShift); ++ } ++ ++ // Return the fields at their original place in the instruction encoding. ++ inline Opcode OpcodeFieldRaw() const { ++ return static_cast(this->InstructionBits() & kOpcodeMask); ++ } ++ ++ inline int OpcodeFieldValue() const { ++ return static_cast(this->InstructionBits() & kOpcodeMask); ++ } ++ ++ inline int SwGetMask(int hi, int lo) const { ++ int mask = 2 << (hi - lo); ++ mask -= 1; ++ return mask << lo; ++ } ++ ++ // the position of instruction function_code in SW is different in different ++ // type of instructions ++ inline int SwFunctionFieldRaw(int hi, int lo) const { ++ return this->InstructionBits() & SwGetMask(hi, lo); ++ } ++ ++ inline int SwImmOrDispFieldRaw(int hi, int lo) const { ++ return this->InstructionBits() & SwGetMask(hi, lo); ++ } ++ ++ inline int SwImmOrDispFieldValue(int hi, int lo) const { ++ int shift_len = 32 - (hi - lo + 1); ++ return (((this->InstructionBits() & SwGetMask(hi, lo)) >> lo) ++ << shift_len) >> ++ shift_len; ++ } ++ ++ inline int SwRaFieldRaw() const { ++ return this->InstructionBits() & sRaFieldMask; // SwGetMask(25,21); ++ } ++ inline int SwRbFieldRaw() const { ++ return this->InstructionBits() & sRbFieldMask; // SwGetMask(20,16); ++ } ++ inline int SwRcFieldRaw(int hi, int lo) const { ++ return this->InstructionBits() & SwGetMask(hi, lo); ++ } ++ inline int SwRdFieldRaw() const { ++ return this->InstructionBits() & sRdFieldMask; // SwGetMask(4,0); ++ } ++ ++ inline int SwFaFieldRaw() const { ++ return this->InstructionBits() & sRaFieldMask; // SwGetMask(25,21); ++ } ++ inline int SwFbFieldRaw() const { ++ return this->InstructionBits() & sRbFieldMask; // SwGetMask(20,16); ++ } ++ inline int SwFcFieldRaw(int hi, int lo) const { ++ return this->InstructionBits() & SwGetMask(hi, lo); ++ } ++ inline int SwFdFieldRaw() const { ++ return this->InstructionBits() & sRdFieldMask; // SwGetMask(4,0); ++ } ++ ++ inline int SwRaValue() const { ++ return this->Bits(sRaShift + sRaBits - 1, sRaShift); // Bits(25,21); ++ } ++ inline int SwRbValue() const { ++ return this->Bits(sRbShift + sRbBits - 1, sRbShift); // Bits(20,16); ++ } ++ // the position of rc register in SW is different in different type of ++ // instructions ++ inline int SwRcValue(int hi, int lo) const { return this->Bits(hi, lo); } ++ inline int SwRdValue() const { ++ return this->Bits(sRdShift + sRdBits - 1, sRdShift); // Bits(4,0); ++ } ++ inline int SwFaValue() const { ++ return this->Bits(sRaShift + sRaBits - 1, sRaShift); // Bits(25,21); ++ } ++ inline int SwFbValue() const { ++ return this->Bits(sRbShift + sRbBits - 1, sRbShift); // Bits(20,16); ++ } ++ // the position of rc register in SW is different in different type of ++ // instructions ++ inline int SwFcValue(int hi, int lo) const { return this->Bits(hi, lo); } ++ inline int SwFdValue() const { ++ return this->Bits(sRdShift + sRdBits - 1, sRdShift); // Bits(4,0); ++ } ++ ++ static bool IsForbiddenAfterBranchInstr(Instr instr); ++ ++ // Say if the instruction should not be used in a branch delay slot or ++ // immediately after a compact branch. ++ inline bool IsForbiddenAfterBranch() const { ++ return IsForbiddenAfterBranchInstr(this->InstructionBits()); ++ } ++ ++ inline bool IsForbiddenInBranchDelay() const { ++ return IsForbiddenAfterBranch(); ++ } ++ ++ // Say if the instruction is a break or a trap. ++ bool IsTrap() const; ++}; ++ ++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. ++ DISALLOW_IMPLICIT_CONSTRUCTORS(Instruction); ++}; ++ ++// ----------------------------------------------------------------------------- ++// SW64 assembly various constants. ++ ++// C/C++ argument slots size. ++const int kCArgSlotCount = 0; ++ ++// TODO(plind): below should be based on kPointerSize ++// TODO(plind): find all usages and remove the needless instructions for n64. ++const int kCArgsSlotsSize = kCArgSlotCount * kInstrSize * 2; ++ ++const int kInvalidStackOffset = -1; ++const int kBranchReturnOffset = 1 * kInstrSize; ++ ++static const int kNegOffset = 0x00008000; ++ ++InstructionBase::Type InstructionBase::InstructionType() const { ++ switch (OpcodeFieldValue()) { ++ // cjq 20150317: add instruction 'sys_call' to do ++ case op_sys_call: ++ return kSwSyscallType; ++ ++ case op_call: ++ case op_ret: ++ case op_jmp: ++ case op_ldwe: ++ case op_ldse: ++ case op_ldde: ++ case op_vlds: ++ case op_vldd: ++ case op_vsts: ++ case op_vstd: ++ case op_ldbu: ++ case op_ldhu: ++ case op_ldw: ++ case op_ldl: ++ case op_ldl_u: ++ case op_flds: ++ case op_fldd: ++ case op_stb: ++ case op_sth: ++ case op_stw: ++ case op_stl: ++ case op_stl_u: ++ case op_fsts: ++ case op_fstd: ++ case op_ldi: ++ case op_ldih: ++ return kSwStorageType; ++ case OP(0x08): ++ return kSwStorageType; ++ case OP(0x06): ++ return kSwStorageType; // jzy 20150213:TODO ++ ++ case op_br: ++ case op_bsr: ++ case op_beq: ++ case op_bne: ++ case op_blt: ++ case op_ble: ++ case op_bgt: ++ case op_bge: ++ case op_blbc: ++ case op_blbs: ++ case op_fbeq: ++ case op_fbne: ++ case op_fblt: ++ case op_fble: ++ case op_fbgt: ++ case op_fbge: ++ return kSwTransferanceType; // ld 20150319 ++ ++ case OP(0x10): ++ case OP(0x12): ++ case OP(0x18): ++ return kSwSimpleCalculationType; ++ ++ case OP(0x11): ++ case OP(0x13): ++ case OP(0x19): ++ return kSwCompositeCalculationType; ++ ++ case op_trap: ++ return kSwSimulatorTrap; ++ ++ default: ++ return kImmediateType; ++ } ++ return kUnsupported; ++} ++ ++#undef OP ++#undef PCD ++#undef OPMEM ++#undef BRA ++ ++#undef OFP ++#undef FMA ++#undef MFC ++#undef MBR ++#undef OPR ++#undef OPRL ++#undef TOPR ++#undef TOPRL ++ ++#undef ATMEM ++#undef PRIRET ++#undef SPCD ++#undef EV6HWMEM ++#undef CSR ++ ++#undef LOGX ++#undef PSE_LOGX ++ ++} // namespace internal ++} // namespace v8 ++ ++#endif // V8_CODEGEN_SW64_CONSTANTS_SW64_H_ +diff --git a/deps/v8/src/codegen/sw64/cpu-sw64.cc b/deps/v8/src/codegen/sw64/cpu-sw64.cc +new file mode 100644 +index 00000000..8faa01d5 +--- /dev/null ++++ b/deps/v8/src/codegen/sw64/cpu-sw64.cc +@@ -0,0 +1,40 @@ ++// Copyright 2012 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// CPU specific code for arm independent of OS goes here. ++ ++#include ++#include ++ ++#if V8_TARGET_ARCH_SW64 ++ ++#include "src/codegen/assembler.h" ++#include "src/codegen/cpu-features.h" ++#include "src/codegen/macro-assembler.h" ++#include "src/execution/simulator.h" // For cache flushing. ++ ++namespace v8 { ++namespace internal { ++ ++void CpuFeatures::FlushICache(void* start, size_t size) { ++#if !defined(USE_SIMULATOR) ++ // Nothing to do, flushing no instructions. ++ if (size == 0) { ++ return; ++ } ++ ++#if defined(__sw_64__) ++ asm volatile( ++ "ldi $0, 266($31)\n" ++ "sys_call 0x83\n"); ++#else ++#error "Target Architecture are not supported in CpuFeatures::FlushICache" ++#endif ++#endif // !USE_SIMULATOR. ++} ++ ++} // namespace internal ++} // namespace v8 ++ ++#endif // V8_TARGET_ARCH_SW64 +diff --git a/deps/v8/src/codegen/sw64/interface-descriptors-sw64-inl.h b/deps/v8/src/codegen/sw64/interface-descriptors-sw64-inl.h +new file mode 100644 +index 00000000..4ab10c96 +--- /dev/null ++++ b/deps/v8/src/codegen/sw64/interface-descriptors-sw64-inl.h +@@ -0,0 +1,320 @@ ++// Copyright 2012 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef V8_CODEGEN_SW64_INTERFACE_DESCRIPTORS_SW64_INL_H_ ++#define V8_CODEGEN_SW64_INTERFACE_DESCRIPTORS_SW64_INL_H_ ++ ++#if V8_TARGET_ARCH_SW64 ++ ++#include "src/codegen/interface-descriptors.h" ++#include "src/execution/frames.h" ++ ++namespace v8 { ++namespace internal { ++ ++constexpr auto CallInterfaceDescriptor::DefaultRegisterArray() { ++ auto registers = RegisterArray(a0, a1, a2, a3, a4); ++ STATIC_ASSERT(registers.size() == kMaxBuiltinRegisterParams); ++ return registers; ++} ++ ++#if DEBUG ++template ++void StaticCallInterfaceDescriptor:: ++ VerifyArgumentRegisterCount(CallInterfaceDescriptorData* data, int argc) { ++ RegList allocatable_regs = data->allocatable_registers(); ++ if (argc >= 1) DCHECK(allocatable_regs.has(a0)); ++ if (argc >= 2) DCHECK(allocatable_regs.has(a1)); ++ if (argc >= 3) DCHECK(allocatable_regs.has(a2)); ++ if (argc >= 4) DCHECK(allocatable_regs.has(a3)); ++ if (argc >= 5) DCHECK(allocatable_regs.has(a4)); ++ if (argc >= 6) DCHECK(allocatable_regs.has(a5)); ++ // if (argc >= 7) DCHECK(allocatable_regs.has(t9)); ++ // if (argc >= 8) DCHECK(allocatable_regs.has(t10)); ++ // Additional arguments are passed on the stack. ++} ++#endif // DEBUG ++ ++// static ++constexpr auto WriteBarrierDescriptor::registers() { ++ return RegisterArray(a1, a5, a4, a0, a2, v0, a3, kContextRegister); ++} ++ ++// static ++constexpr Register LoadDescriptor::ReceiverRegister() { return a1; } ++// static ++constexpr Register LoadDescriptor::NameRegister() { return a2; } ++// static ++constexpr Register LoadDescriptor::SlotRegister() { return a0; } ++ ++// static ++constexpr Register LoadWithVectorDescriptor::VectorRegister() { return a3; } ++ ++// static ++constexpr Register KeyedLoadBaselineDescriptor::ReceiverRegister() { ++ return a1; ++} ++// static ++constexpr Register KeyedLoadBaselineDescriptor::NameRegister() { ++ return kInterpreterAccumulatorRegister; ++} ++// static ++constexpr Register KeyedLoadBaselineDescriptor::SlotRegister() { return a2; } ++ ++// static ++constexpr Register KeyedLoadWithVectorDescriptor::VectorRegister() { ++ return a3; ++} ++ ++// static ++constexpr Register KeyedHasICBaselineDescriptor::ReceiverRegister() { ++ return kInterpreterAccumulatorRegister; ++} ++// static ++constexpr Register KeyedHasICBaselineDescriptor::NameRegister() { return a1; } ++// static ++constexpr Register KeyedHasICBaselineDescriptor::SlotRegister() { return a2; } ++ ++// static ++constexpr Register KeyedHasICWithVectorDescriptor::VectorRegister() { ++ return a3; ++} ++ ++// static ++constexpr Register ++LoadWithReceiverAndVectorDescriptor::LookupStartObjectRegister() { ++ return a4; ++} ++ ++// static ++constexpr Register StoreDescriptor::ReceiverRegister() { return a1; } ++// static ++constexpr Register StoreDescriptor::NameRegister() { return a2; } ++// static ++constexpr Register StoreDescriptor::ValueRegister() { return a0; } ++// static ++constexpr Register StoreDescriptor::SlotRegister() { return a4; } ++ ++// static ++constexpr Register StoreWithVectorDescriptor::VectorRegister() { return a3; } ++ ++// static ++constexpr Register StoreTransitionDescriptor::MapRegister() { return a5; } ++ ++// static ++constexpr Register ApiGetterDescriptor::HolderRegister() { return a0; } ++// static ++constexpr Register ApiGetterDescriptor::CallbackRegister() { return a3; } ++ ++// static ++constexpr Register GrowArrayElementsDescriptor::ObjectRegister() { return a0; } ++// static ++constexpr Register GrowArrayElementsDescriptor::KeyRegister() { return a3; } ++ ++// static ++constexpr Register BaselineLeaveFrameDescriptor::ParamsSizeRegister() { ++ return a2; ++} ++ ++// static ++constexpr Register BaselineLeaveFrameDescriptor::WeightRegister() { return a3; } ++ ++// static ++constexpr Register TypeConversionDescriptor::ArgumentRegister() { return a0; } ++ ++// static ++constexpr auto TypeofDescriptor::registers() { return RegisterArray(a0); } ++ ++// static ++constexpr auto CallTrampolineDescriptor::registers() { ++ // a1: target ++ // a0: number of arguments ++ return RegisterArray(a1, a0); ++} ++ ++// static ++constexpr auto CopyDataPropertiesWithExcludedPropertiesDescriptor::registers() { ++ // a1 : the source ++ // a0 : the excluded property count ++ return RegisterArray(a1, a0); ++} ++ ++// static ++constexpr auto ++CopyDataPropertiesWithExcludedPropertiesOnStackDescriptor::registers() { ++ // a1 : the source ++ // a0 : the excluded property count ++ // a2 : the excluded property base ++ return RegisterArray(a1, a0, a2); ++} ++ ++// static ++constexpr auto CallVarargsDescriptor::registers() { ++ // a0 : number of arguments (on the stack) ++ // a1 : the target to call ++ // a4 : arguments list length (untagged) ++ // a2 : arguments list (FixedArray) ++ return RegisterArray(a1, a0, a4, a2); ++} ++ ++// static ++constexpr auto CallForwardVarargsDescriptor::registers() { ++ // a1: the target to call ++ // a0: number of arguments ++ // a2: start index (to support rest parameters) ++ return RegisterArray(a1, a0, a2); ++} ++ ++// static ++constexpr auto CallFunctionTemplateDescriptor::registers() { ++ // a1 : function template info ++ // a0 : number of arguments (on the stack) ++ return RegisterArray(a1, a0); ++} ++ ++// static ++constexpr auto CallWithSpreadDescriptor::registers() { ++ // a0 : number of arguments (on the stack) ++ // a1 : the target to call ++ // a2 : the object to spread ++ return RegisterArray(a1, a0, a2); ++} ++ ++// static ++constexpr auto CallWithArrayLikeDescriptor::registers() { ++ // a1 : the target to call ++ // a2 : the arguments list ++ return RegisterArray(a1, a2); ++} ++ ++// static ++constexpr auto ConstructVarargsDescriptor::registers() { ++ // a0 : number of arguments (on the stack) ++ // a1 : the target to call ++ // a3 : the new target ++ // a4 : arguments list length (untagged) ++ // a2 : arguments list (FixedArray) ++ return RegisterArray(a1, a3, a0, a4, a2); ++} ++ ++// static ++constexpr auto ConstructForwardVarargsDescriptor::registers() { ++ // a1: the target to call ++ // a3: new target ++ // a0: number of arguments ++ // a2: start index (to support rest parameters) ++ return RegisterArray(a1, a3, a0, a2); ++} ++ ++// static ++constexpr auto ConstructWithSpreadDescriptor::registers() { ++ // a0 : number of arguments (on the stack) ++ // a1 : the target to call ++ // a3 : the new target ++ // a2 : the object to spread ++ return RegisterArray(a1, a3, a0, a2); ++} ++ ++// static ++constexpr auto ConstructWithArrayLikeDescriptor::registers() { ++ // a1 : the target to call ++ // a3 : the new target ++ // a2 : the arguments list ++ return RegisterArray(a1, a3, a2); ++} ++ ++// static ++constexpr auto ConstructStubDescriptor::registers() { ++ // a1: target ++ // a3: new target ++ // a0: number of arguments ++ // a2: allocation site or undefined ++ return RegisterArray(a1, a3, a0, a2); ++} ++ ++// static ++constexpr auto AbortDescriptor::registers() { return RegisterArray(a0); } ++ ++// static ++constexpr auto CompareDescriptor::registers() { return RegisterArray(a1, a0); } ++ ++// static ++constexpr auto Compare_BaselineDescriptor::registers() { ++ // a1: left operand ++ // a0: right operand ++ // a2: feedback slot ++ return RegisterArray(a1, a0, a2); ++} ++ ++// static ++constexpr auto BinaryOpDescriptor::registers() { return RegisterArray(a1, a0); } ++ ++// static ++constexpr auto BinaryOp_BaselineDescriptor::registers() { ++ // a1: left operand ++ // a0: right operand ++ // a2: feedback slot ++ return RegisterArray(a1, a0, a2); ++} ++ ++// static ++constexpr auto BinarySmiOp_BaselineDescriptor::registers() { ++ // a0: left operand ++ // a1: right operand ++ // a2: feedback slot ++ return RegisterArray(a0, a1, a2); ++} ++ ++// static ++constexpr auto ApiCallbackDescriptor::registers() { ++ // a1 : kApiFunctionAddress ++ // a2 : kArgc ++ // a3 : kCallData ++ // a0 : kHolder ++ return RegisterArray(a1, a2, a3, a0); ++} ++ ++// static ++constexpr auto InterpreterDispatchDescriptor::registers() { ++ return RegisterArray( ++ kInterpreterAccumulatorRegister, kInterpreterBytecodeOffsetRegister, ++ kInterpreterBytecodeArrayRegister, kInterpreterDispatchTableRegister); ++} ++ ++// static ++constexpr auto InterpreterPushArgsThenCallDescriptor::registers() { ++ // a0 : argument count ++ // a2 : address of first argument ++ // a1 : the target callable to be call ++ return RegisterArray(a0, a2, a1); ++} ++ ++// static ++constexpr auto InterpreterPushArgsThenConstructDescriptor::registers() { ++ // a0 : argument count ++ // a4 : address of the first argument ++ // a1 : constructor to call ++ // a3 : new target ++ // a2 : allocation site feedback if available, undefined otherwise ++ return RegisterArray(a0, a4, a1, a3, a2); ++} ++ ++// static ++constexpr auto ResumeGeneratorDescriptor::registers() { ++ // v0 : the value to pass to the generator ++ // a1 : the JSGeneratorObject to resume ++ return RegisterArray(v0, a1); ++} ++ ++// static ++constexpr auto RunMicrotasksEntryDescriptor::registers() { ++ return RegisterArray(a0, a1); ++} ++ ++} // namespace internal ++} // namespace v8 ++ ++#endif // V8_TARGET_ARCH_SW64 ++ ++#endif // V8_CODEGEN_SW64_INTERFACE_DESCRIPTORS_SW64_INL_H_ +diff --git a/deps/v8/src/codegen/sw64/macro-assembler-sw64.cc b/deps/v8/src/codegen/sw64/macro-assembler-sw64.cc +new file mode 100644 +index 00000000..76528ed8 +--- /dev/null ++++ b/deps/v8/src/codegen/sw64/macro-assembler-sw64.cc +@@ -0,0 +1,5575 @@ ++// Copyright 2012 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include // For LONG_MIN, LONG_MAX. ++ ++#if V8_TARGET_ARCH_SW64 ++ ++#include "src/base/bits.h" ++#include "src/base/division-by-constant.h" ++#include "src/codegen/assembler-inl.h" ++#include "src/codegen/callable.h" ++#include "src/codegen/code-factory.h" ++#include "src/codegen/external-reference-table.h" ++#include "src/codegen/interface-descriptors-inl.h" ++#include "src/codegen/macro-assembler.h" ++#include "src/codegen/register-configuration.h" ++#include "src/debug/debug.h" ++#include "src/deoptimizer/deoptimizer.h" ++#include "src/execution/frames-inl.h" ++#include "src/heap/memory-chunk.h" ++#include "src/init/bootstrapper.h" ++#include "src/logging/counters.h" ++#include "src/objects/heap-number.h" ++#include "src/runtime/runtime.h" ++#include "src/snapshot/snapshot.h" ++ ++#if V8_ENABLE_WEBASSEMBLY ++#include "src/wasm/wasm-code-manager.h" ++#endif // V8_ENABLE_WEBASSEMBLY ++ ++// Satisfy cpplint check, but don't include platform-specific header. It is ++// included recursively via macro-assembler.h. ++#if 0 ++#include "src/codegen/sw64/macro-assembler-sw64.h" ++#endif ++ ++namespace v8 { ++namespace internal { ++ ++static inline bool IsZero(const Operand& rt) { ++ if (rt.is_reg()) { ++ return rt.rm() == zero_reg; ++ } else { ++ return rt.immediate() == 0; ++ } ++} ++ ++int TurboAssembler::RequiredStackSizeForCallerSaved(SaveFPRegsMode fp_mode, ++ Register exclusion1, ++ Register exclusion2, ++ Register exclusion3) const { ++ int bytes = 0; ++ RegList exclusions = {exclusion1, exclusion2, exclusion3}; ++ RegList list = kJSCallerSaved - exclusions; ++ bytes += list.Count() * kPointerSize; ++ ++ if (fp_mode == SaveFPRegsMode::kSave) { ++ bytes += kCallerSavedFPU.Count() * kDoubleSize; ++ } ++ ++ return bytes; ++} ++ ++int TurboAssembler::PushCallerSaved(SaveFPRegsMode fp_mode, Register exclusion1, ++ Register exclusion2, Register exclusion3) { ++ ASM_CODE_COMMENT(this); ++ int bytes = 0; ++ RegList exclusions = {exclusion1, exclusion2, exclusion3}; ++ RegList list = kJSCallerSaved - exclusions; ++ MultiPush(list); ++ bytes += list.Count() * kPointerSize; ++ ++ if (fp_mode == SaveFPRegsMode::kSave) { ++ MultiPushFPU(kCallerSavedFPU); ++ bytes += kCallerSavedFPU.Count() * kDoubleSize; ++ } ++ ++ return bytes; ++} ++ ++int TurboAssembler::PopCallerSaved(SaveFPRegsMode fp_mode, Register exclusion1, ++ Register exclusion2, Register exclusion3) { ++ ASM_CODE_COMMENT(this); ++ int bytes = 0; ++ if (fp_mode == SaveFPRegsMode::kSave) { ++ MultiPopFPU(kCallerSavedFPU); ++ bytes += kCallerSavedFPU.Count() * kDoubleSize; ++ } ++ ++ RegList exclusions = {exclusion1, exclusion2, exclusion3}; ++ RegList list = kJSCallerSaved - exclusions; ++ MultiPop(list); ++ bytes += list.Count() * kPointerSize; ++ ++ return bytes; ++} ++ ++void TurboAssembler::LoadRoot(Register destination, RootIndex index) { ++ Ldl(destination, MemOperand(s4, RootRegisterOffsetForRootIndex(index))); ++} ++ ++void TurboAssembler::LoadRoot(Register destination, RootIndex index, ++ Condition cond, Register src1, ++ const Operand& src2) { ++ Branch(2, NegateCondition(cond), src1, src2); ++ Ldl(destination, MemOperand(s4, RootRegisterOffsetForRootIndex(index))); ++} ++ ++void TurboAssembler::PushCommonFrame(Register marker_reg) { ++ if (marker_reg.is_valid()) { ++ Push(ra, fp, marker_reg); ++ Addl(fp, sp, Operand(kPointerSize)); ++ } else { ++ Push(ra, fp); ++ mov(fp, sp); ++ } ++} ++ ++void TurboAssembler::PushStandardFrame(Register function_reg) { ++ int offset = -StandardFrameConstants::kContextOffset; ++ if (function_reg.is_valid()) { ++ Push(ra, fp, cp, function_reg, kJavaScriptCallArgCountRegister); ++ offset += 2 * kPointerSize; ++ } else { ++ Push(ra, fp, cp, kJavaScriptCallArgCountRegister); ++ offset += kPointerSize; ++ } ++ Addl(fp, sp, Operand(offset)); ++} ++ ++// Clobbers object, dst, value, and ra, if (ra_status == kRAHasBeenSaved) ++// The register 'object' contains a heap object pointer. The heap object ++// tag is shifted away. ++void MacroAssembler::RecordWriteField(Register object, int offset, ++ Register value, Register dst, ++ RAStatus ra_status, ++ SaveFPRegsMode save_fp, ++ RememberedSetAction remembered_set_action, ++ SmiCheck smi_check) { ++ ASM_CODE_COMMENT(this); ++ DCHECK(!AreAliased(value, dst, t11, object)); ++ // First, check if a write barrier is even needed. The tests below ++ // catch stores of Smis. ++ Label done; ++ ++ // Skip barrier if writing a smi. ++ if (smi_check == SmiCheck::kInline) { ++ JumpIfSmi(value, &done); ++ } ++ ++ // Although the object register is tagged, the offset is relative to the start ++ // of the object, so offset must be a multiple of kPointerSize. ++ DCHECK(IsAligned(offset, kPointerSize)); ++ ++ Addl(dst, object, Operand(offset - kHeapObjectTag)); ++ if (FLAG_debug_code) { ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ Label ok; ++ And(t11, dst, Operand(kPointerSize - 1)); ++ Branch(&ok, eq, t11, Operand(zero_reg)); ++ halt(); // stop("Unaligned cell in write barrier"); ++ bind(&ok); ++ } ++ ++ RecordWrite(object, dst, value, ra_status, save_fp, remembered_set_action, ++ SmiCheck::kOmit); ++ ++ bind(&done); ++ ++ // Clobber clobbered input registers when running with the debug-code flag ++ // turned on to provoke errors. ++ if (FLAG_debug_code) { ++ li(value, Operand(bit_cast(kZapValue + 4))); ++ li(dst, Operand(bit_cast(kZapValue + 8))); ++ } ++} ++ ++void TurboAssembler::MaybeSaveRegisters(RegList registers) { ++ if (registers.is_empty()) return; ++ MultiPush(registers); ++} ++ ++void TurboAssembler::MaybeRestoreRegisters(RegList registers) { ++ if (registers.is_empty()) return; ++ MultiPop(registers); ++} ++ ++void TurboAssembler::CallEphemeronKeyBarrier(Register object, ++ Register slot_address, ++ SaveFPRegsMode fp_mode) { ++ ASM_CODE_COMMENT(this); ++ DCHECK(!AreAliased(object, slot_address)); ++ RegList registers = ++ WriteBarrierDescriptor::ComputeSavedRegisters(object, slot_address); ++ MaybeSaveRegisters(registers); ++ ++ Register object_parameter = WriteBarrierDescriptor::ObjectRegister(); ++ Register slot_address_parameter = ++ WriteBarrierDescriptor::SlotAddressRegister(); ++ ++ Push(object); ++ Push(slot_address); ++ Pop(slot_address_parameter); ++ Pop(object_parameter); ++ ++ Call(isolate()->builtins()->code_handle( ++ Builtins::GetEphemeronKeyBarrierStub(fp_mode)), ++ RelocInfo::CODE_TARGET); ++ MaybeRestoreRegisters(registers); ++} ++ ++void TurboAssembler::CallRecordWriteStubSaveRegisters( ++ Register object, Register slot_address, ++ RememberedSetAction remembered_set_action, SaveFPRegsMode fp_mode, ++ StubCallMode mode) { ++ DCHECK(!AreAliased(object, slot_address)); ++ RegList registers = ++ WriteBarrierDescriptor::ComputeSavedRegisters(object, slot_address); ++ MaybeSaveRegisters(registers); ++ ++ Register object_parameter = WriteBarrierDescriptor::ObjectRegister(); ++ Register slot_address_parameter = ++ WriteBarrierDescriptor::SlotAddressRegister(); ++ ++ Push(object); ++ Push(slot_address); ++ Pop(slot_address_parameter); ++ Pop(object_parameter); ++ ++ CallRecordWriteStub(object_parameter, slot_address_parameter, ++ remembered_set_action, fp_mode, mode); ++ ++ MaybeRestoreRegisters(registers); ++} ++ ++void TurboAssembler::CallRecordWriteStub( ++ Register object, Register slot_address, ++ RememberedSetAction remembered_set_action, SaveFPRegsMode fp_mode, ++ StubCallMode mode) { ++ // Use CallRecordWriteStubSaveRegisters if the object and slot registers ++ // need to be caller saved. ++ DCHECK_EQ(WriteBarrierDescriptor::ObjectRegister(), object); ++ DCHECK_EQ(WriteBarrierDescriptor::SlotAddressRegister(), slot_address); ++#if V8_ENABLE_WEBASSEMBLY ++ if (mode == StubCallMode::kCallWasmRuntimeStub) { ++ auto wasm_target = ++ wasm::WasmCode::GetRecordWriteStub(remembered_set_action, fp_mode); ++ Call(wasm_target, RelocInfo::WASM_STUB_CALL); ++#else ++ if (false) { ++#endif ++ } else { ++ auto builtin = Builtins::GetRecordWriteStub(remembered_set_action, fp_mode); ++ if (options().inline_offheap_trampolines) { ++ // Inline the trampoline. ++ RecordCommentForOffHeapTrampoline(builtin); ++ li(t12, Operand(BuiltinEntry(builtin), RelocInfo::OFF_HEAP_TARGET)); ++ Call(t12); ++ RecordComment("]"); ++ } else { ++ Handle code_target = isolate()->builtins()->code_handle(builtin); ++ Call(code_target, RelocInfo::CODE_TARGET); ++ } ++ } ++} ++ ++// Clobbers object, address, value, and ra, if (ra_status == kRAHasBeenSaved) ++// The register 'object' contains a heap object pointer. The heap object ++// tag is shifted away. ++void MacroAssembler::RecordWrite(Register object, Register address, ++ Register value, RAStatus ra_status, ++ SaveFPRegsMode fp_mode, ++ RememberedSetAction remembered_set_action, ++ SmiCheck smi_check) { ++ DCHECK(!AreAliased(object, address, value, t8)); ++ DCHECK(!AreAliased(object, address, value, t9)); ++ ++ if (FLAG_debug_code) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ DCHECK(!AreAliased(object, value, scratch)); ++ Ldl(scratch, MemOperand(address)); ++ Assert(eq, AbortReason::kWrongAddressOrValuePassedToRecordWrite, scratch, ++ Operand(value)); ++ } ++ ++ if ((remembered_set_action == RememberedSetAction::kOmit && ++ !FLAG_incremental_marking) || ++ FLAG_disable_write_barriers) { ++ return; ++ } ++ ++ // First, check if a write barrier is even needed. The tests below ++ // catch stores of smis and stores into the young generation. ++ Label done; ++ ++ if (smi_check == SmiCheck::kInline) { ++ DCHECK_EQ(0, kSmiTag); ++ JumpIfSmi(value, &done); ++ } ++ ++ CheckPageFlag(value, ++ value, // Used as scratch. ++ MemoryChunk::kPointersToHereAreInterestingMask, eq, &done); ++ CheckPageFlag(object, ++ value, // Used as scratch. ++ MemoryChunk::kPointersFromHereAreInterestingMask, eq, &done); ++ ++ // Record the actual write. ++ if (ra_status == kRAHasNotBeenSaved) { ++ Push(ra); ++ } ++ ++ Register slot_address = WriteBarrierDescriptor::SlotAddressRegister(); ++ DCHECK(!AreAliased(object, slot_address, value)); ++ mov(slot_address, address); ++ CallRecordWriteStub(object, slot_address, remembered_set_action, fp_mode); ++ ++ if (ra_status == kRAHasNotBeenSaved) { ++ pop(ra); ++ } ++ ++ bind(&done); ++ ++ // Clobber clobbered registers when running with the debug-code flag ++ // turned on to provoke errors. ++ if (FLAG_debug_code) { ++ li(address, Operand(bit_cast(kZapValue + 12))); ++ li(value, Operand(bit_cast(kZapValue + 16))); ++ li(slot_address, Operand(bit_cast(kZapValue + 20))); ++ } ++} ++ ++// --------------------------------------------------------------------------- ++// Instruction macros. ++ ++void TurboAssembler::Addw(Register rd, Register rs, const Operand& rt) { ++ // SCOPEMARK_NAME(TurboAssembler::Addw, this); ++ if (rt.is_reg()) { ++ addw(rs, rt.rm(), rd); ++ } else { ++ if (is_uint8(rt.immediate()) && !MustUseReg(rt.rmode())) { ++ addw(rs, (int)rt.immediate(), rd); ++ } else if (is_uint8(-rt.immediate()) && !MustUseReg(rt.rmode())) { ++ subw(rs, (int)-rt.immediate(), rd); ++ } else { ++ // li handles the relocation. ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ DCHECK(rs != scratch); ++ li(scratch, rt); ++ addw(rs, scratch, rd); ++ } ++ } ++} ++ ++void TurboAssembler::Addl(Register rd, Register rs, const Operand& rt) { ++ // SCOPEMARK_NAME(TurboAssembler::Addl, this); ++ if (rt.is_reg()) { ++ addl(rs, rt.rm(), rd); ++ } else { ++ if (is_uint8(rt.immediate()) && !MustUseReg(rt.rmode())) { ++ addl(rs, (int)rt.immediate(), rd); ++ } else if (is_uint8(-rt.immediate()) && !MustUseReg(rt.rmode())) { ++ subl(rs, (int)-rt.immediate(), rd); ++ } else { ++ // li handles the relocation. ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ DCHECK(rs != scratch); ++ li(scratch, rt); ++ addl(rs, scratch, rd); ++ } ++ } ++} ++ ++void TurboAssembler::Subw(Register rd, Register rs, const Operand& rt) { ++ // SCOPEMARK_NAME(TurboAssembler::Subw, this); ++ if (rt.is_reg()) { ++ subw(rs, rt.rm(), rd); ++ } else { ++ if (is_uint8(-rt.immediate()) && !MustUseReg(rt.rmode())) { ++ addw(rs, (int)-rt.immediate(), rd); ++ } else if (is_uint8(rt.immediate()) && !MustUseReg(rt.rmode())) { ++ subw(rs, (int)rt.immediate(), rd); ++ } else { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ DCHECK(rs != scratch); ++ li(scratch, rt); ++ subw(rs, scratch, rd); ++ } ++ } ++} ++ ++void TurboAssembler::Subl(Register rd, Register rs, const Operand& rt) { ++ // SCOPEMARK_NAME(TurboAssembler::Subl, this); ++ if (rt.is_reg()) { ++ subl(rs, rt.rm(), rd); ++ } else if (is_uint8(-rt.immediate()) && !MustUseReg(rt.rmode())) { ++ addl(rs, (int)-rt.immediate(), rd); ++ } else if (is_uint8(rt.immediate()) && !MustUseReg(rt.rmode())) { ++ subl(rs, (int)rt.immediate(), rd); ++ } else { ++ DCHECK(rs != at); ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ li(scratch, rt); ++ subl(rs, scratch, rd); ++ } ++} ++ ++void TurboAssembler::Mulw(Register rd, Register rs, const Operand& rt) { ++ // SCOPEMARK_NAME(TurboAssembler::Mulw, this); ++ if (rt.is_reg()) { ++ mulw(rs, rt.rm(), rd); ++ } else { ++ if (is_uint8(rt.immediate()) && !MustUseReg(rt.rmode())) { ++ mulw(rs, (int)rt.immediate(), rd); ++ } else { ++ // li handles the relocation. ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ DCHECK(rs != scratch); ++ li(scratch, rt); ++ mulw(rs, scratch, rd); ++ } ++ } ++} ++ ++void TurboAssembler::Mulwh(Register rd, Register rs, const Operand& rt) { ++ // SCOPEMARK_NAME(TurboAssembler::Mulwh, this); ++ if (rt.is_reg()) { ++ mull(rs, rt.rm(), rd); ++ } else { ++ if (is_uint8(rt.immediate()) && !MustUseReg(rt.rmode())) { ++ mull(rs, (int)rt.immediate(), rd); ++ } else { ++ // li handles the relocation. ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ DCHECK(rs != scratch); ++ li(scratch, rt); ++ mull(rs, scratch, rd); ++ } ++ } ++ sral(rd, 32, rd); ++} ++//( unsigned rs[31..0] * unsigned rt[31..0] )>>32 --> rd [63..32] ++void TurboAssembler::Mulwhu(Register rd, Register rs, const Operand& rt) { ++ // SCOPEMARK_NAME(TurboAssembler::Mulwhu, this); ++ if (rt.is_reg()) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ DCHECK(rs != scratch); ++ DCHECK(rt.rm() != scratch); ++ zapnot(rs, 0xf, scratch); ++ zapnot(rt.rm(), 0xf, rd); ++ mull(scratch, rd, rd); ++ } else { ++ if (is_uint8(rt.immediate()) && !MustUseReg(rt.rmode())) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ DCHECK(rs != scratch); ++ zapnot(rs, 0xf, scratch); ++ mull(scratch, (int)rt.immediate(), rd); ++ } else { ++ // li handles the relocation. ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ DCHECK(rs != scratch); ++ li(scratch, rt); ++ zapnot(scratch, 0xf, scratch); ++ zapnot(rs, 0xf, rd); ++ mull(rd, scratch, rd); ++ } ++ } ++ sral(rd, 32, rd); ++} ++ ++void TurboAssembler::Mull(Register rd, Register rs, const Operand& rt) { ++ // SCOPEMARK_NAME(TurboAssembler::Mull, this); ++ if (rt.is_reg()) { ++ mull(rs, rt.rm(), rd); ++ } else { ++ if (is_uint8(rt.immediate()) && !MustUseReg(rt.rmode())) { ++ mull(rs, (int)rt.immediate(), rd); ++ } else { ++ // li handles the relocation. ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ DCHECK(rs != scratch); ++ li(scratch, rt); ++ mull(rs, scratch, rd); ++ } ++ } ++} ++ ++void TurboAssembler::Mullh(Register rd, Register rs, const Operand& rt) { ++ // SCOPEMARK_NAME(TurboAssembler::Mullh, this); ++ if (rt.is_reg()) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire();//t3; ++ DCHECK(rs != scratch); ++ DCHECK(rt.rm() != scratch); ++ DCHECK(a5 != scratch); ++ umulh(rs, rt.rm(), a5); ++ srll(rs, 63, scratch); ++ mull(scratch, rt.rm(), scratch); ++ subl(a5, scratch, a5); ++ srll(rt.rm(), 63, scratch); ++ mull(scratch, rs, scratch); ++ subl(a5, scratch, rd); ++ } else { ++ // li handles the relocation. ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ DCHECK(rs != scratch); ++ DCHECK(a5 != scratch); ++ li(scratch, rt); ++ umulh(rs, scratch, rd); ++ srll(rs, 63, a5); ++ mull(a5, scratch, a5); ++ subl(rd, a5, rd); ++ srll(scratch, 63, a5); ++ mull(a5, rs, a5); ++ subl(rd, a5, rd); ++ } ++} ++ ++void TurboAssembler::Divw(Register rd, Register rs, const Operand& rt) { ++ FPURegister fsrc1 = f22; ++ FPURegister fsrc2 = f23; ++ FPURegister fdest = f24; ++ ++ if (rt.is_reg()) { ++ ifmovd(rs, fsrc1); ++ ifmovd(rt.rm(), fsrc2); ++ fcvtld(fsrc1, fsrc1); ++ fcvtld(fsrc2, fsrc2); ++ fdivd(fsrc1, fsrc2, fdest); ++ fcvtdl_z(fdest, fdest); ++ fcvtlw(fdest, fdest); ++ fimovs(fdest, rd); ++ } else { ++ // li handles the relocation. ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ DCHECK(rs != scratch); ++ li(scratch, rt); ++ ifmovd(rs, fsrc1); ++ ifmovd(scratch, fsrc2); ++ fcvtld(fsrc1, fsrc1); ++ fcvtld(fsrc2, fsrc2); ++ fdivd(fsrc1, fsrc2, fdest); ++ fcvtdl_z(fdest, fdest); ++ fcvtlw(fdest, fdest); ++ fimovs(fdest, rd); ++ } ++} ++ ++void TurboAssembler::Divwu(Register rd, Register rs, const Operand& rt) { ++ FPURegister fsrc1 = f22; ++ FPURegister fsrc2 = f23; ++ FPURegister fdest = f24; ++ zapnot(rs, 0xf, rs); ++ ++ if (rt.is_reg()) { ++ zapnot(rt.rm(), 0xf, rt.rm()); ++ ifmovd(rs, fsrc1); ++ ifmovd(rt.rm(), fsrc2); ++ fcvtld(fsrc1, fsrc1); ++ fcvtld(fsrc2, fsrc2); ++ fdivd(fsrc1, fsrc2, fdest); ++ fcvtdl_z(fdest, fdest); ++ fcvtlw(fdest, fdest); ++ fimovs(fdest, rd); ++ } else { ++ // li handles the relocation. ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ DCHECK(rs != scratch); ++ li(scratch, rt); ++ zapnot(scratch, 0xf, scratch); ++ ifmovd(rs, fsrc1); ++ ifmovd(scratch, fsrc2); ++ fcvtld(fsrc1, fsrc1); ++ fcvtld(fsrc2, fsrc2); ++ fdivd(fsrc1, fsrc2, fdest); ++ fcvtdl_z(fdest, fdest); ++ fcvtlw(fdest, fdest); ++ fimovs(fdest, rd); ++ } ++} ++ ++void TurboAssembler::Divl(Register rd, Register rs, const Operand& rt) { ++ FPURegister fsrc1 = f22; ++ FPURegister fsrc2 = f23; ++ FPURegister fdest = f24; ++ ++ if (rt.is_reg()) { ++ ifmovd(rs, fsrc1); ++ ifmovd(rt.rm(), fsrc2); ++ fcvtld(fsrc1, fsrc1); ++ fcvtld(fsrc2, fsrc2); ++ fdivd(fsrc1, fsrc2, fdest); ++ fcvtdl_z(fdest, fdest); ++ fimovd(fdest, rd); ++ } else { ++ // li handles the relocation. ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ DCHECK(rs != scratch); ++ li(scratch, rt); ++ ifmovd(rs, fsrc1); ++ ifmovd(scratch, fsrc2); ++ fcvtld(fsrc1, fsrc1); ++ fcvtld(fsrc2, fsrc2); ++ fdivd(fsrc1, fsrc2, fdest); ++ fcvtdl_z(fdest, fdest); ++ fimovd(fdest, rd); ++ } ++} ++ ++void TurboAssembler::Divlu(Register rd, Register rs, const Operand& rt) { ++ FPURegister fsrc1 = f22; ++ FPURegister fsrc2 = f23; ++ FPURegister fdest = f24; ++ ++ if (rt.is_reg()) { ++ ifmovd(rs, fsrc1); ++ ifmovd(rt.rm(), fsrc2); ++ fcvtld(fsrc1, fsrc1); ++ fcvtld(fsrc2, fsrc2); ++ fdivd(fsrc1, fsrc2, fdest); ++ fcvtdl_z(fdest, fdest); ++ fimovd(fdest, rd); ++ } else { ++ // li handles the relocation. ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ DCHECK(rs != scratch); ++ li(scratch, rt); ++ ifmovd(rs, fsrc1); ++ ifmovd(scratch, fsrc2); ++ fcvtld(fsrc1, fsrc1); ++ fcvtld(fsrc2, fsrc2); ++ fdivd(fsrc1, fsrc2, fdest); ++ fcvtdl_z(fdest, fdest); ++ fimovd(fdest, rd); ++ } ++} ++ ++void TurboAssembler::Modw(Register rd, Register rs, const Operand& rt) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ DCHECK(scratch != rs); ++ ++ FPURegister fsrc1 = f22; ++ FPURegister fsrc2 = f23; ++ FPURegister fdest = f24; ++ ++ if (rt.is_reg()) { ++ ifmovd(rs, fsrc1); ++ ifmovd(rt.rm(), fsrc2); ++ fcvtld(fsrc1, fsrc1); ++ fcvtld(fsrc2, fsrc2); ++ fdivd(fsrc1, fsrc2, fdest); ++ fcvtdl_z(fdest, fdest); ++ fcvtlw(fdest, fdest); ++ fimovs(fdest, scratch); ++ mulw(scratch, rt.rm(), scratch); ++ subw(rs, scratch, rd); ++ } else { ++ Register scratch2 = t12; ++ DCHECK(scratch2 != rs); ++ // li handles the relocation. ++ li(scratch, rt); ++ ifmovd(rs, fsrc1); ++ ifmovd(scratch, fsrc2); ++ fcvtld(fsrc1, fsrc1); ++ fcvtld(fsrc2, fsrc2); ++ fdivd(fsrc1, fsrc2, fdest); ++ fcvtdl_z(fdest, fdest); ++ fcvtlw(fdest, fdest); ++ fimovs(fdest, scratch2); ++ mulw(scratch, scratch2, scratch); ++ subw(rs, scratch, rd); ++ } ++} ++ ++void TurboAssembler::Modwu(Register rd, Register rs, const Operand& rt) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ DCHECK(scratch != rs); ++ zapnot(rs, 0xf, rs); ++ ++ FPURegister fsrc1 = f22; ++ FPURegister fsrc2 = f23; ++ FPURegister fdest = f24; ++ ++ if (rt.is_reg()) { ++ zapnot(rt.rm(), 0xf, rt.rm()); ++ ifmovd(rs, fsrc1); ++ ifmovd(rt.rm(), fsrc2); ++ fcvtld(fsrc1, fsrc1); ++ fcvtld(fsrc2, fsrc2); ++ fdivd(fsrc1, fsrc2, fdest); ++ fcvtdl_z(fdest, fdest); ++ fcvtlw(fdest, fdest); ++ fimovs(fdest, scratch); ++ mulw(scratch, rt.rm(), scratch); ++ subw(rs, scratch, rd); ++ } else { ++ Register scratch2 = t12; ++ DCHECK(scratch2 != rs); ++ // li handles the relocation. ++ li(scratch, rt); ++ zapnot(scratch, 0xf, scratch); ++ ifmovd(rs, fsrc1); ++ ifmovd(scratch, fsrc2); ++ fcvtld(fsrc1, fsrc1); ++ fcvtld(fsrc2, fsrc2); ++ fdivd(fsrc1, fsrc2, fdest); ++ fcvtdl_z(fdest, fdest); ++ fcvtlw(fdest, fdest); ++ fimovs(fdest, scratch2); ++ mulw(scratch, scratch2, scratch); ++ subw(rs, scratch, rd); ++ } ++} ++ ++void TurboAssembler::Modl(Register rd, Register rs, const Operand& rt) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ DCHECK(scratch != rs); ++ ++ FPURegister fsrc1 = f22; ++ FPURegister fsrc2 = f23; ++ FPURegister fdest = f24; ++ ++ if (rt.is_reg()) { ++ ifmovd(rs, fsrc1); ++ ifmovd(rt.rm(), fsrc2); ++ fcvtld(fsrc1, fsrc1); ++ fcvtld(fsrc2, fsrc2); ++ fdivd(fsrc1, fsrc2, fdest); ++ fcvtdl_z(fdest, fdest); ++ fimovd(fdest, scratch); ++ mull(scratch, rt.rm(), scratch); ++ subl(rs, scratch, rd); ++ } else { ++ Register scratch2 = t12; ++ DCHECK(scratch2 != rs); ++ // li handles the relocation. ++ li(scratch, rt); ++ ifmovd(rs, fsrc1); ++ ifmovd(scratch, fsrc2); ++ fcvtld(fsrc1, fsrc1); ++ fcvtld(fsrc2, fsrc2); ++ fdivd(fsrc1, fsrc2, fdest); ++ fcvtdl_z(fdest, fdest); ++ fimovd(fdest, scratch2); ++ mull(scratch, scratch2, scratch); ++ subl(rs, scratch, rd); ++ } ++} ++ ++void TurboAssembler::Modlu(Register rd, Register rs, const Operand& rt) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ DCHECK(scratch != rs); ++ ++ FPURegister fsrc1 = f22; ++ FPURegister fsrc2 = f23; ++ FPURegister fdest = f24; ++ ++ if (rt.is_reg()) { ++ ifmovd(rs, fsrc1); ++ ifmovd(rt.rm(), fsrc2); ++ fcvtld(fsrc1, fsrc1); ++ fcvtld(fsrc2, fsrc2); ++ fdivd(fsrc1, fsrc2, fdest); ++ fcvtdl_z(fdest, fdest); ++ fimovd(fdest, scratch); ++ mull(scratch, rt.rm(), scratch); ++ subl(rs, scratch, rd); ++ } else { ++ Register scratch2 = t12; ++ DCHECK(scratch2 != rs); ++ // li handles the relocation. ++ li(scratch, rt); ++ ifmovd(rs, fsrc1); ++ ifmovd(scratch, fsrc2); ++ fcvtld(fsrc1, fsrc1); ++ fcvtld(fsrc2, fsrc2); ++ fdivd(fsrc1, fsrc2, fdest); ++ fcvtdl_z(fdest, fdest); ++ fimovd(fdest, scratch2); ++ mull(scratch, scratch2, scratch); ++ subl(rs, scratch, rd); ++ } ++} ++ ++void TurboAssembler::Dmodu(Register rd, Register rs, const Operand& rt) { ++ // SCOPEMARK_NAME(TurboAssembler::Dmodu, this); ++ UNREACHABLE(); ++} ++ ++void TurboAssembler::Fabs(FPURegister fd, FPURegister fs) { ++ // SCOPEMARK_NAME(TurboAssembler::Fabs, this); ++ fcpys(f31, fs, fd); ++} ++ ++void TurboAssembler::Sllw(Register rd, Register rs, const Operand& rt) { ++ // SCOPEMARK_NAME(TurboAssembler::Sllw, this); ++ if (rt.is_reg()) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ DCHECK(rs != scratch); ++ and_(rt.rm(), 0x1f, scratch); ++ slll(rs, scratch, rd); ++ addw(rd, 0x0, rd); ++ } else { ++ slll(rs, ((int)rt.immediate()) & 0x1f, rd); ++ addw(rd, 0x0, rd); ++ } ++} ++ ++void TurboAssembler::Srlw(Register rd, Register rs, const Operand& rt) { ++ // SCOPEMARK_NAME(TurboAssembler::Srlw, this); ++ if (rt.is_reg()) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ DCHECK(rs != scratch); ++ and_(rt.rm(), 0x1f, scratch); ++ zapnot(rs, 0xf, rd); ++ srll(rd, scratch, rd); ++ addw(rd, 0x0, rd); ++ } else { ++ zapnot(rs, 0xf, rd); ++ srll(rd, ((int)rt.immediate()) & 0x1f, rd); ++ addw(rd, 0x0, rd); ++ } ++} ++ ++void TurboAssembler::Sraw(Register rd, Register rs, const Operand& rt) { ++ // SCOPEMARK_NAME(TurboAssembler::Sraw, this); ++ if (rt.is_reg()) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ DCHECK(rs != scratch); ++ and_(rt.rm(), 0x1f, scratch); ++ addw(rs, 0x0, rd); ++ sral(rd, scratch, rd); ++ } else { ++ addw(rs, 0x0, rd); ++ sral(rd, ((int)rt.immediate()) & 0x1f, rd); ++ } ++} ++ ++void TurboAssembler::And(Register rd, Register rs, const Operand& rt) { ++ // SCOPEMARK_NAME(TurboAssembler::And, this); ++ if (rt.is_reg()) { ++ and_(rs, rt.rm(), rd); ++ } else { ++ if (is_uint8(rt.immediate()) && ++ !MustUseReg(rt.rmode())) { // 20181121 is_uint16 ++ and_(rs, (int)rt.immediate(), rd); ++ } else { ++ // li handles the relocation. ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ DCHECK(rs != scratch); ++ li(scratch, rt); ++ and_(rs, scratch, rd); ++ } ++ } ++} ++ ++void TurboAssembler::Or(Register rd, Register rs, const Operand& rt) { ++ // SCOPEMARK_NAME(TurboAssembler::Or, this); ++ if (rt.is_reg()) { ++ or_(rs, rt.rm(), rd); ++ } else { ++ if (is_uint8(rt.immediate()) && ++ !MustUseReg(rt.rmode())) { // 20181121 is_uint16 ++ or_(rs, (int)rt.immediate(), rd); ++ } else { ++ // li handles the relocation. ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ DCHECK(rs != scratch); ++ li(scratch, rt); ++ or_(rs, scratch, rd); ++ } ++ } ++} ++ ++void TurboAssembler::Xor(Register rd, Register rs, const Operand& rt) { ++ // SCOPEMARK_NAME(TurboAssembler::Xor, this); ++ if (rt.is_reg()) { ++ xor_(rs, rt.rm(), rd); ++ } else { ++ if (is_uint8(rt.immediate()) && !MustUseReg(rt.rmode())) { ++ xor_(rs, (int)rt.immediate(), rd); ++ } else { ++ // li handles the relocation. ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ DCHECK(rs != scratch); ++ li(scratch, rt); ++ xor_(rs, scratch, rd); ++ } ++ } ++} ++ ++void TurboAssembler::Nor(Register rd, Register rs, const Operand& rt) { ++ // SCOPEMARK_NAME(TurboAssembler::Nor, this); ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ if (rt.is_reg()) { ++ bis(rs, rt.rm(), scratch); ++ ornot(zero_reg, scratch, rd); ++ } else { ++ if (is_uint8(rt.immediate()) && !MustUseReg(rt.rmode())) { ++ bis(rs, (int)rt.immediate(), rd); ++ ornot(zero_reg, rd, rd); ++ } else { ++ // li handles the relocation. ++ DCHECK(rs != scratch); ++ li(scratch, rt); ++ bis(rs, scratch, scratch); ++ ornot(zero_reg, scratch, rd); ++ } ++ } ++} ++ ++void TurboAssembler::Neg(Register rs, const Operand& rt) { ++ // SCOPEMARK_NAME(TurboAssembler::Neg, this); ++ subl(zero_reg, rt.rm(), rs); ++} ++ ++void TurboAssembler::Cmplt(Register rd, Register rs, const Operand& rt) { ++ // SCOPEMARK_NAME(TurboAssembler::Cmplt, this); ++ if (rt.is_reg()) { ++ cmplt(rs, rt.rm(), rd); ++ } else { ++ if (is_uint8(rt.immediate()) && !MustUseReg(rt.rmode())) { ++ cmplt(rs, (int)rt.immediate(), rd); ++ } else { ++ // li handles the relocation. ++ UseScratchRegisterScope temps(this); ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ Register scratch = temps.hasAvailable() ? temps.Acquire() : t11; ++ DCHECK(rs != scratch); ++ li(scratch, rt); ++ cmplt(rs, scratch, rd); ++ } ++ } ++} ++ ++void TurboAssembler::Cmpult(Register rd, Register rs, const Operand& rt) { ++ // SCOPEMARK_NAME(TurboAssembler::Cmpult, this); ++ if (rt.is_reg()) { ++ cmpult(rs, rt.rm(), rd); ++ } else { ++ if (is_uint8(rt.immediate()) && !MustUseReg(rt.rmode())) { ++ cmpult(rs, (int)rt.immediate(), rd); ++ } else { ++ // li handles the relocation. ++ UseScratchRegisterScope temps(this); ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ Register scratch = temps.hasAvailable() ? temps.Acquire() : t11; ++ DCHECK(rs != scratch); ++ li(scratch, rt); ++ cmpult(rs, scratch, rd); ++ } ++ } ++} ++ ++void TurboAssembler::Cmple(Register rd, Register rs, const Operand& rt) { ++ // SCOPEMARK_NAME(TurboAssembler::Cmple, this); ++ if (rt.is_reg()) { ++ cmple(rs, rt.rm(), rd); ++ } else { ++ if (is_uint8(rt.immediate()) && !MustUseReg(rt.rmode())) { ++ cmple(rs, (int)rt.immediate(), rd); ++ } else { ++ // li handles the relocation. ++ UseScratchRegisterScope temps(this); ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ Register scratch = temps.hasAvailable() ? temps.Acquire() : t11; ++ DCHECK(rs != scratch); ++ li(scratch, rt); ++ cmple(rs, scratch, rd); ++ } ++ } ++} ++ ++void TurboAssembler::Cmpule(Register rd, Register rs, const Operand& rt) { ++ // SCOPEMARK_NAME(TurboAssembler::Cmpule, this); ++ if (rt.is_reg()) { ++ cmpule(rs, rt.rm(), rd); ++ } else { ++ if (is_uint8(rt.immediate()) && !MustUseReg(rt.rmode())) { ++ cmpule(rs, (int)rt.immediate(), rd); ++ } else { ++ // li handles the relocation. ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.hasAvailable() ? temps.Acquire() : t11; ++ DCHECK(rs != scratch); ++ li(scratch, rt); ++ cmpule(rs, scratch, rd); ++ } ++ } ++} ++ ++void TurboAssembler::Cmpge(Register rd, Register rs, const Operand& rt) { ++ // SCOPEMARK_NAME(TurboAssembler::Cmpge, this); ++ if (rt.is_reg()) { ++ cmple(rt.rm(), rs, rd); ++ } else { ++ // li handles the relocation. ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.hasAvailable() ? temps.Acquire() : t11; ++ DCHECK(rs != scratch); ++ li(scratch, rt); ++ cmple(scratch, rs, rd); ++ } ++} ++ ++void TurboAssembler::Cmpuge(Register rd, Register rs, const Operand& rt) { ++ // SCOPEMARK_NAME(TurboAssembler::Cmpuge, this); ++ if (rt.is_reg()) { ++ cmpule(rt.rm(), rs, rd); ++ } else { ++ // li handles the relocation. ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.hasAvailable() ? temps.Acquire() : t11; ++ DCHECK(rs != scratch); ++ li(scratch, rt); ++ cmpule(scratch, rs, rd); ++ } ++} ++ ++void TurboAssembler::Cmpgt(Register rd, Register rs, const Operand& rt) { ++ // SCOPEMARK_NAME(TurboAssembler::Cmpgt, this); ++ if (rt.is_reg()) { ++ cmplt(rt.rm(), rs, rd); ++ } else { ++ // li handles the relocation. ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.hasAvailable() ? temps.Acquire() : t11; ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ DCHECK(rs != scratch); ++ li(scratch, rt); ++ cmplt(scratch, rs, rd); ++ } ++} ++ ++void TurboAssembler::Cmpugt(Register rd, Register rs, const Operand& rt) { ++ // SCOPEMARK_NAME(TurboAssembler::Cmpugt, this); ++ if (rt.is_reg()) { ++ cmpult(rt.rm(), rs, rd); ++ } else { ++ // li handles the relocation. ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.hasAvailable() ? temps.Acquire() : t11; ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ DCHECK(rs != scratch); ++ li(scratch, rt); ++ cmpult(scratch, rs, rd); ++ } ++} ++ ++void TurboAssembler::Rolw(Register rd, Register rs, const Operand& rt) { ++ // SCOPEMARK_NAME(TurboAssembler::Rolw, this); ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.hasAvailable() ? temps.Acquire() : t11; ++ DCHECK(rs != scratch); ++ DCHECK(rd != scratch); ++ if (rt.is_reg()) { ++ Register scratch1 = (scratch == t11) ? a5 : t11; ++ Register scratch2 = t12; ++ Register scratch3 = gp; // avoid to get at in srlv and sllv ++ and_(rt.rm(), 0x1f, scratch1); ++ ldi(scratch2, 32, zero_reg); ++ subw(scratch2, scratch1, scratch2); ++ { ++ // srlv(scratch1, rs, scratch1); // srlw(rs, scratch1, scratch1); ++ and_(scratch1, 0x1f, scratch3); ++ zapnot(rs, 0xf, scratch1); ++ srll(scratch1, scratch3, scratch1); ++ addw(scratch1, 0, scratch1); ++ } ++ { ++ // sllv(rd, rs, scratch2); // sllw(rs, scratch2, rd); ++ and_(scratch2, 0x1f, scratch3); ++ slll(rs, scratch3, rd); ++ addw(rd, 0, rd); ++ } ++ bis(scratch1, rd, rd); ++ addw(rd, 0, rd); ++ } else { ++ int64_t ror_value = rt.immediate() % 32; ++ if (ror_value < 0) { ++ ror_value += 32; ++ } ++ Srlw(scratch, rs, ror_value); // srlw(rs, ror_value, scratch); ++ Sllw(rd, rs, 32 - ror_value); // sllw(rs, 32-ror_value, rd); ++ bis(scratch, rd, rd); ++ addw(rd, 0, rd); ++ } ++} ++ ++void TurboAssembler::Roll(Register rd, Register rs, const Operand& rt) { ++ // SCOPEMARK_NAME(TurboAssembler::Roll, this); ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.hasAvailable() ? temps.Acquire() : t11; ++ DCHECK(rs != scratch); ++ DCHECK(rd != scratch); ++ if (rt.is_reg()) { ++ Register scratch1 = (scratch == t11) ? a5 : t11; ++ Register scratch2 = t12; ++ srll(rs, rt.rm(), scratch1); ++ ldi(scratch2, 64, zero_reg); ++ subl(scratch2, rt.rm(), scratch2); ++ slll(rs, scratch2, rd); ++ bis(scratch1, rd, rd); ++ } else { ++ int64_t dror_value = rt.immediate() % 64; ++ if (dror_value < 0) dror_value += 64; ++ srll(rs, (int)dror_value, scratch); ++ slll(rs, (int)(64 - dror_value), rd); ++ bis(scratch, rd, rd); ++ } ++} ++ ++ ++void TurboAssembler::Lsaw(Register rd, Register rt, Register rs, uint8_t sa, ++ Register scratch) {//SCOPEMARK_NAME(TurboAssembler::Lsa, this); ++ DCHECK(sa >= 1 && sa <= 31); ++#ifdef SW64 ++ if (sa == 3) { ++ s8addw(rs, rt, rd); //rd = rs * 8 + rt ++ } else if (sa == 2) { ++ s4addw(rs, rt, rd); //rd = rs * 4 + rt ++ } else { ++ Register tmp = rd == rt ? scratch : rd; ++ DCHECK(tmp != rt); ++ Sllw(tmp, rs, sa); //not sw's sllw ++ addw(rt, tmp, rd); ++ } ++#else ++ //if (kArchVariant == kSw64r3 && sa <= 4) { ++ // lsa(rd, rt, rs, sa - 1); ++ //} else { ++ Register tmp = rd == rt ? scratch : rd; ++ DCHECK(tmp != rt); ++ Sllw(tmp, rs, sa); ++ Addw(rd, rt, tmp); ++ //} ++#endif ++} ++ ++void TurboAssembler::Lsal(Register rd, Register rt, Register rs, uint8_t sa, ++ Register scratch) {//SCOPEMARK_NAME(TurboAssembler::Dlsa, this); ++ DCHECK(sa >= 1 && sa <= 63); ++#ifdef SW64 ++ if (sa == 3) { ++ s8addl(rs, rt, rd); //rd = rs * 8 + rt ++ } else if (sa == 2) { ++ s4addl(rs, rt, rd); //rd = rs * 4 + rt ++ } else { ++ Register tmp = rd == rt ? scratch : rd; ++ DCHECK(tmp != rt); ++ slll(rs, sa, tmp); ++ Addl(rd, rt, tmp); ++ } ++#else ++ Register tmp = rd == rt ? scratch : rd; ++ DCHECK(tmp != rt); ++ slll(rs, sa, tmp); ++ Addl(rd, rt, tmp); ++#endif ++// } ++} ++ ++// ------------Pseudo-instructions------------- ++ ++// Change endianness ++void TurboAssembler::ByteSwapSigned(Register dest, Register src, ++ int operand_size) { ++ // SCOPEMARK_NAME(TurboAssembler::ByteSwapSigned, this); ++ DCHECK(operand_size == 2 || operand_size == 4 || operand_size == 8); ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ DCHECK(src != scratch); ++ if (operand_size == 2) { ++ // HGFEDCBA ==> 000000AB ++ zapnot(src, 0x3, dest); ++ srll(dest, 8, scratch); ++ slll(dest, 24, dest); ++ addw(dest, 0, dest); ++ sral(dest, 16, dest); ++ or_(dest, scratch, dest); ++ } else if (operand_size == 4) { ++ // HGFEDCBA ==> 0000ABCD ++ zapnot(src, 0xf, dest); ++ srll(dest, 8, scratch); // 0DCB ++ slll(dest, 24, dest); // A000 ++ bis(dest, scratch, dest); // ADCB ++ srll(scratch, 16, scratch); // 000D ++ xor_(dest, scratch, scratch); ++ and_(scratch, 0xff, scratch); // 000B^D ++ xor_(dest, scratch, dest); // ADCD ++ slll(scratch, 16, scratch); // 0B^D00 ++ xor_(dest, scratch, dest); // 0000ABCD ++ addw(dest, 0, dest); ++ } else { ++ // 87654321 ==> 12345678 ++ srll(src, 8, scratch); // 08765432 ++ slll(src, 56, dest); // 10000000 ++ bis(dest, scratch, dest); // 18765432 ++ // 8 <==> 2 ++ srll(scratch, 48, scratch); // 00000008 ++ xor_(dest, scratch, scratch); ++ and_(scratch, 0xff, scratch); // 00000002^8 ++ xor_(dest, scratch, dest); // 18765438 ++ slll(scratch, 48, scratch); // 02^8000000 ++ xor_(dest, scratch, dest); // 12765438 ++ // 7 <==> 3 ++ srll(dest, 32, scratch); // 00001276 ++ xor_(dest, scratch, scratch); ++ zapnot(scratch, 0x2, scratch); // 0000003^70 ++ xor_(dest, scratch, dest); // 12765478 ++ slll(scratch, 32, scratch); // 03^7000000 ++ xor_(dest, scratch, dest); // 12365478 ++ // 6 <==> 4 ++ srll(dest, 16, scratch); // 00123654 ++ xor_(dest, scratch, scratch); ++ zapnot(scratch, 0x4, scratch); // 000004^600 ++ xor_(dest, scratch, dest); // 12365678 ++ slll(scratch, 16, scratch); // 0004^60000 ++ xor_(dest, scratch, dest); // 12345678 ++ } ++} ++ ++void TurboAssembler::ByteSwapUnsigned(Register dest, Register src, ++ int operand_size) { ++ // SCOPEMARK_NAME(TurboAssembler::ByteSwapUnsigned, this); ++ DCHECK(operand_size == 2 || operand_size == 4); ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ DCHECK(src != scratch); ++ if (operand_size == 2) { ++ zapnot(src, 0x3, dest); ++ srll(dest, 8, scratch); ++ slll(dest, 8, dest); ++ bis(dest, scratch, dest); ++ // slll(dest, 48, dest); ++ zapnot(dest, 0x3, dest); ++ } else { ++ zapnot(src, 0xf, dest); ++ srll(dest, 8, scratch); // 0DCB ++ slll(dest, 24, dest); // A000 ++ bis(dest, scratch, dest); // ADCB ++ srll(scratch, 16, scratch); // 000D ++ xor_(dest, scratch, scratch); ++ and_(scratch, 0xff, scratch); // 000B^D ++ xor_(dest, scratch, dest); // ADCD ++ slll(scratch, 16, scratch); // 0B^D000 ++ xor_(dest, scratch, dest); // 0000ABCD ++ // slll(dest, 32, dest); //ABCD0000 ++ } ++} ++ ++void TurboAssembler::Ldw_u(Register rd, const MemOperand& rs) { ++ // SCOPEMARK_NAME(TurboAssembler::Ldw_u, this); ++ DCHECK(rd != at); ++ DCHECK(rs.rm() != at); ++ Ldw(rd, rs); ++} ++ ++void TurboAssembler::Ldwu_u(Register rd, const MemOperand& rs) { ++ // SCOPEMARK_NAME(TurboAssembler::Ldwu_u, this); DCHECK_EQ(kArchVariant, ++ // kSw64r2); ++ Ldw_u(rd, rs); ++ zapnot(rd, 0xf, rd); ++} ++ ++void TurboAssembler::Stw_u(Register rd, const MemOperand& rs) { ++ // SCOPEMARK_NAME(TurboAssembler::Stw_u, this); ++ DCHECK(rd != at); ++ DCHECK(rs.rm() != at); ++ DCHECK(rd != rs.rm()); ++ Stw(rd, rs); ++} ++ ++void TurboAssembler::Ldh_u(Register rd, const MemOperand& rs) { ++ // SCOPEMARK_NAME(TurboAssembler::Ldh_u, this); ++ DCHECK(rd != at); ++ DCHECK(rs.rm() != at); ++ MemOperand source = rs; ++ // Adjust offset for two accesses and check if offset + 1 fits into int16_t. ++ AdjustBaseAndOffset(&source, OffsetAccessType::TWO_ACCESSES, 1); ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ if (source.rm() == scratch) { ++#if defined(V8_TARGET_LITTLE_ENDIAN) ++ Ldb(rd, MemOperand(source.rm(), source.offset() + 1)); ++ Ldbu(scratch, source); ++#endif ++ } else { ++#if defined(V8_TARGET_LITTLE_ENDIAN) ++ Ldbu(scratch, source); ++ Ldb(rd, MemOperand(source.rm(), source.offset() + 1)); ++#endif ++ } ++ slll(rd, 8, rd); ++ or_(rd, scratch, rd); ++} ++ ++void TurboAssembler::Ldhu_u(Register rd, const MemOperand& rs) { ++ // SCOPEMARK_NAME(TurboAssembler::Ldhu_u, this); ++ DCHECK(rd != at); ++ DCHECK(rs.rm() != at); ++ MemOperand source = rs; ++ // Adjust offset for two accesses and check if offset + 1 fits into int16_t. ++ AdjustBaseAndOffset(&source, OffsetAccessType::TWO_ACCESSES, 1); ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ if (source.rm() == scratch) { ++#if defined(V8_TARGET_LITTLE_ENDIAN) ++ Ldbu(rd, MemOperand(source.rm(), source.offset() + 1)); ++ Ldbu(scratch, source); ++#endif ++ } else { ++#if defined(V8_TARGET_LITTLE_ENDIAN) ++ Ldbu(scratch, source); ++ Ldbu(rd, MemOperand(source.rm(), source.offset() + 1)); ++#endif ++ } ++ slll(rd, 8, rd); ++ or_(rd, scratch, rd); ++} ++ ++void TurboAssembler::Sth_u(Register rd, const MemOperand& rs, ++ Register scratch) { ++ // SCOPEMARK_NAME(TurboAssembler::Sth_u, this); ++ DCHECK(rd != at); ++ DCHECK(rs.rm() != at); ++ DCHECK(rs.rm() != scratch); ++ DCHECK(scratch != at); ++ MemOperand source = rs; ++ // Adjust offset for two accesses and check if offset + 1 fits into int16_t. ++ AdjustBaseAndOffset(&source, OffsetAccessType::TWO_ACCESSES, 1); ++ if (scratch != rd) { ++ mov(scratch, rd); ++ } ++#if defined(V8_TARGET_LITTLE_ENDIAN) ++ Stb(scratch, source); ++ Srlw(scratch, scratch, 8); ++ Stb(scratch, MemOperand(source.rm(), source.offset() + 1)); ++#endif ++} ++ ++void TurboAssembler::Ldl_u(Register rd, const MemOperand& rs) { ++ // SCOPEMARK_NAME(TurboAssembler::Ldl_u, this); ++ DCHECK(rd != at); ++ DCHECK(rs.rm() != at); ++ Ldl(rd, rs); ++} ++ ++void TurboAssembler::Stl_u(Register rd, const MemOperand& rs) { ++ // SCOPEMARK_NAME(TurboAssembler::Stl_u, this); ++ DCHECK(rd != at); ++ DCHECK(rs.rm() != at); ++ Stl(rd, rs); ++} ++ ++void TurboAssembler::Flds_u(FPURegister fd, const MemOperand& rs, ++ Register scratch) { ++ // SCOPEMARK_NAME(TurboAssembler::Flds_u, this); ++ Flds(fd, rs); ++} ++ ++void TurboAssembler::Fsts_u(FPURegister fd, const MemOperand& rs, ++ Register scratch) { ++ // SCOPEMARK_NAME(TurboAssembler::Fsts_u, this); ++ Fsts(fd, rs); ++} ++ ++void TurboAssembler::Fldd_u(FPURegister fd, const MemOperand& rs, ++ Register scratch) { ++ // SCOPEMARK_NAME(TurboAssembler::Fldd_u, this); ++ DCHECK(scratch != at); ++ Fldd(fd, rs); ++} ++ ++void TurboAssembler::Fstd_u(FPURegister fd, const MemOperand& rs, ++ Register scratch) { ++ // SCOPEMARK_NAME(TurboAssembler::Fstd_u, this); ++ DCHECK(scratch != at); ++ Fstd(fd, rs); ++} ++ ++void TurboAssembler::Ldb(Register rd, const MemOperand& rs) { ++ // SCOPEMARK_NAME(TurboAssembler::Ldb, this); ++ ldbu(rd, rs); ++ sextb(rd, rd); ++} ++ ++void TurboAssembler::Ldbu(Register rd, const MemOperand& rs) { ++ // SCOPEMARK_NAME(TurboAssembler::Ldbu, this); ++ ldbu(rd, rs); ++} ++ ++void TurboAssembler::Stb(Register rd, const MemOperand& rs) { ++ // SCOPEMARK_NAME(TurboAssembler::Stb, this); ++ stb(rd, rs); ++} ++ ++void TurboAssembler::Ldh(Register rd, const MemOperand& rs) { ++ // SCOPEMARK_NAME(TurboAssembler::Ldh, this); ++ ldhu(rd, rs); ++ sexth(rd, rd); ++} ++ ++void TurboAssembler::Ldhu(Register rd, const MemOperand& rs) { ++ // SCOPEMARK_NAME(TurboAssembler::Ldhu, this); ++ ldhu(rd, rs); ++} ++ ++void TurboAssembler::Sth(Register rd, const MemOperand& rs) { ++ // SCOPEMARK_NAME(TurboAssembler::Sth, this); ++ sth(rd, rs); ++} ++ ++void TurboAssembler::Ldw(Register rd, const MemOperand& rs) { ++ // SCOPEMARK_NAME(TurboAssembler::Ldw, this); ++ ldw(rd, rs); ++} ++ ++void TurboAssembler::Ldwu(Register rd, const MemOperand& rs) { ++ // SCOPEMARK_NAME(TurboAssembler::Ldwu, this); ++ ldw(rd, rs); ++ zapnot(rd, 0xf, rd); ++} ++ ++void TurboAssembler::Stw(Register rd, const MemOperand& rs) { ++ // SCOPEMARK_NAME(TurboAssembler::Stw, this); ++ stw(rd, rs); ++} ++ ++void TurboAssembler::Ldl(Register rd, const MemOperand& rs) { ++ // SCOPEMARK_NAME(TurboAssembler::Ldl, this); ++ ldl(rd, rs); ++} ++ ++void TurboAssembler::Stl(Register rd, const MemOperand& rs) { ++ // SCOPEMARK_NAME(TurboAssembler::Stl, this); ++ stl(rd, rs); ++} ++ ++void TurboAssembler::Flds(FPURegister fd, const MemOperand& src) { ++ // SCOPEMARK_NAME(TurboAssembler::Flds, this); ++ flds(fd, src); ++} ++ ++void TurboAssembler::Fsts(FPURegister fs, const MemOperand& src) { ++ // SCOPEMARK_NAME(TurboAssembler::Fsts, this); ++ fsts(fs, src); ++} ++ ++void TurboAssembler::Fldd(FPURegister fd, const MemOperand& src) { ++ // SCOPEMARK_NAME(TurboAssembler::Fldd, this); ++ fldd(fd, src); ++} ++ ++void TurboAssembler::Fstd(FPURegister fs, const MemOperand& src) { ++ // SCOPEMARK_NAME(TurboAssembler::Fstd, this); ++ fstd(fs, src); ++} ++ ++void TurboAssembler::li(Register dst, Handle value, LiFlags mode) { ++ // SCOPEMARK_NAME(TurboAssembler::li, this); ++ if (root_array_available_ && options().isolate_independent_code) { ++ IndirectLoadConstant(dst, value); ++ return; ++ } ++ li(dst, Operand(value), mode); ++} ++ ++void TurboAssembler::li(Register dst, ExternalReference value, LiFlags mode) { ++ // SCOPEMARK_NAME(TurboAssembler::li, this); ++ if (root_array_available_ && options().isolate_independent_code) { ++ IndirectLoadExternalReference(dst, value); ++ return; ++ } ++ li(dst, Operand(value), mode); ++} ++ ++void TurboAssembler::li(Register dst, const StringConstantBase* string, ++ LiFlags mode) { ++ li(dst, Operand::EmbeddedStringConstant(string), mode); ++} ++ ++static inline int InstrCountForLiLower32Bit(int64_t value) { ++ int32_t lsb32 = static_cast(value); ++ int16_t lsb_h = (lsb32 - static_cast(lsb32)) >> 16; ++ int16_t lsb_l = static_cast(lsb32); ++ ++ if (is_int16(lsb32)) { ++ return 1; ++ } else { ++ if ((int32_t)(lsb_h) == -32768 && (int32_t)(lsb_l) < 0) { ++ // range from 0x7FFF8000 to 0x7FFFFFFF ++ return lsb_l ? 3 : 2; ++ } else { ++ return lsb_l ? 2 : 1; ++ } ++ } ++} ++ ++void TurboAssembler::LiLower32BitHelper(Register rd, Operand j) { ++ // SCOPEMARK_NAME(TurboAssembler::LiLower32BitHelper, this); ++ int32_t lsb32 = static_cast(j.immediate()); ++ ++ if (is_int16(lsb32)) { ++ ldi(rd, lsb32, zero_reg); ++ } else { ++ int16_t lsb_h = (lsb32 - static_cast(lsb32)) >> 16; ++ int16_t lsb_l = static_cast(lsb32); ++ if ((int32_t)(lsb_h) == -32768 && (int32_t)(lsb_l) < 0) { ++ // range from 0x7FFF8000 to 0x7FFFFFFF ++ ldih(rd, 0x4000, zero_reg); ++ ldih(rd, 0x4000, rd); ++ if (lsb_l) ldi(rd, lsb_l, rd); ++ } else { ++ ldih(rd, lsb_h, zero_reg); ++ if (lsb_l) { ++ ldi(rd, lsb_l, rd); ++ } ++ } ++ } ++} ++ ++int TurboAssembler::InstrCountForLi64Bit(int64_t value) { ++ int32_t lo = static_cast(value); ++ int32_t hi = static_cast((value - lo) >> 32); ++ int16_t lo_h16 = (lo - static_cast(lo)) >> 16; ++ int16_t lo_l16 = static_cast(lo); ++ int16_t hi_l16 = static_cast(hi); ++ ++ if (is_int32(value)) { ++ return InstrCountForLiLower32Bit(value); ++ } else { ++ int count = 1; // slll 32 ++ if (is_int16(hi)) { ++ count += 1; ++ } else { ++ count += hi_l16 ? 2 : 1; ++ } ++ if (lo != 0) { ++ if (((int32_t)lo_h16 == -32768) && ((int32_t)lo_l16 < 0)) { ++ // range from 0x7FFF8000 to 0x7FFFFFFF ++ count += lo_l16 ? 3 : 2; ++ } else { ++ count += lo_l16 ? 2 : 1; ++ } ++ } ++ return count; ++ } ++ ++ UNREACHABLE(); ++ return INT_MAX; ++} ++ ++// All changes to if...else conditions here must be added to ++// InstrCountForLi64Bit as well. ++void TurboAssembler::li_optimized(Register rd, Operand j, LiFlags mode) { ++ // SCOPEMARK_NAME(TurboAssembler::li_optimized, this); ++ DCHECK(!j.is_reg()); ++ DCHECK(!MustUseReg(j.rmode())); ++ DCHECK(mode == OPTIMIZE_SIZE); ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ int64_t value = j.immediate(); ++ int32_t lo = static_cast(value); ++ int32_t hi = static_cast((value - lo) >> 32); ++ int16_t lo_h16 = (lo - static_cast(lo)) >> 16; ++ int16_t lo_l16 = static_cast(lo); ++ int16_t hi_h16 = (hi - static_cast(hi)) >> 16; ++ int16_t hi_l16 = static_cast(hi); ++ // Normal load of an immediate value which does not need Relocation Info. ++ if (is_int32(value)) { ++ LiLower32BitHelper(rd, j); ++ } else { ++ if (is_int16(hi)) { ++ ldi(rd, hi, zero_reg); ++ } else { ++ ldih(rd, hi_h16, zero_reg); ++ if (hi_l16 != 0) ldi(rd, hi_l16, rd); ++ } ++ slll(rd, 32, rd); ++ if (lo != 0) { ++ if (((int32_t)lo_h16 == -32768) && ((int32_t)lo_l16 < 0)) { ++ // range from 0x7FFF8000 to 0x7FFFFFFF ++ ldih(rd, 0x4000, rd); ++ ldih(rd, 0x4000, rd); ++ if (lo_l16 != 0) ldi(rd, lo_l16, rd); ++ } else { ++ ldih(rd, lo_h16, rd); ++ if (lo_l16 != 0) ldi(rd, lo_l16, rd); ++ } ++ } ++ } ++} ++ ++void TurboAssembler::li(Register rd, Operand j, LiFlags mode) { ++ // SCOPEMARK_NAME(TurboAssembler::li, this); ++ DCHECK(!j.is_reg()); ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ if (!MustUseReg(j.rmode()) && mode == OPTIMIZE_SIZE) { ++ int li_count = InstrCountForLi64Bit(j.immediate()); ++ int li_neg_count = InstrCountForLi64Bit(-j.immediate()); ++ int li_not_count = InstrCountForLi64Bit(~j.immediate()); ++ // Loading -MIN_INT64 could cause problems, but loading MIN_INT64 takes ++ // only two instructions so no need to check for this. ++ if (li_neg_count <= li_not_count && li_neg_count < li_count - 1) { ++ DCHECK(j.immediate() != std::numeric_limits::min()); ++ li_optimized(rd, Operand(-j.immediate()), mode); ++ Subl(rd, zero_reg, rd); ++ } else if (li_neg_count > li_not_count && li_not_count < li_count - 1) { ++ DCHECK(j.immediate() != std::numeric_limits::min()); ++ li_optimized(rd, Operand(~j.immediate()), mode); ++ ornot(zero_reg, rd, rd); // nor(rd, rd, rd); ++ } else { ++ li_optimized(rd, j, mode); ++ } ++ } else if (MustUseReg(j.rmode())) { ++ int64_t immediate; ++ if (j.IsHeapObjectRequest()) { ++ RequestHeapObject(j.heap_object_request()); ++ immediate = 0; ++ } else { ++ immediate = j.immediate(); ++ } ++ ++ RecordRelocInfo(j.rmode(), immediate); ++ ++ int32_t lsb32 = static_cast(immediate); ++ int32_t msb32 = static_cast((immediate - lsb32) >> 32); ++ int16_t msb_l = static_cast(msb32); ++ int16_t lsb_h = (lsb32 - static_cast(lsb32)) >> 16; ++ int16_t lsb_l = static_cast(lsb32); ++ ++ // lsb32's range should not be from 0x7FFF8000 to 0x7FFFFFFF. ++ DCHECK(!((lsb32 > 0x7FFF8000) && (lsb32 < 0x7FFFFFFF))); ++ ldi(rd, msb_l, zero_reg); ++ slll(rd, 32, rd); ++ ldih(rd, lsb_h, rd); ++ ldi(rd, lsb_l, rd); ++ ++ } else if (mode == ADDRESS_LOAD) { ++ // We always need the same number of instructions as we may need to patch ++ // this code to load another value which may need all 4 instructions. ++ int32_t lsb32 = static_cast(j.immediate()); ++ int32_t msb32 = static_cast((j.immediate() - lsb32) >> 32); ++ ++ if (lsb32 < 0 && (j.immediate() >> 32) > 0) { ++ return li_optimized(rd, j, OPTIMIZE_SIZE); ++ } ++ ++ int16_t msb_l = static_cast(msb32); ++ int16_t lsb_h = (lsb32 - static_cast(lsb32)) >> 16; ++ int16_t lsb_l = static_cast(lsb32); ++ ++ // lsb32's range should not be from 0x7FFF8000 to 0x7FFFFFFF. ++ DCHECK(!((lsb32 > 0x7FFF8000) && (lsb32 < 0x7FFFFFFF))); ++ ldi(rd, msb_l, zero_reg); ++ slll(rd, 32, rd); ++ ldih(rd, lsb_h, rd); ++ ldi(rd, lsb_l, rd); ++ ++ } else { // mode == CONSTANT_SIZE - always emit the same instruction ++ // sequence. ++ // CONSTANT_SIZE, must 5 instructions. ++ int64_t imm = j.immediate(); ++ int32_t lsb32 = static_cast(imm); ++ int32_t msb32 = static_cast((imm - lsb32) >> 32); ++ int16_t msb_h = (msb32 - static_cast(msb32)) >> 16; ++ int16_t msb_l = static_cast(msb32); ++ int16_t lsb_h = (lsb32 - static_cast(lsb32)) >> 16; ++ int16_t lsb_l = static_cast(lsb32); ++ ++ ldih(rd, msb_h, zero_reg); ++ ldi(rd, msb_l, rd); ++ slll(rd, 32, rd); ++ ldih(rd, lsb_h, rd); ++ ldi(rd, lsb_l, rd); ++ } ++} ++ ++void TurboAssembler::MultiPush(RegList regs) { ++ // SCOPEMARK_NAME(TurboAssembler::MultiPush, this); ++ int16_t num_to_push = regs.Count(); ++ int16_t stack_offset = num_to_push * kPointerSize; ++ ++ Subl(sp, sp, Operand(stack_offset)); ++ if ((regs.bits() & (1 << ra.code())) != 0) { ++ stack_offset -= kPointerSize; ++ stl(ToRegister(ra.code()), MemOperand(sp, stack_offset)); ++ } ++ if ((regs.bits() & (1 << fp.code())) != 0) { ++ stack_offset -= kPointerSize; ++ stl(ToRegister(fp.code()), MemOperand(sp, stack_offset)); ++ } ++ for (int16_t i = kNumRegisters - 1; i >= 0; i--) { ++ if ((regs.bits() & (1 << i)) != 0 && (i != ra.code()) && (i != fp.code())) { ++ stack_offset -= kPointerSize; ++ stl(ToRegister(i), MemOperand(sp, stack_offset)); ++ } ++ } ++} ++ ++void TurboAssembler::MultiPop(RegList regs) { ++ // SCOPEMARK_NAME(TurboAssembler::MultiPop, this); ++ int16_t stack_offset = 0; ++ ++ for (int16_t i = 0; i < kNumRegisters; i++) { ++ if ((regs.bits() & (1 << i)) != 0 && (i != ra.code()) && (i != fp.code())) { ++ ldl(ToRegister(i), MemOperand(sp, stack_offset)); ++ stack_offset += kPointerSize; ++ } ++ } ++ if ((regs.bits() & (1 << fp.code())) != 0) { ++ ldl(ToRegister(fp.code()), MemOperand(sp, stack_offset)); ++ stack_offset += kPointerSize; ++ } ++ if ((regs.bits() & (1 << ra.code())) != 0) { ++ ldl(ToRegister(ra.code()), MemOperand(sp, stack_offset)); ++ stack_offset += kPointerSize; ++ } ++ addl(sp, stack_offset, sp); ++} ++ ++void TurboAssembler::MultiPushFPU(DoubleRegList regs) { ++ int16_t num_to_push = regs.Count(); ++ int16_t stack_offset = num_to_push * kDoubleSize; ++ ++ Subl(sp, sp, Operand(stack_offset)); ++ for (int16_t i = kNumRegisters - 1; i >= 0; i--) { ++ if ((regs.bits() & (1 << i)) != 0) { ++ stack_offset -= kDoubleSize; ++ fstd(FPURegister::from_code(i), MemOperand(sp, stack_offset)); ++ } ++ } ++} ++ ++void TurboAssembler::MultiPopFPU(DoubleRegList regs) { ++ int16_t stack_offset = 0; ++ ++ for (int16_t i = 0; i < kNumRegisters; i++) { ++ if ((regs.bits() & (1 << i)) != 0) { ++ fldd(FPURegister::from_code(i), MemOperand(sp, stack_offset)); ++ stack_offset += kDoubleSize; ++ } ++ } ++ addl(sp, stack_offset, sp); ++} ++ ++void TurboAssembler::MultiPushSWSA(DoubleRegList regs) { ++ int16_t num_to_push = regs.Count(); ++ int16_t stack_offset = num_to_push * kSimd128Size; ++ ++ Subl(sp, sp, Operand(stack_offset)); ++ for (int16_t i = kNumRegisters - 1; i >= 0; i--) { ++ if ((regs.bits() & (1 << i)) != 0) { ++ stack_offset -= kSimd128Size; ++ stl(ToRegister(i), MemOperand(sp, stack_offset)); ++ } ++ } ++} ++ ++void TurboAssembler::MultiPopSWSA(DoubleRegList regs) { ++ int16_t stack_offset = 0; ++ ++ for (int16_t i = 0; i < kNumRegisters; i++) { ++ if ((regs.bits() & (1 << i)) != 0) { ++ ldl(ToRegister(i), MemOperand(sp, stack_offset)); ++ stack_offset += kSimd128Size; ++ } ++ } ++ addl(sp, stack_offset, sp); ++} ++ ++void TurboAssembler::Extw(Register rt, Register rs, uint16_t pos, ++ uint16_t size) { ++ // SCOPEMARK_NAME(TurboAssembler::Ext, this); ++ DCHECK_LT(pos, 32); ++ DCHECK_LT(pos + size, 33); ++ // Ext is word-sign-extend. ++ if (pos == 0) { ++ if (size == 8) { ++ zapnot(rs, 0x1, rt); ++ } else if (size == 16) { ++ zapnot(rs, 0x3, rt); ++ } else if (size == 32) { ++ addw(rs, 0, rt); ++ } else { ++ long bitmask = (0x1L << size) - 1; ++ And(rt, rs, (int)bitmask); ++ // addw(rt, 0, rt); //the only case that rt[31]==0 is pos==0 && ++ // size==32 ++ } ++ } else { ++ long bitmask = (0x1L << size) - 1; ++ srll(rs, pos, rt); ++ And(rt, rt, (int)bitmask); ++ // addw(rt, 0, rt); ++ } ++} ++ ++void TurboAssembler::Extl(Register rt, Register rs, uint16_t pos, ++ uint16_t size) { ++ // SCOPEMARK_NAME(TurboAssembler::Extl, this); ++ DCHECK(pos < 64 && 0 < size && size <= 64 && 0 < pos + size && ++ pos + size <= 64); ++ // dext is zero-extend ++ if (pos != 0) { ++ srll(rs, pos, rt); ++ ++ switch (size) { ++ case 8: ++ zapnot(rt, 0x1, rt); ++ break; ++ case 16: ++ zapnot(rt, 0x3, rt); ++ break; ++ case 32: ++ zapnot(rt, 0xf, rt); ++ break; ++ default: { ++ DCHECK(size < 64); ++ long bitmask = (0x1L << size) - 1; ++ And(rt, rt, (int)bitmask); ++ } ++ } ++ } else { ++ switch (size) { ++ case 8: ++ zapnot(rs, 0x1, rt); ++ break; ++ case 16: ++ zapnot(rs, 0x3, rt); ++ break; ++ case 32: ++ zapnot(rs, 0xf, rt); ++ break; ++ case 64: // the result of 0x1L<<64 is 1. ++ mov(rt, rs); ++ break; ++ default: { ++ long bitmask = (0x1L << size) - 1; ++ And(rt, rs, (int)bitmask); ++ } ++ } ++ } ++ // TODO ++} ++ ++void TurboAssembler::Insw(Register rt, Register rs, uint16_t pos, ++ uint16_t size) { ++ // SCOPEMARK_NAME(TurboAssembler::Ins, this); ++ DCHECK_LT(pos, 32); ++ DCHECK_LE(pos + size, 32); ++ DCHECK_NE(size, 0); ++ DCHECK(rs != t11 && rt != t11); ++ DCHECK(rt != at); ++ ++ long bitsize = (0x1L << size) - 1; ++ li(t11, bitsize); ++ and_(rs, t11, at); ++ slll(at, pos, at); ++ slll(t11, pos, t11); ++ bic(rt, t11, rt); ++ bis(rt, at, rt); ++ addw(rt, 0, rt); ++} ++ ++void TurboAssembler::Insd(Register rt, Register rs, uint16_t pos, ++ uint16_t size) { ++ // SCOPEMARK_NAME(TurboAssembler::Dins, this); ++ DCHECK_LT(pos, 64); ++ DCHECK_LE(pos + size, 64); ++ DCHECK_NE(size, 0); ++ DCHECK(rs != t11 && rt != t11); ++ DCHECK(rt != at); ++ ++ long bitsize = (size == 64) ? -1 : (0x1L << size) - 1; ++ li(t11, bitsize); ++ and_(rs, t11, at); ++ slll(at, pos, at); ++ slll(t11, pos, t11); ++ bic(rt, t11, rt); ++ bis(rt, at, rt); ++} ++ ++void TurboAssembler::ExtractBits(Register dest, Register source, Register pos, ++ int size, bool sign_extend) { ++ // SCOPEMARK_NAME(TurboAssembler::ExtractBits, this); ++ sral(source, pos, dest); ++ if (sign_extend) { ++ switch (size) { ++ case 8: ++ sextb(dest, dest); ++ break; ++ case 16: ++ sexth(dest, dest); ++ break; ++ case 32: ++ addw(dest, 0, dest); ++ break; ++ default: ++ UNREACHABLE(); ++ } ++ } else { ++ switch (size) { ++ case 8: ++ zapnot(dest, 0x1, dest); ++ break; ++ case 16: ++ zapnot(dest, 0x3, dest); ++ break; ++ case 32: ++ zapnot(dest, 0xf, dest); ++ break; ++ default: ++ UNREACHABLE(); ++ } ++ } ++} ++ ++void TurboAssembler::InsertBits(Register dest, Register source, Register pos, ++ int size) { ++ // SCOPEMARK_NAME(TurboAssembler::InsertBits, this); ++ DCHECK(source != t12 && dest != t12); ++ DCHECK(source != t11 && dest != t11); ++ ++ long sizemask = (0x1L << size) - 1; ++ li(t11, sizemask); ++ and_(source, t11, t12); ++ slll(t12, pos, t12); // (source 0..size-1) << pos ++ slll(t11, pos, t11); ++ bic(dest, t11, dest); ++ bis(dest, t12, dest); ++} ++ ++void TurboAssembler::Fneg(FPURegister fd, FPURegister fs) { ++ // SCOPEMARK_NAME(TurboAssembler::Fneg, this); ++ fcpysn(fs, fs, fd); ++} ++ ++void TurboAssembler::Cvt_d_uw(FPURegister fd, FPURegister fs) { ++ // SCOPEMARK_NAME(TurboAssembler::Cvt_d_uw, this); ++ // Move the data from fs to t11. ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ fimovs(fs, t11); ++ Cvt_d_uw(fd, t11); ++} ++ ++void TurboAssembler::Cvt_d_uw(FPURegister fd, Register rs) { ++ // SCOPEMARK_NAME(TurboAssembler::Cvt_d_uw, this); ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ ++ // Convert rs to a FP value in fd. ++ DCHECK(rs != t12); ++ DCHECK(rs != at); ++ ++ // Zero extend int32 in rs. ++ zapnot(rs, 0xf, t12); ++ ifmovd(t12, fd); ++ fcvtld_(fd, fd); ++} ++ ++void TurboAssembler::Cvt_d_ul(FPURegister fd, FPURegister fs) { ++ // SCOPEMARK_NAME(TurboAssembler::Cvt_d_ul, this); ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ // Move the data from fs to t11. ++ fimovd(fs, t11); ++ Cvt_d_ul(fd, t11); ++} ++ ++void TurboAssembler::Cvt_d_ul(FPURegister fd, Register rs) { ++ // SCOPEMARK_NAME(TurboAssembler::Cvt_d_ul, this); ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ // Convert rs to a FP value in fd. ++ ++ DCHECK(rs != t12); ++ DCHECK(rs != at); ++ ++ Label msb_clear, conversion_done; ++ ++ Branch(&msb_clear, ge, rs, Operand(zero_reg)); ++ ++ // Rs >= 2^63 ++ and_(rs, 1, t12); ++ srll(rs, 1, rs); ++ or_(t12, rs, t12); ++ ifmovd(t12, fd); ++ fcvtld_(fd, fd); ++ faddd(fd, fd, fd); // In delay slot. ++ Branch(&conversion_done); ++ ++ bind(&msb_clear); ++ // Rs < 2^63, we can do simple conversion. ++ ifmovd(rs, fd); ++ fcvtld_(fd, fd); ++ ++ bind(&conversion_done); ++} ++ ++void TurboAssembler::Cvt_s_uw(FPURegister fd, FPURegister fs) { ++ // SCOPEMARK_NAME(TurboAssembler::Cvt_s_uw, this); ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ // Move the data from fs to t11. ++ fimovs(fs, t11); ++ Cvt_s_uw(fd, t11); ++} ++ ++void TurboAssembler::Cvt_s_uw(FPURegister fd, Register rs) { ++ // SCOPEMARK_NAME(TurboAssembler::Cvt_s_uw, this); ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ // Convert rs to a FP value in fd. ++ DCHECK(rs != t12); ++ DCHECK(rs != at); ++ ++ // Zero extend int32 in rs. ++ zapnot(rs, 0xf, t12); ++ ifmovd(t12, fd); ++ fcvtls_(fd, fd); ++} ++ ++void TurboAssembler::Cvt_s_ul(FPURegister fd, FPURegister fs) { ++ // SCOPEMARK_NAME(TurboAssembler::Cvt_s_ul, this); ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ // Move the data from fs to t11. ++ fimovd(fs, t11); ++ Cvt_s_ul(fd, t11); ++} ++ ++void TurboAssembler::Cvt_s_ul(FPURegister fd, Register rs) { ++ // SCOPEMARK_NAME(TurboAssembler::Cvt_s_ul, this); ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ // Convert rs to a FP value in fd. ++ ++ DCHECK(rs != t12); ++ DCHECK(rs != at); ++ ++ Label positive, conversion_done; ++ ++ Branch(&positive, ge, rs, Operand(zero_reg)); ++ ++ // Rs >= 2^31. ++ and_(rs, 1, t12); ++ srll(rs, 1, rs); ++ or_(t12, rs, t12); ++ ifmovd(t12, fd); ++ fcvtls_(fd, fd); ++ fadds(fd, fd, fd); // In delay slot. ++ Branch(&conversion_done); ++ ++ bind(&positive); ++ // Rs < 2^31, we can do simple conversion. ++ ifmovd(rs, fd); ++ fcvtls_(fd, fd); ++ ++ bind(&conversion_done); ++} ++ ++void MacroAssembler::Round_l_d(FPURegister fd, FPURegister fs) { ++ // SCOPEMARK_NAME(TurboAssembler::Round_l_d, this); ++ fcvtdl_g(fs, fd); // rounding to nearest ++} ++ ++void MacroAssembler::Floor_l_d(FPURegister fd, FPURegister fs) { ++ // SCOPEMARK_NAME(TurboAssembler::Floor_l_d, this); ++ fcvtdl_n(fs, fd); // rounding down ++} ++ ++void MacroAssembler::Ceil_l_d(FPURegister fd, FPURegister fs) { ++ // SCOPEMARK_NAME(TurboAssembler::Ceil_l_d, this); ++ fcvtdl_p(fs, fd); // rounding up ++} ++ ++void MacroAssembler::Trunc_l_d(FPURegister fd, FPURegister fs) { ++ // SCOPEMARK_NAME(TurboAssembler::Trunc_l_d, this); ++ fcvtdl_z(fs, fd); // rounding toward zero ++} ++ ++void MacroAssembler::Trunc_l_ud( ++ FPURegister fd, FPURegister fs, ++ FPURegister scratch) { // SCOPEMARK_NAME(TurboAssembler::Trunc_l_ud, this); ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ // Load to GPR. ++ fimovd(fs, t11); ++ // Reset sign bit. ++ { ++ UseScratchRegisterScope temps(this); ++ Register scratch1 = temps.Acquire(); ++ li(scratch1, 0x7FFFFFFFFFFFFFFF); ++ and_(t11, scratch1, t11); ++ } ++ ifmovd(t11, fs); ++ ftruncdl(fs, fd); ++} ++ ++void TurboAssembler::Trunc_uw_d(FPURegister fd, FPURegister fs, ++ FPURegister scratch) { ++ // SCOPEMARK_NAME(TurboAssembler::Trunc_uw_d, this); ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ Trunc_uw_d(t11, fs, scratch); ++ ifmovs(t11, fd); ++} ++ ++void TurboAssembler::Trunc_uw_s(FPURegister fd, FPURegister fs, ++ FPURegister scratch) { ++ // SCOPEMARK_NAME(TurboAssembler::Trunc_uw_s, this); ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ Trunc_uw_s(t11, fs, scratch); ++ ifmovs(t11, fd); ++} ++ ++void TurboAssembler::Trunc_ul_d(FPURegister fd, FPURegister fs, ++ FPURegister scratch, Register result) { ++ // SCOPEMARK_NAME(TurboAssembler::Trunc_ul_d, this); ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ Trunc_ul_d(t11, fs, scratch, result); ++ ifmovd(t11, fd); ++} ++ ++void TurboAssembler::Trunc_ul_s(FPURegister fd, FPURegister fs, ++ FPURegister scratch, Register result) { ++ // SCOPEMARK_NAME(TurboAssembler::Trunc_ul_s, this); ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ Trunc_ul_s(t11, fs, scratch, result); ++ ifmovd(t11, fd); ++} ++ ++void MacroAssembler::Trunc_w_d(FPURegister fd, FPURegister fs) { ++ // SCOPEMARK_NAME(TurboAssembler::Trunc_w_d, this); ++ ftruncdw(fs, fd); ++} ++ ++void MacroAssembler::Round_w_d(FPURegister fd, FPURegister fs) { ++ // SCOPEMARK_NAME(TurboAssembler::Round_w_d, this); ++ frounddw(fs, fd); ++} ++ ++void MacroAssembler::Floor_w_d(FPURegister fd, FPURegister fs) { ++ // SCOPEMARK_NAME(TurboAssembler::Floor_w_d, this); ++ ffloordw(fs, fd); ++} ++ ++void MacroAssembler::Ceil_w_d(FPURegister fd, FPURegister fs) { ++ // SCOPEMARK_NAME(TurboAssembler::Ceil_w_d, this); ++ fceildw(fs, fd); ++} ++ ++void TurboAssembler::Trunc_uw_d(Register rd, FPURegister fs, ++ FPURegister scratch) { ++ // SCOPEMARK_NAME(TurboAssembler::Trunc_uw_d, this); ++ DCHECK(fs != scratch); ++ DCHECK(rd != at); ++ ++ { ++ // Load 2^31 into scratch as its float representation. ++ UseScratchRegisterScope temps(this); ++ Register scratch1 = temps.Acquire(); ++ li(scratch1, 0x41E00000); ++ slll(scratch1, 32, scratch1); ++ ifmovd(scratch1, scratch); ++ } ++ // Test if scratch > fd. ++ // If fd < 2^31 we can convert it normally. ++ Label simple_convert; ++ CompareF(OLT, fs, scratch); ++ BranchTrueShortF(&simple_convert); ++ ++ // First we subtract 2^31 from fd, then trunc it to rs ++ // and add 2^31 to rs. ++ fsubd(fs, scratch, kDoubleCompareReg); ++ ftruncdw(kDoubleCompareReg, scratch); ++ fimovs(scratch, rd); ++ Or(rd, rd, 1 << 31); ++ ++ Label done; ++ Branch(&done); ++ // Simple conversion. ++ bind(&simple_convert); ++ fcvtdl_z(fs, kScratchDoubleReg1); ++ fcvtlw(kScratchDoubleReg1, scratch); ++ fimovs(scratch, rd); ++ ++ bind(&done); ++} ++ ++void TurboAssembler::Trunc_uw_s(Register rd, FPURegister fs, ++ FPURegister scratch) { ++ // SCOPEMARK_NAME(TurboAssembler::Trunc_uw_s, this); ++ DCHECK(fs != scratch); ++ DCHECK(rd != at); ++ ++ { ++ // Load 2^31 into scratch as its float representation. ++ UseScratchRegisterScope temps(this); ++ Register scratch1 = temps.Acquire(); ++ li(scratch1, 0x4F000000); ++ ifmovs(scratch1, scratch); ++ } ++ // Test if scratch > fs. ++ // If fs < 2^31 we can convert it normally. ++ Label simple_convert; ++ CompareF(OLT, fs, scratch); ++ BranchTrueShortF(&simple_convert); ++ ++ // First we subtract 2^31 from fs, then trunc it to rd ++ // and add 2^31 to rd. ++ fsubs(fs, scratch, kDoubleCompareReg); // sub_s(scratch, fs, scratch); ++ ftruncsw(kDoubleCompareReg, scratch); // trunc_w_s(scratch, scratch); ++ fimovs(scratch, rd); ++ Or(rd, rd, 1 << 31); ++ ++ Label done; ++ Branch(&done); ++ // Simple conversion. ++ bind(&simple_convert); ++ ftruncsw(fs, scratch); ++ fimovs(scratch, rd); ++ ++ bind(&done); ++} ++ ++void TurboAssembler::Trunc_ul_d(Register rd, FPURegister fs, ++ FPURegister scratch, Register result) { ++ // SCOPEMARK_NAME(TurboAssembler::Trunc_ul_d, this); ++ DCHECK(fs != scratch); ++ DCHECK(result.is_valid() ? !AreAliased(rd, result, at) : !AreAliased(rd, at)); ++ ++ Label simple_convert, done, fail; ++ if (result.is_valid()) { ++ mov(result, zero_reg); ++ Move(scratch, -1.0); ++ // If fd =< -1 or unordered, then the conversion fails. ++ CompareF(OLE, fs, scratch); ++ BranchTrueShortF(&fail); ++ CompareIsNanF(fs, scratch); ++ BranchTrueShortF(&fail); ++ // if fd >= (double)UINT64_MAX, then the conversion fails. ++ ldih(rd, 0x43f0, zero_reg); ++ slll(rd, 32, rd); ++ ifmovd(rd, scratch); ++ CompareF(OLT, fs, scratch); ++ BranchFalseShortF(&fail); ++ } ++ ++ // Load 2^63 into scratch as its double representation. ++ li(at, 0x43E0000000000000); ++ ifmovd(at, scratch); ++ ++ // Test if scratch > fs. ++ // If fs < 2^63 we can convert it normally. ++ CompareF(OLT, fs, scratch); ++ BranchTrueShortF(&simple_convert); ++ ++ // First we subtract 2^63 from fs, then trunc it to rd ++ // and add 2^63 to rd. ++ fsubd(fs, scratch, kDoubleCompareReg); // sub_d(scratch, fs, scratch); ++ fcvtdl_z(kDoubleCompareReg, scratch); // trunc_l_d(scratch, scratch); ++ fimovd(scratch, rd); ++ Or(rd, rd, Operand(1UL << 63)); ++ Branch(&done); ++ ++ // Simple conversion. ++ bind(&simple_convert); ++ ftruncdl(fs, scratch); ++ fimovd(scratch, rd); ++ ++ bind(&done); ++ if (result.is_valid()) { ++ // Conversion is failed if the result is negative. ++ { ++ UseScratchRegisterScope temps(this); ++ Register scratch1 = temps.Acquire(); ++ Addw(scratch1, zero_reg, Operand(-1)); ++ srll(scratch1, 1, scratch1); // Load 2^62. ++ fimovd(scratch, result); ++ xor_(result, scratch1, result); ++ } ++ Cmplt(result, zero_reg, result); ++ } ++ ++ bind(&fail); ++} ++ ++void TurboAssembler::Trunc_ul_s(Register rd, FPURegister fs, ++ FPURegister scratch, Register result) { ++ // SCOPEMARK_NAME(TurboAssembler::Trunc_ul_s, this); ++ DCHECK(fs != scratch); ++ DCHECK(result.is_valid() ? !AreAliased(rd, result, at) : !AreAliased(rd, at)); ++ ++ Label simple_convert, done, fail; ++ FPURegister fscratch2 = kScratchDoubleReg2; ++ if (result.is_valid()) { ++ mov(result, zero_reg); ++ Move(scratch, -1.0f); ++ // If fd =< -1 or unordered, then the conversion fails. ++ CompareF(OLE, fs, scratch); ++ BranchTrueShortF(&fail); ++ CompareIsNanF(fs, scratch); ++ BranchTrueShortF(&fail); ++ // if fd >= (float)UINT64_MAX, then the conversion fails. ++ ldih(rd, 0x5F80, zero_reg); ++ ifmovs(rd, scratch); ++ CompareF(OLT, fs, scratch); ++ BranchFalseShortF(&fail); ++ } ++ ++ { ++ // Load 2^63 into scratch as its float representation. ++ UseScratchRegisterScope temps(this); ++ Register scratch1 = temps.Acquire(); ++ li(scratch1, 0x5F000000); ++ ifmovs(scratch1, scratch); ++ } ++ ++ // Test if scratch > fs. ++ // If fs < 2^63 we can convert it normally. ++ CompareF(OLT, fs, scratch); ++ BranchTrueShortF(&simple_convert); ++ ++ // First we subtract 2^63 from fs, then trunc it to rd ++ // and add 2^63 to rd. ++ fsubs(fs, scratch, fscratch2); ++ ftruncsl(fscratch2, scratch); ++ fimovd(scratch, rd); ++ Or(rd, rd, Operand(1UL << 63)); ++ Branch(&done); ++ ++ // Simple conversion. ++ bind(&simple_convert); ++ ftruncsl(fs, scratch); ++ fimovd(scratch, rd); ++ ++ bind(&done); ++ if (result.is_valid()) { ++ // Conversion is failed if the result is negative or unordered. ++ { ++ UseScratchRegisterScope temps(this); ++ Register scratch1 = temps.Acquire(); ++ Addw(scratch1, zero_reg, Operand(-1)); ++ srll(scratch1, 1, scratch1); // Load 2^62. ++ fimovd(scratch, result); ++ xor_(result, scratch1, result); ++ } ++ Cmplt(result, zero_reg, result); ++ } ++ ++ bind(&fail); ++} ++ ++void TurboAssembler::Fridn_d_d(FPURegister dst, FPURegister src) { ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ Register scratch = t11; ++ Label done; ++ fimovd(src, scratch); ++ srll(scratch, 32, scratch); ++ srll(scratch, HeapNumber::kExponentShift, at); ++ li(gp, (0x1L< ++void TurboAssembler::RoundDouble(FPURegister dst, FPURegister src, ++ FPURoundingMode mode, RoundFunc round) { ++ // SCOPEMARK_NAME(TurboAssembler::RoundDouble, this); ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ Register scratch = t11; ++ FPURegister fscratch = f12; ++ FPURegister fscratch2 = kScratchDoubleReg2; ++ Label done; ++ fimovd(src, scratch); ++ srll(scratch, 32, scratch); ++ srll(scratch, HeapNumber::kExponentShift, at); ++ li(gp, (0x1L << HeapNumber::kExponentBits) - 1); ++ and_(at, gp, at); ++ addw(at, 0, at); ++ fmovd(src, dst); ++ li(gp, HeapNumber::kExponentBias + HeapNumber::kMantissaBits); ++ cmpult(at, gp, at); ++ beq(at, &done); ++ // round(this, dst, src); ++ // fimovd(dst,at); ++ // fcvtld_(dst, dst); ++ // bne(at, &done); ++ // srll(scratch, 31, at); ++ // slll(at, 31 + 32, at); ++ // ifmovd(at, dst); ++ round(this, fscratch, src); ++ fcvtld_(fscratch,fscratch2); ++ fcpys(src,fscratch2,dst); ++ bind(&done); ++} ++ ++void TurboAssembler::Floord(FPURegister dst, FPURegister src) { ++ // SCOPEMARK_NAME(TurboAssembler::Floord, this); ++ RoundDouble(dst, src, mode_floor, ++ [](TurboAssembler* tasm, FPURegister dst, FPURegister src) { ++ tasm->ffloordl(src, dst); ++ }); ++} ++ ++void TurboAssembler::Ceild(FPURegister dst, FPURegister src) { ++ // SCOPEMARK_NAME(TurboAssembler::Ceild, this); ++ RoundDouble(dst, src, mode_ceil, ++ [](TurboAssembler* tasm, FPURegister dst, FPURegister src) { ++ tasm->fceildl(src, dst); ++ }); ++} ++ ++void TurboAssembler::Truncd(FPURegister dst, FPURegister src) { ++ // SCOPEMARK_NAME(TurboAssembler::Truncd, this); ++ RoundDouble(dst, src, mode_trunc, ++ [](TurboAssembler* tasm, FPURegister dst, FPURegister src) { ++ tasm->ftruncdl(src, dst); ++ }); ++} ++ ++void TurboAssembler::Roundd(FPURegister dst, FPURegister src) { ++ // SCOPEMARK_NAME(TurboAssembler::Roundd, this); ++ RoundDouble(dst, src, mode_round, ++ [](TurboAssembler* tasm, FPURegister dst, FPURegister src) { ++ tasm->frounddl(src, dst); ++ }); ++} ++ ++template ++void TurboAssembler::RoundFloat(FPURegister dst, FPURegister src, ++ FPURoundingMode mode, RoundFunc round) { ++ // SCOPEMARK_NAME(TurboAssembler::RoundFloat, this); ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ Register scratch = t11; ++ Register scratch2 = t8; ++ FPURegister fscratch = f12; ++ FPURegister fscratch2 = kScratchDoubleReg2; ++ int32_t kFloat32ExponentBias = 127; ++ int32_t kFloat32MantissaBits = 23; ++ // int32_t kFloat32ExponentBits = 8; ++ Label done; ++ fimovd(src, scratch); ++ srll(scratch, 32, scratch); ++ srll(scratch, 20, scratch2); // sign + exponent, 12 bits ++ and_(scratch2, 0x7F, at); // low 7 exponent bits ++ addw(at, 0, at); ++ srll(scratch2, 3, gp); ++ and_(gp, 0x80, gp); ++ addw(gp, 0, gp); ++ or_(at, gp, at); ++ addw(at, 0, at); ++ fmovs(src, dst); ++ li(gp, kFloat32ExponentBias + kFloat32MantissaBits); ++ cmpult(at, gp, at); ++ beq(at, &done); ++ round(this, fscratch, src); ++ // fimovs(dst, at); ++ fcvtws(fscratch, fscratch2); ++ // bne(at, &done); ++ // srll(scratch, 31, at); ++ // slll(at, 31 + 32, at); ++ // ifmovd(at, dst); ++ fcpys(src,fscratch2,dst); ++ bind(&done); ++} ++ ++void TurboAssembler::Floors(FPURegister dst, FPURegister src) { ++ // SCOPEMARK_NAME(TurboAssembler::Floors, this); ++ RoundFloat(dst, src, mode_floor, ++ [](TurboAssembler* tasm, FPURegister dst, FPURegister src) { ++ tasm->ffloorsw(src, dst); ++ }); ++} ++ ++void TurboAssembler::Ceils(FPURegister dst, FPURegister src) { ++ // SCOPEMARK_NAME(TurboAssembler::Ceils, this); ++ RoundFloat(dst, src, mode_ceil, ++ [](TurboAssembler* tasm, FPURegister dst, FPURegister src) { ++ tasm->fceilsw(src, dst); ++ }); ++} ++ ++void TurboAssembler::Truncs(FPURegister dst, FPURegister src) { ++ // SCOPEMARK_NAME(TurboAssembler::Truncs, this); ++ RoundFloat(dst, src, mode_trunc, ++ [](TurboAssembler* tasm, FPURegister dst, FPURegister src) { ++ tasm->ftruncsw(src, dst); ++ }); ++} ++ ++void TurboAssembler::Rounds(FPURegister dst, FPURegister src) { ++ // SCOPEMARK_NAME(TurboAssembler::Rounds, this); ++ RoundFloat(dst, src, mode_round, ++ [](TurboAssembler* tasm, FPURegister dst, FPURegister src) { ++ tasm->froundsw(src, dst); ++ }); ++} ++// SKTODO ++#if 0 ++ ++void TurboAssembler::StoreLane(SWSASize sz, SWSARegister src, uint8_t laneidx, ++ MemOperand dst) { ++ switch (sz) { ++ case SWSA_B: ++ copy_u_b(kScratchReg, src, laneidx); ++ Sb(kScratchReg, dst); ++ break; ++ case SWSA_H: ++ copy_u_h(kScratchReg, src, laneidx); ++ Sh(kScratchReg, dst); ++ break; ++ case SWSA_W: ++ if (laneidx == 0) { ++ FPURegister src_reg = FPURegister::from_code(src.code()); ++ Swc1(src_reg, dst); ++ } else { ++ copy_u_w(kScratchReg, src, laneidx); ++ Sw(kScratchReg, dst); ++ } ++ break; ++ case SWSA_D: ++ if (laneidx == 0) { ++ FPURegister src_reg = FPURegister::from_code(src.code()); ++ Sdc1(src_reg, dst); ++ } else { ++ copy_s_d(kScratchReg, src, laneidx); ++ Sd(kScratchReg, dst); ++ } ++ break; ++ default: ++ UNREACHABLE(); ++ } ++} ++ ++void TurboAssembler::SWSARoundW(SWSARegister dst, SWSARegister src, ++ FPURoundingMode mode) { ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ Register scratch = t8; ++ Register scratch2 = at; ++ cfcswsa(scratch, SWSACSR); ++ if (mode == kRoundToNearest) { ++ scratch2 = zero_reg; ++ } else { ++ li(scratch2, Operand(mode)); ++ } ++ ctcswsa(SWSACSR, scratch2); ++ frint_w(dst, src); ++ ctcswsa(SWSACSR, scratch); ++} ++ ++void TurboAssembler::SWSARoundD(SWSARegister dst, SWSARegister src, ++ FPURoundingMode mode) { ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ Register scratch = t8; ++ Register scratch2 = at; ++ cfcswsa(scratch, SWSACSR); ++ if (mode == kRoundToNearest) { ++ scratch2 = zero_reg; ++ } else { ++ li(scratch2, Operand(mode)); ++ } ++ ctcswsa(SWSACSR, scratch2); ++ frint_d(dst, src); ++ ctcswsa(SWSACSR, scratch); ++} ++#endif ++ ++void MacroAssembler::Madd_s(FPURegister fd, FPURegister fr, FPURegister fs, ++ FPURegister ft, FPURegister scratch) { ++ // SCOPEMARK_NAME(TurboAssembler::Madd_s, this); ++ DCHECK(fr != scratch && fs != scratch && ft != scratch); ++ fmuls(fs, ft, scratch); ++ fadds(fr, scratch, fd); ++} ++ ++void MacroAssembler::Madd_d(FPURegister fd, FPURegister fr, FPURegister fs, ++ FPURegister ft, FPURegister scratch) { ++ // SCOPEMARK_NAME(TurboAssembler::Madd_d, this); ++ DCHECK(fr != scratch && fs != scratch && ft != scratch); ++ fmuld(fs, ft, scratch); ++ faddd(fr, scratch, fd); ++} ++ ++void MacroAssembler::Msub_s(FPURegister fd, FPURegister fr, FPURegister fs, ++ FPURegister ft, FPURegister scratch) { ++ // SCOPEMARK_NAME(TurboAssembler::Msub_s, this); ++ DCHECK(fr != scratch && fs != scratch && ft != scratch); ++ fmuls(fs, ft, scratch); ++ fsubs(scratch, fr, fd); ++} ++ ++void MacroAssembler::Msub_d(FPURegister fd, FPURegister fr, FPURegister fs, ++ FPURegister ft, FPURegister scratch) { ++ // SCOPEMARK_NAME(TurboAssembler::Msub_d, this); ++ DCHECK(fr != scratch && fs != scratch && ft != scratch); ++ fmuld(fs, ft, scratch); ++ fsubd(scratch, fr, fd); ++} ++ ++void TurboAssembler::CompareF(FPUCondition cc, ++ FPURegister cmp1, FPURegister cmp2) { ++ // SCOPEMARK_NAME(TurboAssembler::CompareF, this); ++ DCHECK(cmp1 != kDoubleCompareReg && cmp2 != kDoubleCompareReg); ++ switch (cc) { ++ case EQ: ++ fcmpeq(cmp1, cmp2, kDoubleCompareReg); ++ break; ++ case OLT: ++ fcmplt(cmp1, cmp2, kDoubleCompareReg); ++ break; ++ case OLE: ++ fcmple(cmp1, cmp2, kDoubleCompareReg); ++ break; ++ case UN: ++ fcmpun(cmp1, cmp2, kDoubleCompareReg); ++ break; ++ default: ++ UNREACHABLE(); ++ }; ++} ++ ++void TurboAssembler::CompareIsNanF(FPURegister cmp1, ++ FPURegister cmp2) { ++ // SCOPEMARK_NAME(TurboAssembler::CompareIsNanF, this); ++ CompareF(UN, cmp1, cmp2); ++} ++ ++void TurboAssembler::BranchTrueShortF(Label* target) { ++ // SCOPEMARK_NAME(TurboAssembler::BranchTrueShortF, this); ++ fbne(kDoubleCompareReg, target); ++} ++ ++void TurboAssembler::BranchFalseShortF(Label* target) { ++ // SCOPEMARK_NAME(TurboAssembler::BranchFalseShortF, this); ++ fbeq(kDoubleCompareReg, target); ++} ++ ++void TurboAssembler::BranchTrueF(Label* target) { ++ // SCOPEMARK_NAME(TurboAssembler::BranchTrueF, this); ++ bool long_branch = ++ target->is_bound() ? !is_near(target) : is_trampoline_emitted(); ++ if (long_branch) { ++ Label skip; ++ BranchFalseShortF(&skip); ++ BranchLong(target); ++ bind(&skip); ++ } else { ++ BranchTrueShortF(target); ++ } ++} ++ ++void TurboAssembler::BranchFalseF(Label* target) { ++ // SCOPEMARK_NAME(TurboAssembler::BranchFalseF, this); ++ bool long_branch = ++ target->is_bound() ? !is_near(target) : is_trampoline_emitted(); ++ if (long_branch) { ++ Label skip; ++ BranchTrueShortF(&skip); ++ BranchLong(target); ++ bind(&skip); ++ } else { ++ BranchFalseShortF(target); ++ } ++} ++ ++void TurboAssembler::FmoveLow(FPURegister dst, Register src_low) { ++ // SCOPEMARK_NAME(TurboAssembler::FmoveLow, this); ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ Register scratch1 = t8; ++ DCHECK(src_low != scratch); ++ fimovd(dst, scratch); ++ zap(scratch, 0xf, scratch); ++ zapnot(src_low, 0xf, scratch1); ++ or_(scratch, scratch1, scratch); ++ ifmovd(scratch, dst); ++} ++ ++void TurboAssembler::Move(FPURegister dst, uint32_t src) { ++ // SCOPEMARK_NAME(TurboAssembler::Move, this); ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ li(scratch, Operand(static_cast(src))); ++ ifmovs(scratch, dst); ++} ++ ++void TurboAssembler::Move(FPURegister dst, uint64_t src) { ++ // SCOPEMARK_NAME(TurboAssembler::Move, this); ++ // Handle special values first. ++ if (src == bit_cast(0.0) /*&& has_double_zero_reg_set_*/) { ++ fmovd(kDoubleRegZero, dst); ++ } else if (src == ++ bit_cast(-0.0) /*&& has_double_zero_reg_set_*/) { ++ Fneg(dst, kDoubleRegZero); ++ } else { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ li(scratch, Operand(bit_cast(src))); ++ ifmovd(scratch, dst); ++ } ++} ++ ++void TurboAssembler::Seleq(Register rd, Register rs, Register rt) { ++ // SCOPEMARK_NAME(TurboAssembler::Seleq, this); ++ seleq(rt, rs, rd, rd); ++} ++ ++void TurboAssembler::Selne(Register rd, Register rs, Register rt) { ++ // SCOPEMARK_NAME(TurboAssembler::Selne, this); ++ selne(rt, rs, rd, rd); ++} ++ ++void TurboAssembler::LoadZeroOnCondition(Register rd, Register rs, ++ const Operand& rt, Condition cond) { ++ // SCOPEMARK_NAME(TurboAssembler::LoadZeroOnCondition, this); ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ switch (cond) { ++ case cc_always: ++ mov(rd, zero_reg); ++ break; ++ case eq: ++ if (rs == zero_reg) { ++ if (rt.is_reg()) { ++ LoadZeroIfConditionZero(rd, rt.rm()); ++ } else { ++ if (rt.immediate() == 0) { ++ mov(rd, zero_reg); ++ } else { ++ nop(); ++ } ++ } ++ } else if (IsZero(rt)) { ++ LoadZeroIfConditionZero(rd, rs); ++ } else { ++ Subl(t12, rs, rt); ++ LoadZeroIfConditionZero(rd, t12); ++ } ++ break; ++ case ne: ++ if (rs == zero_reg) { ++ if (rt.is_reg()) { ++ LoadZeroIfConditionNotZero(rd, rt.rm()); ++ } else { ++ if (rt.immediate() != 0) { ++ mov(rd, zero_reg); ++ } else { ++ nop(); ++ } ++ } ++ } else if (IsZero(rt)) { ++ LoadZeroIfConditionNotZero(rd, rs); ++ } else { ++ Subl(t12, rs, rt); ++ LoadZeroIfConditionNotZero(rd, t12); ++ } ++ break; ++ ++ // Signed comparison. ++ case greater: ++ Cmpgt(t12, rs, rt); ++ LoadZeroIfConditionNotZero(rd, t12); ++ break; ++ case greater_equal: ++ Cmpge(t12, rs, rt); ++ LoadZeroIfConditionNotZero(rd, t12); ++ // rs >= rt ++ break; ++ case less: ++ Cmplt(t12, rs, rt); ++ LoadZeroIfConditionNotZero(rd, t12); ++ // rs < rt ++ break; ++ case less_equal: ++ Cmple(t12, rs, rt); ++ LoadZeroIfConditionNotZero(rd, t12); ++ // rs <= rt ++ break; ++ ++ // Unsigned comparison. ++ case Ugreater: ++ Cmpugt(t12, rs, rt); ++ LoadZeroIfConditionNotZero(rd, t12); ++ // rs > rt ++ break; ++ ++ case Ugreater_equal: ++ Cmpuge(t12, rs, rt); ++ LoadZeroIfConditionNotZero(rd, t12); ++ // rs >= rt ++ break; ++ case Uless: ++ Cmpult(t12, rs, rt); ++ LoadZeroIfConditionNotZero(rd, t12); ++ // rs < rt ++ break; ++ case Uless_equal: ++ Cmpule(t12, rs, rt); ++ LoadZeroIfConditionNotZero(rd, t12); ++ // rs <= rt ++ break; ++ default: ++ UNREACHABLE(); ++ } ++} ++ ++void TurboAssembler::LoadZeroIfConditionNotZero(Register dest, ++ Register condition) { ++ // SCOPEMARK_NAME(TurboAssembler::LoadZeroIfConditionNotZero, this); ++ Selne(dest, zero_reg, condition); ++} ++ ++void TurboAssembler::LoadZeroIfConditionZero(Register dest, ++ Register condition) { ++ // SCOPEMARK_NAME(TurboAssembler::LoadZeroIfConditionZero, this); ++ Seleq(dest, zero_reg, condition); ++} ++ ++void TurboAssembler::LoadZeroIfFPUCondition(Register dest) { ++ // SCOPEMARK_NAME(TurboAssembler::LoadZeroIfFPUCondition, this); ++ fimovd(kDoubleCompareReg, kScratchReg); ++ LoadZeroIfConditionNotZero(dest, kScratchReg); ++} ++ ++void TurboAssembler::LoadZeroIfNotFPUCondition(Register dest) { ++ // SCOPEMARK_NAME(TurboAssembler::LoadZeroIfNotFPUCondition, this); ++ fimovd(kDoubleCompareReg, kScratchReg); ++ LoadZeroIfConditionZero(dest, kScratchReg); ++} ++ ++void TurboAssembler::Ctlzw(Register rd, Register rs) { ++ // SCOPEMARK_NAME(TurboAssembler::Ctlzw, this); ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ addw(rs, 0, scratch); // sign extend ++ ctlz(scratch, rd); ++ subl(rd, 32, rd); // rd=rd-32; ++ sellt(scratch, zero_reg, rd, rd); // (int)rs < 0 => rd = 0; ++} ++ ++void TurboAssembler::Ctlzl(Register rd, Register rs) { ctlz(rs, rd); } ++ ++void TurboAssembler::Cttzw(Register rd, Register rs) { ++ // SCOPEMARK_NAME(TurboAssembler::Cttzw, this); ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ cttz(rs, rd); ++ subl(rd, 32, scratch); ++ selge(scratch, 32, rd, rd); ++} ++ ++void TurboAssembler::Cttzl(Register rd, Register rs) { ++ // SCOPEMARK_NAME(TurboAssembler::Cttzl, this); ++ cttz(rs, rd); ++} ++ ++void TurboAssembler::Ctpopw(Register rd, Register rs) { ++ // SCOPEMARK_NAME(TurboAssembler::Ctpopw, this); ++ ASM_CODE_COMMENT(this); ++ zapnot(rs, 0xf, rd); ++ ctpop(rd, rd); ++} ++ ++void TurboAssembler::Ctpopl(Register rd, Register rs) { ++ // SCOPEMARK_NAME(TurboAssembler::Ctpopl, this); ++ ASM_CODE_COMMENT(this); ++ ctpop(rs, rd); ++} ++ ++void TurboAssembler::TryInlineTruncateDoubleToI(Register result, ++ DoubleRegister double_input, ++ Label* done) { ++ //SCOPEMARK_NAME(TurboAssembler::TryInlineTruncateDoubleToI, this); ++ if (FLAG_use_sw8a) { ++ DoubleRegister single_scratch = kScratchDoubleReg; ++ UseScratchRegisterScope temps(this); ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ Register scratch = temps.Acquire(); ++ Register scratch2 = t12; ++ Register scratch3 = gp; ++ ++ DCHECK(single_scratch != double_input); ++ DCHECK(scratch != result && scratch2 != result); ++ ++ fcvtdl_z(double_input, single_scratch); ++ fimovd(single_scratch, scratch2); ++ li(scratch, 1L << 63); ++ xor_(scratch2, scratch, scratch); ++ roll(scratch,-1,scratch3); ++ addw(scratch2, 0,result); ++ Branch(done, ne, scratch, Operand(scratch3)); ++ }else { ++ DoubleRegister single_scratch = kScratchDoubleReg; ++ UseScratchRegisterScope temps(this); ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ Register scratch = temps.Acquire(); ++ Register scratch2 = t12; ++ ++ DoubleRegister fp_scratch = f20; ++ DoubleRegister fp_scratch2 = f21; ++ DCHECK(fp_scratch != double_input && fp_scratch2 != double_input && single_scratch != double_input); ++ DCHECK(scratch != result && scratch2 != result); ++ ++ DoubleRegList FPUscratch = {fp_scratch, fp_scratch2}; ++ // MultiPushFPU(FPUscratch); // optimize from octane ++ ++ // Clear cumulative exception flags and save the FPCR. ++ // SW64 FPCR, equal to DoubleToIStub::Generate ++ //in order to have same effection, we should do four steps in sw: ++ //1) set fpcr = 0 ++ //2) Rounding: sw(10), round-to-even ++ //3) set trap bit: sw(62~61,51~49), exception controlled by fpcr but not trap ++ //4) set exception mode: sw(00) setfpec0 ++ rfpcr(fp_scratch2); ++ if((strcmp(FLAG_sw64_arch, "sw8a") == 0) ||CpuFeatures::IsSupported(SW_8A)) { ++ li(scratch, sFPCR_8aControlMask | sFPCRRound1Mask); //1), 2), 3) ++ } else { ++ li(scratch, sFPCRControlMask | sFPCRRound1Mask); //1), 2), 3) ++ } ++ ifmovd(scratch, fp_scratch); ++ wfpcr(fp_scratch); ++ setfpec1();//4) ++ ++ // Try a conversion to a signed integer. ++ if(FLAG_use_cmovdw|| FLAG_use_cmovx) { ++ fcvtdl_z(double_input, single_scratch); ++ fcvtlw(single_scratch, fp_scratch); ++ fimovs(fp_scratch, result); ++ // cmovdw_z(double_input, result); ++ }else { ++ fcvtdl_z(double_input, single_scratch); ++ fcvtlw(single_scratch, fp_scratch); ++ fimovs(fp_scratch, result); ++ } ++ // Retrieve and restore the FPCR. ++ rfpcr(fp_scratch); ++ wfpcr(fp_scratch2); ++ setfpec1(); ++ fimovd(fp_scratch, scratch); ++ ++ // MultiPopFPU(FPUscratch); ++ ++ // Check for overflow and NaNs. ++ if((strcmp(FLAG_sw64_arch, "sw8a") == 0) ||CpuFeatures::IsSupported(SW_8A)) { ++ li(scratch2, sFPCROverflowFlagMask | sFPCRUnderflowFlagMask | ++ sFPCRInvalidOpFlagMask | sFPCROverflowIntegerFlagMask | sFPCRInexactFlagBit); ++ } else { ++ li(scratch2, sFPCROverflowFlagMask | sFPCRUnderflowFlagMask | ++ sFPCRInvalidOpFlagMask); ++ } ++ And(scratch, scratch, Operand(scratch2)); ++ ++ // If we had no exceptions we are done. ++ Branch(done, eq, scratch, Operand(zero_reg)); ++ } ++} ++ ++void TurboAssembler::TruncateDoubleToI(Isolate* isolate, Zone* zone, ++ Register result, ++ DoubleRegister double_input, ++ StubCallMode stub_mode) { ++ // SCOPEMARK_NAME(TurboAssembler::TruncateDoubleToI, this); ++ Label done; ++ ++ TryInlineTruncateDoubleToI(result, double_input, &done); ++ ++ // If we fell through then inline version didn't succeed - call stub instead. ++ Push(ra); ++ Subl(sp, sp, Operand(kDoubleSize)); // Put input on stack. ++ Fstd(double_input, MemOperand(sp, 0)); ++ ++#if V8_ENABLE_WEBASSEMBLY ++ if (stub_mode == StubCallMode::kCallWasmRuntimeStub) { ++ Call(wasm::WasmCode::kDoubleToI, RelocInfo::WASM_STUB_CALL); ++#else ++ // For balance. ++ if (false) { ++#endif // V8_ENABLE_WEBASSEMBLY ++ } else { ++ Call(BUILTIN_CODE(isolate, DoubleToI), RelocInfo::CODE_TARGET); ++ } ++ Ldl(result, MemOperand(sp, 0)); ++ ++ Addl(sp, sp, Operand(kDoubleSize)); ++ pop(ra); ++ ++ bind(&done); ++} ++ ++// Emulated condtional branches do not emit a nop in the branch delay slot. ++// ++// BRANCH_ARGS_CHECK checks that conditional jump arguments are correct. ++#define BRANCH_ARGS_CHECK(cond, rs, rt) \ ++ DCHECK((cond == cc_always && rs == zero_reg && rt.rm() == zero_reg) || \ ++ (cond != cc_always && (rs != zero_reg || rt.rm() != zero_reg))) ++ ++void TurboAssembler::Branch(int32_t offset) { ++ // SCOPEMARK_NAME(TurboAssembler::Branch, this); ++ DCHECK(is_int21(offset)); ++ BranchShort(offset); ++} ++ ++void TurboAssembler::Branch(int32_t offset, Condition cond, Register rs, ++ const Operand& rt) { ++ // SCOPEMARK_NAME(TurboAssembler::Branch, this); ++ bool is_near = BranchShortCheck(offset, nullptr, cond, rs, rt); ++ DCHECK(is_near); ++ USE(is_near); ++} ++ ++void TurboAssembler::Branch(Label* L) { ++ // SCOPEMARK_NAME(TurboAssembler::Branch, this); ++ if (L->is_bound()) { ++ if (is_near(L)) { ++ BranchShort(L); ++ } else { ++ BranchLong(L); ++ } ++ } else { ++ if (is_trampoline_emitted()) { ++ BranchLong(L); ++ } else { ++ BranchShort(L); ++ } ++ } ++} ++ ++void TurboAssembler::Branch(Label* L, Condition cond, Register rs, ++ const Operand& rt) { ++ // SCOPEMARK_NAME(TurboAssembler::Branch, this); ++ if (L->is_bound()) { ++ if (!BranchShortCheck(0, L, cond, rs, rt)) { ++ if (cond != cc_always) { ++ Label skip; ++ Condition neg_cond = NegateCondition(cond); ++ BranchShort(&skip, neg_cond, rs, rt); ++ BranchLong(L); ++ bind(&skip); ++ } else { ++ BranchLong(L); ++ } ++ } ++ } else { ++ if (is_trampoline_emitted()) { ++ if (cond != cc_always) { ++ Label skip; ++ Condition neg_cond = NegateCondition(cond); ++ BranchShort(&skip, neg_cond, rs, rt); ++ BranchLong(L); ++ bind(&skip); ++ } else { ++ BranchLong(L); ++ } ++ } else { ++ BranchShort(L, cond, rs, rt); ++ } ++ } ++} ++ ++void TurboAssembler::Branch(Label* L, Condition cond, Register rs, ++ RootIndex index) { ++ // SCOPEMARK_NAME(TurboAssembler::Branch, this); ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ LoadRoot(scratch, index); ++ Branch(L, cond, rs, Operand(scratch)); ++} ++ ++void TurboAssembler::BranchShortHelper(int32_t offset, Label* L) { ++ // SCOPEMARK_NAME(TurboAssembler::BranchShortHelper, this); ++ DCHECK(L == nullptr || offset == 0); ++ offset = GetOffset(offset, L, OffsetSize::kOffset21); ++ br(offset); ++} ++ ++void TurboAssembler::BranchShort(int32_t offset) { ++ // SCOPEMARK_NAME(TurboAssembler::BranchShort, this); ++ DCHECK(is_int21(offset)); ++ BranchShortHelper(offset, nullptr); ++} ++ ++void TurboAssembler::BranchShort(Label* L) { ++ // SCOPEMARK_NAME(TurboAssembler::BranchShort, this); ++ BranchShortHelper(0, L); ++} ++ ++int32_t TurboAssembler::GetOffset(int32_t offset, Label* L, OffsetSize bits) { ++ // SCOPEMARK_NAME(TurboAssembler::GetOffset, this); ++ if (L) { ++ offset = branch_offset_helper(L, bits) >> 2; ++ } else { ++ DCHECK(is_intn(offset, bits)); ++ } ++ return offset; ++} ++ ++Register TurboAssembler::GetRtAsRegisterHelper(const Operand& rt, ++ Register scratch) { ++ // SCOPEMARK_NAME(TurboAssembler::GetRtAsRegisterHelper, this); ++ Register r2 = no_reg; ++ if (rt.is_reg()) { ++ r2 = rt.rm(); ++ } else { ++ r2 = scratch; ++ li(r2, rt); ++ } ++ ++ return r2; ++} ++ ++bool TurboAssembler::CalculateOffset(Label* L, int32_t* offset, ++ OffsetSize bits) { ++ // SCOPEMARK_NAME(TurboAssembler::CalculateOffset, this); ++ if (!is_near(L, bits)) return false; ++ *offset = GetOffset(*offset, L, bits); ++ return true; ++} ++ ++bool TurboAssembler::CalculateOffset(Label* L, int32_t* offset, OffsetSize bits, ++ Register* scratch, const Operand& rt) { ++ // SCOPEMARK_NAME(TurboAssembler::CalculateOffset, this); ++ if (!is_near(L, bits)) return false; ++ *scratch = GetRtAsRegisterHelper(rt, *scratch); ++ *offset = GetOffset(*offset, L, bits); ++ return true; ++} ++ ++bool TurboAssembler::BranchShortHelper(int32_t offset, Label* L, Condition cond, ++ Register rs, const Operand& rt) { ++ // SCOPEMARK_NAME(TurboAssembler::BranchShortHelper, this); ++ DCHECK(L == nullptr || offset == 0); ++ if (!is_near(L, OffsetSize::kOffset21)) return false; ++ ++ UseScratchRegisterScope temps(this); ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ Register scratch = temps.hasAvailable() ? temps.Acquire() : t11; ++ Register scratch2 = gp; ++ int32_t offset32; ++ ++ // Be careful to always use shifted_branch_offset only just before the ++ // branch instruction, as the location will be remember for patching the ++ // target. ++ { ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ switch (cond) { ++ case cc_always: ++ offset32 = GetOffset(offset, L, OffsetSize::kOffset21); ++ br(offset32); // b(offset32); ++ break; ++ case eq: ++ if (IsZero(rt)) { ++ offset32 = GetOffset(offset, L, OffsetSize::kOffset21); ++ beq(rs, offset32); // beq(rs, zero_reg, offset32); ++ } else { ++ // We don't want any other register but scratch clobbered. ++ scratch = GetRtAsRegisterHelper(rt, scratch); ++ DCHECK(rs != scratch2 && scratch != scratch2); ++ cmpeq(rs, scratch, scratch2); ++ offset32 = GetOffset(offset, L, OffsetSize::kOffset21); ++ bne(scratch2, offset32); ++ } ++ break; ++ case ne: ++ if (IsZero(rt)) { ++ offset32 = GetOffset(offset, L, OffsetSize::kOffset21); ++ bne(rs, offset32); // bne(rs, zero_reg, offset32); ++ } else { ++ // We don't want any other register but scratch clobbered. ++ scratch = GetRtAsRegisterHelper(rt, scratch); ++ DCHECK(rs != scratch2 && scratch != scratch2); ++ cmpeq(rs, scratch, scratch2); ++ offset32 = GetOffset(offset, L, OffsetSize::kOffset21); ++ beq(scratch2, offset32); ++ } ++ break; ++ ++ // Signed comparison. ++ case greater: ++ if (IsZero(rt)) { ++ offset32 = GetOffset(offset, L, OffsetSize::kOffset21); ++ bgt(rs, offset32); // bgtz(rs, offset32); ++ } else { ++ Cmplt(scratch, GetRtAsRegisterHelper(rt, scratch), rs); ++ offset32 = GetOffset(offset, L, OffsetSize::kOffset21); ++ bne(scratch, offset32); // bne(scratch, zero_reg, offset32); ++ } ++ break; ++ case greater_equal: ++ if (IsZero(rt)) { ++ offset32 = GetOffset(offset, L, OffsetSize::kOffset21); ++ bge(rs, offset32); // bgez(rs, offset32); ++ } else { ++ Cmplt(scratch, rs, rt); ++ offset32 = GetOffset(offset, L, OffsetSize::kOffset21); ++ beq(scratch, offset32); // beq(scratch, zero_reg, offset32); ++ } ++ break; ++ case less: ++ if (IsZero(rt)) { ++ offset32 = GetOffset(offset, L, OffsetSize::kOffset21); ++ blt(rs, offset32); // bltz(rs, offset32); ++ } else { ++ Cmplt(scratch, rs, rt); ++ offset32 = GetOffset(offset, L, OffsetSize::kOffset21); ++ bne(scratch, offset32); // bne(scratch, zero_reg, offset32); ++ } ++ break; ++ case less_equal: ++ if (IsZero(rt)) { ++ offset32 = GetOffset(offset, L, OffsetSize::kOffset21); ++ ble(rs, offset32); // blez(rs, offset32); ++ } else { ++ Cmplt(scratch, GetRtAsRegisterHelper(rt, scratch), rs); ++ offset32 = GetOffset(offset, L, OffsetSize::kOffset21); ++ beq(scratch, offset32); // beq(scratch, zero_reg, offset32); ++ } ++ break; ++ ++ // Unsigned comparison. ++ case Ugreater: ++ if (IsZero(rt)) { ++ offset32 = GetOffset(offset, L, OffsetSize::kOffset21); ++ bne(rs, offset32); // bne(rs, zero_reg, offset32); ++ } else { ++ Cmpult(scratch, GetRtAsRegisterHelper(rt, scratch), rs); ++ offset32 = GetOffset(offset, L, OffsetSize::kOffset21); ++ bne(scratch, offset32); // bne(scratch, zero_reg, offset32); ++ } ++ break; ++ case Ugreater_equal: ++ if (IsZero(rt)) { ++ offset32 = GetOffset(offset, L, OffsetSize::kOffset21); ++ br(offset32); // b(offset32); ++ } else { ++ Cmpult(scratch, rs, rt); ++ offset32 = GetOffset(offset, L, OffsetSize::kOffset21); ++ beq(scratch, offset32); // beq(scratch, zero_reg, offset32); ++ } ++ break; ++ case Uless: ++ if (IsZero(rt)) { ++ return true; // No code needs to be emitted. ++ } else { ++ Cmpult(scratch, rs, rt); ++ offset32 = GetOffset(offset, L, OffsetSize::kOffset21); ++ bne(scratch, offset32); // bne(scratch, zero_reg, offset32); ++ } ++ break; ++ case Uless_equal: ++ if (IsZero(rt)) { ++ offset32 = GetOffset(offset, L, OffsetSize::kOffset21); ++ beq(rs, offset32); // beq(rs, zero_reg, offset32); ++ } else { ++ Cmpult(scratch, GetRtAsRegisterHelper(rt, scratch), rs); ++ offset32 = GetOffset(offset, L, OffsetSize::kOffset21); ++ beq(scratch, offset32); // beq(scratch, zero_reg, offset32); ++ } ++ break; ++ default: ++ UNREACHABLE(); ++ } ++ } ++ ++ return true; ++} ++ ++bool TurboAssembler::BranchShortCheck(int32_t offset, Label* L, Condition cond, ++ Register rs, const Operand& rt) { ++ // SCOPEMARK_NAME(TurboAssembler::BranchShortCheck, this); ++ BRANCH_ARGS_CHECK(cond, rs, rt); ++ ++ if (!L) { ++ DCHECK(is_int21(offset)); ++ return BranchShortHelper(offset, nullptr, cond, rs, rt); ++ } else { ++ DCHECK_EQ(offset, 0); ++ return BranchShortHelper(0, L, cond, rs, rt); ++ } ++} ++ ++void TurboAssembler::BranchShort(int32_t offset, Condition cond, Register rs, ++ const Operand& rt) { ++ // SCOPEMARK_NAME(TurboAssembler::BranchShort, this); ++ BranchShortCheck(offset, nullptr, cond, rs, rt); ++} ++ ++void TurboAssembler::BranchShort(Label* L, Condition cond, Register rs, ++ const Operand& rt) { ++ // SCOPEMARK_NAME(TurboAssembler::BranchShort, this); ++ BranchShortCheck(0, L, cond, rs, rt); ++} ++ ++void TurboAssembler::BranchAndLink(int32_t offset) { ++ // SCOPEMARK_NAME(TurboAssembler::BranchAndLink, this); ++ BranchAndLinkShort(offset); ++} ++ ++void TurboAssembler::BranchAndLink(int32_t offset, Condition cond, Register rs, ++ const Operand& rt) { ++ // SCOPEMARK_NAME(TurboAssembler::BranchAndLink, this); ++ bool is_near = BranchAndLinkShortCheck(offset, nullptr, cond, rs, rt); ++ DCHECK(is_near); ++ USE(is_near); ++} ++ ++void TurboAssembler::BranchAndLink(Label* L) { ++ // SCOPEMARK_NAME(TurboAssembler::BranchAndLink, this); ++ if (L->is_bound()) { ++ if (is_near(L)) { ++ BranchAndLinkShort(L); ++ } else { ++ BranchAndLinkLong(L); ++ } ++ } else { ++ if (is_trampoline_emitted()) { ++ BranchAndLinkLong(L); ++ } else { ++ BranchAndLinkShort(L); ++ } ++ } ++} ++ ++void TurboAssembler::BranchAndLink(Label* L, Condition cond, Register rs, ++ const Operand& rt) { ++ // SCOPEMARK_NAME(TurboAssembler::BranchAndLink, this); ++ if (L->is_bound()) { ++ if (!BranchAndLinkShortCheck(0, L, cond, rs, rt)) { ++ Label skip; ++ Condition neg_cond = NegateCondition(cond); ++ BranchShort(&skip, neg_cond, rs, rt); ++ BranchAndLinkLong(L); ++ bind(&skip); ++ } ++ } else { ++ if (is_trampoline_emitted()) { ++ Label skip; ++ Condition neg_cond = NegateCondition(cond); ++ BranchShort(&skip, neg_cond, rs, rt); ++ BranchAndLinkLong(L); ++ bind(&skip); ++ } else { ++ BranchAndLinkShortCheck(0, L, cond, rs, rt); ++ } ++ } ++} ++ ++void TurboAssembler::BranchAndLinkShortHelper(int32_t offset, Label* L) { ++ // SCOPEMARK_NAME(TurboAssembler::BranchAndLinkShortHelper, this); ++ DCHECK(L == nullptr || offset == 0); ++ offset = GetOffset(offset, L, OffsetSize::kOffset21); ++ br(ra, offset); // bal(offset); ++} ++ ++void TurboAssembler::BranchAndLinkShort(int32_t offset) { ++ // SCOPEMARK_NAME(TurboAssembler::BranchAndLinkShort, this); ++ DCHECK(is_int21(offset)); ++ BranchAndLinkShortHelper(offset, nullptr); ++} ++ ++void TurboAssembler::BranchAndLinkShort(Label* L) { ++ // SCOPEMARK_NAME(TurboAssembler::BranchAndLinkShort, this); ++ BranchAndLinkShortHelper(0, L); ++} ++ ++bool TurboAssembler::BranchAndLinkShortHelper(int32_t offset, Label* L, ++ Condition cond, Register rs, ++ const Operand& rt) { ++ // SCOPEMARK_NAME(TurboAssembler::BranchAndLinkShortHelper, this); ++ DCHECK(L == nullptr || offset == 0); ++ if (!is_near(L, OffsetSize::kOffset21)) return false; ++ ++ Register scratch = t11; ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ ++ switch (cond) { ++ case cc_always: ++ offset = GetOffset(offset, L, OffsetSize::kOffset21); ++ br(ra, offset); // bal(offset); ++ break; ++ case eq: ++ cmpeq(rs, GetRtAsRegisterHelper(rt, scratch), scratch); ++ beq(scratch, 1); // bne(rs, GetRtAsRegisterHelper(rt, scratch), 2); ++ offset = GetOffset(offset, L, OffsetSize::kOffset21); ++ br(ra, offset); // bal(offset); ++ break; ++ case ne: ++ cmpeq(rs, GetRtAsRegisterHelper(rt, scratch), scratch); ++ bne(scratch, 1); // beq(rs, GetRtAsRegisterHelper(rt, scratch), 2); ++ offset = GetOffset(offset, L, OffsetSize::kOffset21); ++ br(ra, offset); // bal(offset); ++ break; ++ ++ // Signed comparison. ++ case greater: ++ Cmplt(scratch, GetRtAsRegisterHelper(rt, scratch), rs); ++ Addw(scratch, scratch, Operand(-1)); ++ blt(scratch, 1); ++ bsr(L); // bgezal(scratch, offset); ++ break; ++ case greater_equal: ++ Cmplt(scratch, rs, rt); ++ Addw(scratch, scratch, Operand(-1)); ++ bge(scratch, 1); ++ bsr(L); // bltzal(scratch, offset); ++ break; ++ case less: ++ Cmplt(scratch, rs, rt); ++ Addw(scratch, scratch, Operand(-1)); ++ offset = GetOffset(offset, L, OffsetSize::kOffset21); ++ blt(scratch, 1); ++ br(ra, offset); // bgezal(scratch, offset); ++ break; ++ case less_equal: ++ Cmplt(scratch, GetRtAsRegisterHelper(rt, scratch), rs); ++ Addw(scratch, scratch, Operand(-1)); ++ bge(scratch, 1); ++ bsr(L); // bltzal(scratch, offset); ++ break; ++ ++ // Unsigned comparison. ++ case Ugreater: ++ Cmpult(scratch, GetRtAsRegisterHelper(rt, scratch), rs); ++ Addw(scratch, scratch, Operand(-1)); ++ blt(scratch, 1); ++ bsr(L); // bgezal(scratch, offset); ++ break; ++ case Ugreater_equal: ++ Cmpult(scratch, rs, rt); ++ Addw(scratch, scratch, Operand(-1)); ++ bge(scratch, 1); ++ bsr(L); // bltzal(scratch, offset); ++ break; ++ case Uless: ++ Cmpult(scratch, rs, rt); ++ Addw(scratch, scratch, Operand(-1)); ++ blt(scratch, 1); ++ bsr(L); // bgezal(scratch, offset); ++ break; ++ case Uless_equal: ++ Cmpult(scratch, GetRtAsRegisterHelper(rt, scratch), rs); ++ Addw(scratch, scratch, Operand(-1)); ++ bge(scratch, 1); ++ bsr(L); // bltzal(scratch, offset); ++ break; ++ ++ default: ++ UNREACHABLE(); ++ } ++ ++ return true; ++} ++ ++bool TurboAssembler::BranchAndLinkShortCheck(int32_t offset, Label* L, ++ Condition cond, Register rs, ++ const Operand& rt) { ++ // SCOPEMARK_NAME(TurboAssembler::BranchAndLinkShortCheck, this); ++ BRANCH_ARGS_CHECK(cond, rs, rt); ++ ++ if (!L) { ++ DCHECK(is_int21(offset)); ++ return BranchAndLinkShortHelper(offset, nullptr, cond, rs, rt); ++ } else { ++ DCHECK_EQ(offset, 0); ++ return BranchAndLinkShortHelper(0, L, cond, rs, rt); ++ } ++} ++ ++void TurboAssembler::LoadFromConstantsTable(Register destination, ++ int constant_index) { ++ ASM_CODE_COMMENT(this); ++ DCHECK(RootsTable::IsImmortalImmovable(RootIndex::kBuiltinsConstantsTable)); ++ LoadRoot(destination, RootIndex::kBuiltinsConstantsTable); ++ Ldl(destination, ++ FieldMemOperand(destination, ++ FixedArray::kHeaderSize + constant_index * kPointerSize)); ++} ++ ++void TurboAssembler::LoadRootRelative(Register destination, int32_t offset) { ++ // SCOPEMARK_NAME(TurboAssembler::LoadRootRelative, this); ++ Ldl(destination, MemOperand(kRootRegister, offset)); ++} ++ ++void TurboAssembler::LoadRootRegisterOffset(Register destination, ++ intptr_t offset) { ++ // SCOPEMARK_NAME(TurboAssembler::LoadRootRegisterOffset, this); ++ if (offset == 0) { ++ Move(destination, kRootRegister); ++ } else { ++ Addl(destination, kRootRegister, Operand(offset)); ++ } ++} ++ ++void TurboAssembler::Jump(Register target, Condition cond, Register rs, ++ const Operand& rt) { ++ // SCOPEMARK_NAME(TurboAssembler::Jump, this); ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ if (cond == cc_always) { ++ Assembler::jmp(zero_reg, target, 0); ++ } else { ++ BRANCH_ARGS_CHECK(cond, rs, rt); ++ Branch(1, NegateCondition(cond), rs, rt); ++ Assembler::jmp(zero_reg, target, 0); ++ } ++} ++ ++void TurboAssembler::Jump(intptr_t target, RelocInfo::Mode rmode, ++ Condition cond, Register rs, const Operand& rt) { ++ // SCOPEMARK_NAME(TurboAssembler::Jump, this); ++ Label skip; ++ { ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ li(t12, Operand(target, rmode)); ++ if (cond != cc_always) { ++ Branch(&skip, NegateCondition(cond), rs, rt); ++ } ++ Jump(t12, al, zero_reg, Operand(zero_reg)); ++ bind(&skip); ++ } ++} ++ ++void TurboAssembler::Jump(Address target, RelocInfo::Mode rmode, Condition cond, ++ Register rs, const Operand& rt) { ++ // SCOPEMARK_NAME(TurboAssembler::Jump, this); ++ DCHECK(!RelocInfo::IsCodeTarget(rmode)); ++ Jump(static_cast(target), rmode, cond, rs, rt); ++} ++ ++void TurboAssembler::Jump(Handle code, RelocInfo::Mode rmode, ++ Condition cond, Register rs, const Operand& rt) { ++ // SCOPEMARK_NAME(TurboAssembler::Jump, this); ++ DCHECK(RelocInfo::IsCodeTarget(rmode)); ++ ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ if (root_array_available_ && options().isolate_independent_code) { ++ IndirectLoadConstant(t12, code); ++ Addl(t12, t12, Operand(Code::kHeaderSize - kHeapObjectTag)); ++ Jump(t12, cond, rs, rt); ++ return; ++ } else if (options().inline_offheap_trampolines) { ++ Builtin builtin = Builtin::kNoBuiltinId; ++ if (isolate()->builtins()->IsBuiltinHandle(code, &builtin) && ++ Builtins::IsIsolateIndependent(builtin)) { ++ // Inline the trampoline. ++ RecordCommentForOffHeapTrampoline(builtin); ++ li(t12, Operand(BuiltinEntry(builtin), RelocInfo::OFF_HEAP_TARGET)); ++ Jump(t12, cond, rs, rt); ++ RecordComment("]"); ++ return; ++ } ++ } ++ ++ Jump(static_cast(code.address()), rmode, cond, rs, rt); ++} ++ ++void TurboAssembler::Jump(const ExternalReference& reference) { ++ li(t12, reference); ++ Jump(t12); ++} ++ ++// Note: To call gcc-compiled C code on sw64, you must call through t12. ++void TurboAssembler::Call(Register target, Condition cond, Register rs, ++ const Operand& rt) { ++ // SCOPEMARK_NAME(TurboAssembler::Call, this); ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ if (cond == cc_always) { ++ call(ra, target, 0); ++ } else { ++ BRANCH_ARGS_CHECK(cond, rs, rt); ++ Branch(1, NegateCondition(cond), rs, rt); ++ call(ra, target, 0); ++ } ++ set_pc_for_safepoint(); ++} ++ ++void MacroAssembler::JumpIfIsInRange(Register value, unsigned lower_limit, ++ unsigned higher_limit, ++ Label* on_in_range) { ++ ASM_CODE_COMMENT(this); ++ if (lower_limit != 0) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ Subl(scratch, value, Operand(lower_limit)); ++ Branch(on_in_range, ls, scratch, Operand(higher_limit - lower_limit)); ++ } else { ++ Branch(on_in_range, ls, value, Operand(higher_limit - lower_limit)); ++ } ++} ++ ++void TurboAssembler::Call(Address target, RelocInfo::Mode rmode, Condition cond, ++ Register rs, const Operand& rt) { ++ // SCOPEMARK_NAME(TurboAssembler::Call, this); ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ li(t12, Operand(static_cast(target), rmode), ADDRESS_LOAD); ++ Call(t12, cond, rs, rt); ++} ++ ++void TurboAssembler::Call(Handle code, RelocInfo::Mode rmode, ++ Condition cond, Register rs, const Operand& rt) { ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ ++ if (root_array_available_ && options().isolate_independent_code) { ++ IndirectLoadConstant(t12, code); ++ Addl(t12, t12, Operand(Code::kHeaderSize - kHeapObjectTag)); ++ Call(t12, cond, rs, rt); ++ return; ++ } else if (options().inline_offheap_trampolines) { ++ Builtin builtin = Builtin::kNoBuiltinId; ++ if (isolate()->builtins()->IsBuiltinHandle(code, &builtin) && ++ Builtins::IsIsolateIndependent(builtin)) { ++ // Inline the trampoline. ++ RecordCommentForOffHeapTrampoline(builtin); ++ li(t12, Operand(BuiltinEntry(builtin), RelocInfo::OFF_HEAP_TARGET)); ++ Call(t12, cond, rs, rt); ++ RecordComment("]"); ++ return; ++ } ++ } ++ ++ DCHECK(RelocInfo::IsCodeTarget(rmode)); ++ DCHECK(code->IsExecutable()); ++ Call(code.address(), rmode, cond, rs, rt); ++} ++ ++void TurboAssembler::LoadEntryFromBuiltinIndex(Register builtin_index) { ++ ASM_CODE_COMMENT(this); ++ STATIC_ASSERT(kSystemPointerSize == 8); ++ STATIC_ASSERT(kSmiTagSize == 1); ++ STATIC_ASSERT(kSmiTag == 0); ++ ++ // The builtin_index register contains the builtin index as a Smi. ++ SmiUntag(builtin_index, builtin_index); ++ Lsal(builtin_index, kRootRegister, builtin_index, kSystemPointerSizeLog2); ++ Ldl(builtin_index, ++ MemOperand(builtin_index, IsolateData::builtin_entry_table_offset())); ++} ++void TurboAssembler::LoadEntryFromBuiltin(Builtin builtin, ++ Register destination) { ++ Ldl(destination, EntryFromBuiltinAsOperand(builtin)); ++} ++MemOperand TurboAssembler::EntryFromBuiltinAsOperand(Builtin builtin) { ++ DCHECK(root_array_available()); ++ return MemOperand(kRootRegister, ++ IsolateData::BuiltinEntrySlotOffset(builtin)); ++} ++ ++void TurboAssembler::CallBuiltinByIndex(Register builtin_index) { ++ ASM_CODE_COMMENT(this); ++ LoadEntryFromBuiltinIndex(builtin_index); ++ Call(builtin_index); ++} ++void TurboAssembler::CallBuiltin(Builtin builtin) { ++ RecordCommentForOffHeapTrampoline(builtin); ++ Call(BuiltinEntry(builtin), RelocInfo::OFF_HEAP_TARGET); ++ RecordComment("]"); ++} ++ ++void TurboAssembler::PatchAndJump(Address target) { ++ ASM_CODE_COMMENT(this); ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ mov(scratch, ra); ++ Align(8); ++ br(ra, 0); // jump to ld ++ ldl(t12, MemOperand(ra, kInstrSize * 3)); // ra == pc_ ++ mov(ra, scratch); ++ Assembler::jmp(zero_reg, t12, 0); ++ DCHECK_EQ(reinterpret_cast(pc_) % 8, 0); ++ *reinterpret_cast(pc_) = target; // pc_ should be align. ++ pc_ += sizeof(uint64_t); ++} ++ ++void TurboAssembler::StoreReturnAddressAndCall(Register target) { ++ ASM_CODE_COMMENT(this); ++ // This generates the final instruction sequence for calls to C functions ++ // once an exit frame has been constructed. ++ // ++ // Note that this assumes the caller code (i.e. the Code object currently ++ // being generated) is immovable or that the callee function cannot trigger ++ // GC, since the callee function will return to it. ++ ++ // Compute the return address in lr to return to after the jump below. The ++ // pc is already at '+ 8' from the current instruction; but return is after ++ // three instructions, so add another 4 to pc to get the return address. ++ ++ Assembler::BlockTrampolinePoolScope block_trampoline_pool(this); ++ static constexpr int kNumInstructionsToJump = 2; ++ Label find_ra; ++ ++ Move(t12, target); ++ br(kScratchReg, 0); ++ addl(kScratchReg, (kNumInstructionsToJump + 1) * kInstrSize, ra); ++ bind(&find_ra); ++ stl(ra, MemOperand(sp, 0)); ++ call(ra, t12, 0); ++ DCHECK_EQ(kNumInstructionsToJump, InstructionsGeneratedSince(&find_ra)); ++ setfpec1(); ++} ++ ++void TurboAssembler::Ret(Condition cond, Register rs, const Operand& rt) { ++ // SCOPEMARK_NAME(TurboAssembler::Ret, this); ++ Jump(ra, cond, rs, rt); ++} ++ ++void TurboAssembler::BranchLong(Label* L) { ++ // SCOPEMARK_NAME(TurboAssembler::BranchLong, this); ++ // use jmp only when Label is bound and target is beyond br's limit ++ if (L->is_bound() && !is_near(L)) { ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ int64_t imm64; ++ imm64 = branch_long_offset(L); ++ DCHECK(is_int32(imm64)); ++ br(t8, 0); ++ li(t12, Operand(imm64)); ++ addl(t8, t12, t12); ++ Assembler::jmp(zero_reg, t12, 0); ++ } else { ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ { ++ BlockGrowBufferScope block_buf_growth(this); ++ // Buffer growth (and relocation) must be blocked for internal ++ // references until associated instructions are emitted and available to ++ // be patched. ++ RecordRelocInfo(RelocInfo::INTERNAL_REFERENCE_ENCODED); ++ br(L); // ZHJ j(L); ++ } ++ } ++} ++ ++// SKTODO ++void TurboAssembler::BranchLong(int32_t offset) { ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ br(t8, 0); ++ li(t12, Operand(offset)); ++ addl(t8, t12, t12); ++ Assembler::jmp(zero_reg, t12, 0); ++} ++ ++void TurboAssembler::BranchAndLinkLong(Label* L) { ++ // SCOPEMARK_NAME(TurboAssembler::BranchAndLinkLong, this); ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ { ++ BlockGrowBufferScope block_buf_growth(this); ++ // Buffer growth (and relocation) must be blocked for internal references ++ // until associated instructions are emitted and available to be patched. ++ RecordRelocInfo(RelocInfo::INTERNAL_REFERENCE_ENCODED); ++ bsr(L); // jal(L); ++ } ++} ++ ++void TurboAssembler::DropArguments(Register count, ArgumentsCountType type, ++ ArgumentsCountMode mode, Register scratch) { ++ switch (type) { ++ case kCountIsInteger: { ++ Lsal(sp, sp, count, kPointerSizeLog2); ++ break; ++ } ++ case kCountIsSmi: { ++ STATIC_ASSERT(kSmiTagSize == 1 && kSmiTag == 0); ++ DCHECK_NE(scratch, no_reg); ++ SmiScale(scratch, count, kPointerSizeLog2); ++ Addl(sp, sp, scratch); ++ break; ++ } ++ case kCountIsBytes: { ++ Addl(sp, sp, count); ++ break; ++ } ++ } ++ if (mode == kCountExcludesReceiver) { ++ Addl(sp, sp, kSystemPointerSize); ++ } ++} ++ ++void TurboAssembler::DropArgumentsAndPushNewReceiver(Register argc, ++ Register receiver, ++ ArgumentsCountType type, ++ ArgumentsCountMode mode, ++ Register scratch) { ++ DCHECK(!AreAliased(argc, receiver)); ++ if (mode == kCountExcludesReceiver) { ++ // Drop arguments without receiver and override old receiver. ++ DropArguments(argc, type, kCountIncludesReceiver, scratch); ++ Stl(receiver, MemOperand(sp)); ++ } else { ++ DropArguments(argc, type, mode, scratch); ++ Push(receiver); ++ } ++} ++ ++void TurboAssembler::DropAndRet(int drop) { ++ // SCOPEMARK_NAME(TurboAssembler::DropAndRet, this); ++ if (is_uint8(drop * kPointerSize)) { ++ addl(sp, drop * kPointerSize, sp); ++ } else { ++ DCHECK(is_int16(drop * kPointerSize)); ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ ldi(scratch, drop * kPointerSize, zero_reg); ++ addl(sp, scratch, sp); ++ } ++ Ret(); ++} ++ ++void TurboAssembler::DropAndRet(int drop, Condition cond, Register r1, ++ const Operand& r2) { ++ // SCOPEMARK_NAME(TurboAssembler::DropAndRet, this); ++ // Both Drop and Ret need to be conditional. ++ Label skip; ++ if (cond != cc_always) { ++ Branch(&skip, NegateCondition(cond), r1, r2); ++ } ++ ++ Drop(drop); ++ Ret(); ++ ++ if (cond != cc_always) { ++ bind(&skip); ++ } ++} ++ ++void TurboAssembler::Drop(int count, Condition cond, Register reg, ++ const Operand& op) { ++ // SCOPEMARK_NAME(TurboAssembler::Drop, this); ++ if (count <= 0) { ++ return; ++ } ++ ++ Label skip; ++ ++ if (cond != al) { ++ Branch(&skip, NegateCondition(cond), reg, op); ++ } ++ ++ Addl(sp, sp, Operand(count * kPointerSize)); ++ ++ if (cond != al) { ++ bind(&skip); ++ } ++} ++ ++void MacroAssembler::Swap(Register reg1, Register reg2, Register scratch) { ++ // SCOPEMARK_NAME(TurboAssembler::Swap, this); ++ if (scratch == no_reg) { ++ Xor(reg1, reg1, Operand(reg2)); ++ Xor(reg2, reg2, Operand(reg1)); ++ Xor(reg1, reg1, Operand(reg2)); ++ } else { ++ mov(scratch, reg1); ++ mov(reg1, reg2); ++ mov(reg2, scratch); ++ } ++} ++ ++void TurboAssembler::Call(Label* target) { ++ // SCOPEMARK_NAME(TurboAssembler::Call, this); ++ BranchAndLink(target); ++} ++ ++void TurboAssembler::LoadAddress(Register dst, Label* target) { ++ uint64_t address = jump_address(target); ++ li(dst, address); ++} ++ ++void TurboAssembler::Push( ++ Smi smi) { // SCOPEMARK_NAME(TurboAssembler::Push, this); ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ li(scratch, Operand(smi)); ++ Push(scratch); ++} ++ ++void TurboAssembler::Push(Handle handle) { ++ // SCOPEMARK_NAME(TurboAssembler::Push, this); ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ li(scratch, Operand(handle)); ++ Push(scratch); ++} ++ ++// SKTODO ++void TurboAssembler::PushArray(Register array, Register size, Register scratch, ++ Register scratch2, PushArrayOrder order) { ++ DCHECK(!AreAliased(array, size, scratch, scratch2)); ++ Label loop, entry; ++ if (order == PushArrayOrder::kReverse) { ++ mov(scratch, zero_reg); ++ jmp(&entry); ++ bind(&loop); ++ Lsal(scratch2, array, scratch, kPointerSizeLog2); ++ Ldl(scratch2, MemOperand(scratch2)); ++ Push(scratch2); ++ Addl(scratch, scratch, Operand(1)); ++ bind(&entry); ++ Branch(&loop, less, scratch, Operand(size)); ++ } else { ++ mov(scratch, size); ++ jmp(&entry); ++ bind(&loop); ++ Lsal(scratch2, array, scratch, kPointerSizeLog2); ++ Ldl(scratch2, MemOperand(scratch2)); ++ Push(scratch2); ++ bind(&entry); ++ Addl(scratch, scratch, Operand(-1)); ++ Branch(&loop, greater_equal, scratch, Operand(zero_reg)); ++ } ++} ++ ++// --------------------------------------------------------------------------- ++// Exception handling. ++ ++void MacroAssembler::PushStackHandler() { ++ // SCOPEMARK_NAME(TurboAssembler::PushStackHandler, this); ++ // Adjust this code if not the case. ++ STATIC_ASSERT(StackHandlerConstants::kSize == 2 * kPointerSize); ++ STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0 * kPointerSize); ++ ++ Push(Smi::zero()); // Padding. ++ ++ // Link the current handler as the next handler. ++ li(t9, ++ ExternalReference::Create(IsolateAddressId::kHandlerAddress, isolate())); ++ Ldl(t1, MemOperand(t9)); ++ Push(t1); ++ ++ // Set this new handler as the current one. ++ Stl(sp, MemOperand(t9)); ++} ++ ++void MacroAssembler::PopStackHandler() { ++ // SCOPEMARK_NAME(TurboAssembler::PopStackHandler, this); ++ STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0); ++ pop(a1); ++ Addl(sp, sp, ++ Operand( ++ static_cast(StackHandlerConstants::kSize - kPointerSize))); ++ // UseScratchRegisterScope temps(this); ++ // Register scratch = temps.Acquire(); ++ // li(scratch, ++ li(t9, ++ ExternalReference::Create(IsolateAddressId::kHandlerAddress, isolate())); ++ Stl(a1, MemOperand(t9)); ++} ++ ++void TurboAssembler::FPUCanonicalizeNaN(const DoubleRegister dst, ++ const DoubleRegister src) { ++ // SCOPEMARK_NAME(TurboAssembler::FPUCanonicalizeNaN, this); ++ fsubd(src, kDoubleRegZero, dst); ++} ++ ++void TurboAssembler::MovFromFloatResult(const DoubleRegister dst) { ++ // SCOPEMARK_NAME(TurboAssembler::MovFromFloatResult, this); ++ Move(dst, f0); // Reg f0 is o32 ABI FP return value. ++} ++ ++void TurboAssembler::MovFromFloatParameter(const DoubleRegister dst) { ++ // SCOPEMARK_NAME(TurboAssembler::MovFromFloatParameter, this); ++ Move(dst, f16); // Reg f16 is sw64 ABI FP first argument value. ++} ++ ++void TurboAssembler::MovToFloatParameter(DoubleRegister src) { ++ // SCOPEMARK_NAME(TurboAssembler::MovToFloatParameter, this); ++ Move(f16, src); ++} ++ ++void TurboAssembler::MovToFloatResult(DoubleRegister src) { ++ // SCOPEMARK_NAME(TurboAssembler::MovToFloatResult, this); ++ Move(f0, src); ++} ++ ++void TurboAssembler::MovToFloatParameters(DoubleRegister fsrc0, ++ DoubleRegister fsrc1) { ++ // SCOPEMARK_NAME(TurboAssembler::MovToFloatParameters, this); ++ const DoubleRegister fparg2 = f17; ++ if (fsrc1 == f16) { ++ DCHECK(fsrc0 != fparg2); ++ Move(fparg2, fsrc1); ++ Move(f16, fsrc0); ++ } else { ++ Move(f16, fsrc0); ++ Move(fparg2, fsrc1); ++ } ++} ++ ++void TurboAssembler::MovFromGeneralResult(const Register dst) { ++ // SCOPEMARK_NAME(TurboAssembler::MovFromGeneralResult, this); ++ Move(dst, v0); ++} ++ ++void TurboAssembler::MovFromGeneralParameter(const Register dst) { ++ // SCOPEMARK_NAME(TurboAssembler::MovFromGeneralParameter, this); ++ Move(dst, a0); ++} ++ ++void TurboAssembler::MovToGeneralParameter(Register src) { ++ // SCOPEMARK_NAME(TurboAssembler::MovToGeneralParameter, this); ++ Move(a0, src); ++} ++ ++void TurboAssembler::MovToGeneralResult(Register src) { ++ // SCOPEMARK_NAME(TurboAssembler::MovToGeneralResult, this); ++ Move(a0, src); ++} ++ ++void TurboAssembler::MovToGeneralParameters(Register src0, Register src1) { ++ // SCOPEMARK_NAME(TurboAssembler::MovToGeneralParameters, this); ++ if (src1 == a0) { ++ if (src0 != a1) { ++ Move(a1, src1); // src1 = a0 ++ Move(a0, src0); ++ } else { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ Move(scratch, src1); // at = src1(a0) ++ Move(a0, src0); // a0 = src0(a1) ++ Move(a1, scratch); // a1 = at ++ } ++ } else { ++ Move(a0, src0); ++ Move(a1, src1); ++ } ++} ++ ++// ----------------------------------------------------------------------------- ++// JavaScript invokes. ++ ++void MacroAssembler::LoadStackLimit(Register destination, StackLimitKind kind) { ++ ASM_CODE_COMMENT(this); ++ DCHECK(root_array_available()); ++ Isolate* isolate = this->isolate(); ++ ExternalReference limit = ++ kind == StackLimitKind::kRealStackLimit ++ ? ExternalReference::address_of_real_jslimit(isolate) ++ : ExternalReference::address_of_jslimit(isolate); ++ DCHECK(TurboAssembler::IsAddressableThroughRootRegister(isolate, limit)); ++ ++ intptr_t offset = ++ TurboAssembler::RootRegisterOffsetForExternalReference(isolate, limit); ++ CHECK(is_int32(offset)); ++ Ldl(destination, MemOperand(kRootRegister, static_cast(offset))); ++} ++ ++void MacroAssembler::StackOverflowCheck(Register num_args, Register scratch1, ++ Register scratch2, ++ Label* stack_overflow) { ++ ASM_CODE_COMMENT(this); ++ // Check the stack for overflow. We are not trying to catch ++ // interruptions (e.g. debug break and preemption) here, so the "real stack ++ // limit" is checked. ++ ++ LoadStackLimit(scratch1, StackLimitKind::kRealStackLimit); ++ // Make scratch1 the space we have left. The stack might already be overflowed ++ // here which will cause scratch1 to become negative. ++ subl(sp, scratch1, scratch1); ++ // Check if the arguments will overflow the stack. ++ slll(num_args, kPointerSizeLog2, scratch2); ++ // Signed comparison. ++ Branch(stack_overflow, le, scratch1, Operand(scratch2)); ++} ++ ++void MacroAssembler::InvokePrologue(Register expected_parameter_count, ++ Register actual_parameter_count, ++ Label* done, InvokeType type) { ++ ASM_CODE_COMMENT(this); ++ Label regular_invoke; ++ ++ // a0: actual arguments count ++ // a1: function (passed through to callee) ++ // a2: expected arguments count ++ ++ DCHECK_EQ(actual_parameter_count, a0); ++ DCHECK_EQ(expected_parameter_count, a2); ++ ++ // If the expected parameter count is equal to the adaptor sentinel, no need ++ // to push undefined value as arguments. ++ if (kDontAdaptArgumentsSentinel != 0) { ++ Branch(®ular_invoke, eq, expected_parameter_count, ++ Operand(kDontAdaptArgumentsSentinel)); ++ } ++ ++ // If overapplication or if the actual argument count is equal to the ++ // formal parameter count, no need to push extra undefined values. ++ Subl(expected_parameter_count, expected_parameter_count, ++ actual_parameter_count); ++ Branch(®ular_invoke, le, expected_parameter_count, Operand(zero_reg)); ++ ++ Label stack_overflow; ++ StackOverflowCheck(expected_parameter_count, t0, t1, &stack_overflow); ++ // Underapplication. Move the arguments already in the stack, including the ++ // receiver and the return address. ++ { ++ Label copy; ++ Register src = t9, dest = t10; ++ mov(src, sp); ++ slll(expected_parameter_count, kSystemPointerSizeLog2, t0); ++ Subl(sp, sp, Operand(t0)); ++ // Update stack pointer. ++ mov(dest, sp); ++ mov(t0, actual_parameter_count); ++ bind(©); ++ Ldl(t1, MemOperand(src, 0)); ++ Stl(t1, MemOperand(dest, 0)); ++ Subl(t0, t0, Operand(1)); ++ Addl(src, src, Operand(kSystemPointerSize)); ++ Addl(dest, dest, Operand(kSystemPointerSize)); ++ Branch(©, gt, t0, Operand(zero_reg)); ++ } ++ ++ // Fill remaining expected arguments with undefined values. ++ LoadRoot(t0, RootIndex::kUndefinedValue); ++ { ++ Label loop; ++ bind(&loop); ++ Stl(t0, MemOperand(t10, 0)); ++ Subl(expected_parameter_count, expected_parameter_count, Operand(1)); ++ Addl(t10, t10, Operand(kSystemPointerSize)); ++ Branch(&loop, gt, expected_parameter_count, Operand(zero_reg)); ++ } ++ br(®ular_invoke); ++ // nop(); ++ ++ bind(&stack_overflow); ++ { ++ FrameScope frame( ++ this, has_frame() ? StackFrame::NO_FRAME_TYPE : StackFrame::INTERNAL); ++ CallRuntime(Runtime::kThrowStackOverflow); ++ sys_call(0x80); ++ } ++ ++ bind(®ular_invoke); ++} ++ ++void MacroAssembler::CheckDebugHook(Register fun, Register new_target, ++ Register expected_parameter_count, ++ Register actual_parameter_count) { ++ Label skip_hook; ++ ++ li(t0, ExternalReference::debug_hook_on_function_call_address(isolate())); ++ Ldb(t0, MemOperand(t0)); ++ Branch(&skip_hook, eq, t0, Operand(zero_reg)); ++ ++ { ++ // Load receiver to pass it later to DebugOnFunctionCall hook. ++ LoadReceiver(t0, actual_parameter_count); ++ ++ FrameScope frame( ++ this, has_frame() ? StackFrame::NO_FRAME_TYPE : StackFrame::INTERNAL); ++ SmiTag(expected_parameter_count); ++ Push(expected_parameter_count); ++ ++ SmiTag(actual_parameter_count); ++ Push(actual_parameter_count); ++ ++ if (new_target.is_valid()) { ++ Push(new_target); ++ } ++ Push(fun); ++ Push(fun); ++ Push(t0); ++ CallRuntime(Runtime::kDebugOnFunctionCall); ++ Pop(fun); ++ if (new_target.is_valid()) { ++ Pop(new_target); ++ } ++ ++ Pop(actual_parameter_count); ++ SmiUntag(actual_parameter_count); ++ ++ Pop(expected_parameter_count); ++ SmiUntag(expected_parameter_count); ++ } ++ bind(&skip_hook); ++} ++ ++void MacroAssembler::InvokeFunctionCode(Register function, Register new_target, ++ Register expected_parameter_count, ++ Register actual_parameter_count, ++ InvokeType type) { ++ // You can't call a function without a valid frame. ++ DCHECK_IMPLIES(type == InvokeType::kCall, has_frame()); ++ DCHECK_EQ(function, a1); ++ DCHECK_IMPLIES(new_target.is_valid(), new_target == a3); ++ ++ // On function call, call into the debugger if necessary. ++ CheckDebugHook(function, new_target, expected_parameter_count, ++ actual_parameter_count); ++ ++ // Clear the new.target register if not given. ++ if (!new_target.is_valid()) { ++ LoadRoot(a3, RootIndex::kUndefinedValue); ++ } ++ ++ Label done; ++ InvokePrologue(expected_parameter_count, actual_parameter_count, &done, type); ++ // We call indirectly through the code field in the function to ++ // allow recompilation to take effect without changing any of the ++ // call sites. ++ Register code = kJavaScriptCallCodeStartRegister; ++ Ldl(code, FieldMemOperand(function, JSFunction::kCodeOffset)); ++ switch (type) { ++ case InvokeType::kCall: ++ Addl(code, code, Operand(Code::kHeaderSize - kHeapObjectTag)); ++ Call(code); ++ break; ++ case InvokeType::kJump: ++ Addl(code, code, Operand(Code::kHeaderSize - kHeapObjectTag)); ++ Jump(code); ++ break; ++ } ++ ++ // Continue here if InvokePrologue does handle the invocation due to ++ // mismatched parameter counts. ++ bind(&done); ++} ++ ++void MacroAssembler::InvokeFunctionWithNewTarget( ++ Register function, Register new_target, Register actual_parameter_count, ++ InvokeType type) { ++ ASM_CODE_COMMENT(this); ++ // You can't call a function without a valid frame. ++ DCHECK_IMPLIES(type == InvokeType::kCall, has_frame()); ++ ++ // Contract with called JS functions requires that function is passed in a1. ++ DCHECK_EQ(function, a1); ++ Register expected_parameter_count = a2; ++ Register temp_reg = t0; ++ Ldl(temp_reg, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset)); ++ Ldl(cp, FieldMemOperand(a1, JSFunction::kContextOffset)); ++ // The argument count is stored as int32_t on 64-bit platforms. ++ // TODO(plind): Smi on 32-bit platforms. ++ Ldhu(expected_parameter_count, ++ FieldMemOperand(temp_reg, ++ SharedFunctionInfo::kFormalParameterCountOffset)); ++ ++ InvokeFunctionCode(a1, new_target, expected_parameter_count, ++ actual_parameter_count, type); ++} ++ ++void MacroAssembler::InvokeFunction(Register function, ++ Register expected_parameter_count, ++ Register actual_parameter_count, ++ InvokeType type) { ++ ASM_CODE_COMMENT(this); ++ // You can't call a function without a valid frame. ++ DCHECK_IMPLIES(type == InvokeType::kCall, has_frame()); ++ ++ // Contract with called JS functions requires that function is passed in a1. ++ DCHECK_EQ(function, a1); ++ ++ // Get the function and setup the context. ++ Ldl(cp, FieldMemOperand(a1, JSFunction::kContextOffset)); ++ ++ InvokeFunctionCode(a1, no_reg, expected_parameter_count, ++ actual_parameter_count, type); ++} ++ ++// --------------------------------------------------------------------------- ++// Support functions. ++ ++void MacroAssembler::GetObjectType( ++ Register object, Register map, ++ Register type_reg) { // SCOPEMARK_NAME(TurboAssembler::GetObjectType, ++ // this); ++ LoadMap(map, object); ++ Ldhu(type_reg, FieldMemOperand(map, Map::kInstanceTypeOffset)); ++} ++ ++void MacroAssembler::GetInstanceTypeRange(Register map, Register type_reg, ++ InstanceType lower_limit, ++ Register range) { ++ Ldhu(type_reg, FieldMemOperand(map, Map::kInstanceTypeOffset)); ++ Subl(range, type_reg, Operand(lower_limit)); ++} ++ ++// ----------------------------------------------------------------------------- ++// Runtime calls. ++ ++void TurboAssembler::DaddOverflow(Register dst, Register left, ++ const Operand& right, Register overflow) { ++ ASM_CODE_COMMENT(this); ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ Register right_reg = no_reg; ++ Register scratch = t11; ++ if (!right.is_reg()) { ++ li(at, Operand(right)); ++ right_reg = at; ++ } else { ++ right_reg = right.rm(); ++ } ++ ++ DCHECK(left != scratch && right_reg != scratch && dst != scratch && ++ overflow != scratch); ++ DCHECK(overflow != left && overflow != right_reg); ++ ++ if (dst == left || dst == right_reg) { ++ addl(left, right_reg, scratch); ++ xor_(scratch, left, overflow); ++ xor_(scratch, right_reg, at); ++ and_(overflow, at, overflow); ++ mov(dst, scratch); ++ } else { ++ addl(left, right_reg, dst); ++ xor_(dst, left, overflow); ++ xor_(dst, right_reg, at); ++ and_(overflow, at, overflow); ++ } ++} ++ ++void TurboAssembler::SublOverflow(Register dst, Register left, ++ const Operand& right, Register overflow) { ++ ASM_CODE_COMMENT(this); ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ Register right_reg = no_reg; ++ Register scratch = t11; ++ if (!right.is_reg()) { ++ li(at, Operand(right)); ++ right_reg = at; ++ } else { ++ right_reg = right.rm(); ++ } ++ ++ DCHECK(left != scratch && right_reg != scratch && dst != scratch && ++ overflow != scratch); ++ DCHECK(overflow != left && overflow != right_reg); ++ ++ if (dst == left || dst == right_reg) { ++ subl(left, right_reg, scratch); ++ xor_(left, scratch, overflow); ++ xor_(left, right_reg, at); ++ and_(overflow, at, overflow); ++ mov(dst, scratch); ++ } else { ++ subl(left, right_reg, dst); ++ xor_(left, dst, overflow); ++ xor_(left, right_reg, at); ++ and_(overflow, at, overflow); ++ } ++} ++ ++void TurboAssembler::MulwOverflow(Register dst, Register left, ++ const Operand& right, Register overflow) { ++ ASM_CODE_COMMENT(this); ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ Register right_reg = no_reg; ++ Register scratch = t11; ++ if (!right.is_reg()) { ++ li(at, Operand(right)); ++ right_reg = at; ++ } else { ++ right_reg = right.rm(); ++ } ++ ++ DCHECK(left != scratch && right_reg != scratch && dst != scratch && ++ overflow != scratch); ++ DCHECK(overflow != left && overflow != right_reg); ++ ++ if (dst == left || dst == right_reg) { ++ Mulw(scratch, left, right_reg); ++ Mulwh(overflow, left, right_reg); ++ mov(dst, scratch); ++ } else { ++ Mulw(dst, left, right_reg); ++ Mulwh(overflow, left, right_reg); ++ } ++ ++ sral(dst, 32, scratch); ++ xor_(overflow, scratch, overflow); ++} ++ ++void MacroAssembler::CallRuntime(const Runtime::Function* f, int num_arguments, ++ SaveFPRegsMode save_doubles) { ++ ASM_CODE_COMMENT(this); ++ // All parameters are on the stack. v0 has the return value after call. ++ ++ // If the expected number of arguments of the runtime function is ++ // constant, we check that the actual number of arguments match the ++ // expectation. ++ CHECK(f->nargs < 0 || f->nargs == num_arguments); ++ ++ // TODO(1236192): Most runtime routines don't need the number of ++ // arguments passed in because it is constant. At some point we ++ // should remove this need and make the runtime routine entry code ++ // smarter. ++ PrepareCEntryArgs(num_arguments); ++ PrepareCEntryFunction(ExternalReference::Create(f)); ++ Handle code = ++ CodeFactory::CEntry(isolate(), f->result_size, save_doubles); ++ Call(code, RelocInfo::CODE_TARGET); ++} ++ ++void MacroAssembler::TailCallRuntime(Runtime::FunctionId fid) { ++ ASM_CODE_COMMENT(this); ++ const Runtime::Function* function = Runtime::FunctionForId(fid); ++ DCHECK_EQ(1, function->result_size); ++ if (function->nargs >= 0) { ++ PrepareCEntryArgs(function->nargs); ++ } ++ JumpToExternalReference(ExternalReference::Create(fid)); ++} ++ ++void MacroAssembler::JumpToExternalReference(const ExternalReference& builtin, ++ bool builtin_exit_frame) { ++ // SCOPEMARK_NAME(TurboAssembler::JumpToExternalReference, this); ++ PrepareCEntryFunction(builtin); ++ Handle code = CodeFactory::CEntry(isolate(), 1, SaveFPRegsMode::kIgnore, ++ ArgvMode::kStack, builtin_exit_frame); ++ Jump(code, RelocInfo::CODE_TARGET, al, zero_reg, Operand(zero_reg)); ++} ++ ++void MacroAssembler::JumpToOffHeapInstructionStream(Address entry) { ++ li(kOffHeapTrampolineRegister, Operand(entry, RelocInfo::OFF_HEAP_TARGET)); ++ Jump(kOffHeapTrampolineRegister); ++} ++ ++void MacroAssembler::LoadWeakValue(Register out, Register in, ++ Label* target_if_cleared) { ++ // SCOPEMARK_NAME(TurboAssembler::LoadWeakValue, this); ++ Branch(target_if_cleared, eq, in, Operand(kClearedWeakHeapObjectLower32)); ++ ++ And(out, in, Operand(~kWeakHeapObjectMask)); ++} ++ ++void MacroAssembler::EmitIncrementCounter(StatsCounter* counter, int value, ++ Register scratch1, ++ Register scratch2) { ++ DCHECK_GT(value, 0); ++ if (FLAG_native_code_counters && counter->Enabled()) { ++ ASM_CODE_COMMENT(this); ++ // This operation has to be exactly 32-bit wide in case the external ++ // reference table redirects the counter to a uint32_t ++ // dummy_stats_counter_ field. ++ li(scratch2, ExternalReference::Create(counter)); ++ Ldw(scratch1, MemOperand(scratch2)); ++ Addw(scratch1, scratch1, Operand(value)); ++ Stw(scratch1, MemOperand(scratch2)); ++ } ++} ++ ++void MacroAssembler::EmitDecrementCounter(StatsCounter* counter, int value, ++ Register scratch1, ++ Register scratch2) { ++ DCHECK_GT(value, 0); ++ if (FLAG_native_code_counters && counter->Enabled()) { ++ ASM_CODE_COMMENT(this); ++ // This operation has to be exactly 32-bit wide in case the external ++ // reference table redirects the counter to a uint32_t ++ // dummy_stats_counter_ field. ++ li(scratch2, ExternalReference::Create(counter)); ++ Ldw(scratch1, MemOperand(scratch2)); ++ Subw(scratch1, scratch1, Operand(value)); ++ Stw(scratch1, MemOperand(scratch2)); ++ } ++} ++ ++// ----------------------------------------------------------------------------- ++// Debugging. ++ ++void TurboAssembler::Trap() { halt(); } ++void TurboAssembler::DebugBreak() { halt(); } ++ ++void TurboAssembler::Assert(Condition cc, AbortReason reason, Register rs, ++ Operand rt) { ++ if (FLAG_debug_code) Check(cc, reason, rs, rt); ++} ++ ++void TurboAssembler::Check(Condition cc, AbortReason reason, Register rs, ++ Operand rt) { ++ Label L; ++ Branch(&L, cc, rs, rt); ++ Abort(reason); ++ // Will not return here. ++ bind(&L); ++} ++ ++void TurboAssembler::Abort(AbortReason reason) { ++ // SCOPEMARK_NAME(TurboAssembler::Abort, this); ++ Label abort_start; ++ bind(&abort_start); ++ if (FLAG_code_comments) { ++ const char* msg = GetAbortReason(reason); ++ RecordComment("Abort message: "); ++ RecordComment(msg); ++ } ++ ++ // Avoid emitting call to builtin if requested. ++ if (trap_on_abort()) { ++ halt(); // stop(msg); ++ return; ++ } ++ ++ if (should_abort_hard()) { ++ // We don't care if we constructed a frame. Just pretend we did. ++ FrameScope assume_frame(this, StackFrame::NO_FRAME_TYPE); ++ PrepareCallCFunction(0, a0); ++ li(a0, Operand(static_cast(reason))); ++ CallCFunction(ExternalReference::abort_with_reason(), 1); ++ return; ++ } ++ ++ Move(a0, Smi::FromInt(static_cast(reason))); ++ ++ // Disable stub call restrictions to always allow calls to abort. ++ if (!has_frame()) { ++ // We don't actually want to generate a pile of code for this, so just ++ // claim there is a stack frame, without generating one. ++ FrameScope scope(this, StackFrame::NO_FRAME_TYPE); ++ Call(BUILTIN_CODE(isolate(), Abort), RelocInfo::CODE_TARGET); ++ } else { ++ Call(BUILTIN_CODE(isolate(), Abort), RelocInfo::CODE_TARGET); ++ } ++ // Will not return here. ++ if (is_trampoline_pool_blocked()) { ++ // If the calling code cares about the exact number of ++ // instructions generated, we insert padding here to keep the size ++ // of the Abort macro constant. ++ // Currently in debug mode with debug_code enabled the number of ++ // generated instructions is 10, so we use this as a maximum value. ++ static const int kExpectedAbortInstructions = 10; ++ int abort_instructions = InstructionsGeneratedSince(&abort_start); ++ DCHECK_LE(abort_instructions, kExpectedAbortInstructions); ++ while (abort_instructions++ < kExpectedAbortInstructions) { ++ nop(); ++ } ++ } ++} ++ ++void TurboAssembler::LoadMap(Register destination, Register object) { ++ Ldl(destination, FieldMemOperand(object, HeapObject::kMapOffset)); ++} ++ ++void MacroAssembler::LoadNativeContextSlot(Register dst, int index) { ++ LoadMap(dst, cp); ++ Ldl(dst, FieldMemOperand( ++ dst, Map::kConstructorOrBackPointerOrNativeContextOffset)); ++ Ldl(dst, MemOperand(dst, Context::SlotOffset(index))); ++} ++ ++void TurboAssembler::StubPrologue(StackFrame::Type type) { ++ // SCOPEMARK_NAME(TurboAssembler::StubPrologue, this); ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ li(scratch, Operand(StackFrame::TypeToMarker(type))); ++ PushCommonFrame(scratch); ++} ++ ++void TurboAssembler::Prologue() { ++ // SCOPEMARK_NAME(TurboAssembler::Prologue, this); ++ PushStandardFrame(a1); ++} ++ ++void TurboAssembler::EnterFrame(StackFrame::Type type) { ++ ASM_CODE_COMMENT(this); ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ Push(ra, fp); ++ Move(fp, sp); ++ if (!StackFrame::IsJavaScript(type)) { ++ li(kScratchReg, Operand(StackFrame::TypeToMarker(type))); ++ Push(kScratchReg); ++ } ++#if V8_ENABLE_WEBASSEMBLY ++ if (type == StackFrame::WASM) Push(kWasmInstanceRegister); ++#endif // V8_ENABLE_WEBASSEMBLY ++} ++ ++void TurboAssembler::LeaveFrame(StackFrame::Type type) { ++ ASM_CODE_COMMENT(this); ++ Addl(sp, fp, 2 * kPointerSize); ++ Ldl(ra, MemOperand(fp, 1 * kPointerSize)); ++ Ldl(fp, MemOperand(fp, 0 * kPointerSize)); ++} ++ ++void MacroAssembler::EnterExitFrame(bool save_doubles, int stack_space, ++ StackFrame::Type frame_type) { ++ ASM_CODE_COMMENT(this); ++ DCHECK(frame_type == StackFrame::EXIT || ++ frame_type == StackFrame::BUILTIN_EXIT); ++ ++ // Set up the frame structure on the stack. ++ STATIC_ASSERT(2 * kPointerSize == ExitFrameConstants::kCallerSPDisplacement); ++ STATIC_ASSERT(1 * kPointerSize == ExitFrameConstants::kCallerPCOffset); ++ STATIC_ASSERT(0 * kPointerSize == ExitFrameConstants::kCallerFPOffset); ++ ++ // This is how the stack will look: ++ // fp + 2 (==kCallerSPDisplacement) - old stack's end ++ // [fp + 1 (==kCallerPCOffset)] - saved old ra ++ // [fp + 0 (==kCallerFPOffset)] - saved old fp ++ // [fp - 1 StackFrame::EXIT Smi ++ // [fp - 2 (==kSPOffset)] - sp of the called function ++ // fp - (2 + stack_space + alignment) == sp == [fp - kSPOffset] - top of the ++ // new stack (will contain saved ra) ++ ++ // Save registers and reserve room for saved entry sp and code object. ++ subl(sp, 2 * kPointerSize + ExitFrameConstants::kFixedFrameSizeFromFp, sp); ++ Stl(ra, MemOperand(sp, 3 * kPointerSize)); ++ Stl(fp, MemOperand(sp, 2 * kPointerSize)); ++ { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ li(scratch, Operand(StackFrame::TypeToMarker(frame_type))); ++ Stl(scratch, MemOperand(sp, 1 * kPointerSize)); ++ } ++ // Set up new frame pointer. ++ addl(sp, ExitFrameConstants::kFixedFrameSizeFromFp, fp); ++ ++ if (FLAG_debug_code) { ++ Stl(zero_reg, MemOperand(fp, ExitFrameConstants::kSPOffset)); ++ } ++ ++ { ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ // Save the frame pointer and the context in top. ++ li(t11, ExternalReference::Create(IsolateAddressId::kCEntryFPAddress, ++ isolate())); ++ Stl(fp, MemOperand(t11)); ++ li(t11, ++ ExternalReference::Create(IsolateAddressId::kContextAddress, isolate())); ++ Stl(cp, MemOperand(t11)); ++ } ++ ++ const int frame_alignment = MacroAssembler::ActivationFrameAlignment(); ++ if (save_doubles) { ++ // The stack is already aligned to 0 modulo 8 for stores with sdc1. ++ int kNumOfSavedRegisters = FPURegister::kNumRegisters / 2; ++ int space = kNumOfSavedRegisters * kDoubleSize; ++ Subl(sp, sp, Operand(space)); ++ // Remember: we only need to save every 2nd double FPU value. ++ for (int i = 0; i < kNumOfSavedRegisters; i++) { ++ FPURegister reg = FPURegister::from_code(2 * i); ++ Fstd(reg, MemOperand(sp, i * kDoubleSize)); ++ } ++ } ++ ++ // Reserve place for the return address, stack space and an optional slot ++ // (used by DirectCEntry to hold the return value if a struct is ++ // returned) and align the frame preparing for calling the runtime function. ++ DCHECK_GE(stack_space, 0); ++ Subl(sp, sp, Operand((stack_space + 2) * kPointerSize)); ++ if (frame_alignment > 0) { ++ DCHECK(base::bits::IsPowerOfTwo(frame_alignment)); ++ And(sp, sp, Operand(-frame_alignment)); // Align stack. ++ } ++ ++ // Set the exit frame sp value to point just before the return address ++ // location. ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ addl(sp, kPointerSize, scratch); ++ Stl(scratch, MemOperand(fp, ExitFrameConstants::kSPOffset)); ++} ++ ++void MacroAssembler::LeaveExitFrame(bool save_doubles, Register argument_count, ++ bool do_return, ++ bool argument_count_is_length) { ++ ASM_CODE_COMMENT(this); ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ // Optionally restore all double registers. ++ if (save_doubles) { ++ // Remember: we only need to restore every 2nd double FPU value. ++ int kNumOfSavedRegisters = FPURegister::kNumRegisters / 2; ++ Subl(t11, fp, ++ Operand(ExitFrameConstants::kFixedFrameSizeFromFp + ++ kNumOfSavedRegisters * kDoubleSize)); ++ for (int i = 0; i < kNumOfSavedRegisters; i++) { ++ FPURegister reg = FPURegister::from_code(2 * i); ++ Fldd(reg, MemOperand(t11, i * kDoubleSize)); ++ } ++ } ++ ++ // Clear top frame. ++ li(t11, ++ ExternalReference::Create(IsolateAddressId::kCEntryFPAddress, isolate())); ++ Stl(zero_reg, MemOperand(t11)); ++ ++ // Restore current context from top and clear it in debug mode. ++ li(t11, ++ ExternalReference::Create(IsolateAddressId::kContextAddress, isolate())); ++ Ldl(cp, MemOperand(t11)); ++ ++ if (FLAG_debug_code) { ++ UseScratchRegisterScope temp(this); ++ Register scratch = temp.Acquire(); ++ li(scratch, Operand(Context::kInvalidContext)); ++ Stl(scratch, MemOperand(t11)); ++ } ++ ++ // Pop the arguments, restore registers, and return. ++ mov(sp, fp); // Respect ABI stack constraint. ++ Ldl(fp, MemOperand(sp, ExitFrameConstants::kCallerFPOffset)); ++ Ldl(ra, MemOperand(sp, ExitFrameConstants::kCallerPCOffset)); ++ ++ if (argument_count.is_valid()) { ++ if (argument_count_is_length) { ++ addl(sp, argument_count, sp); ++ } else { ++ s8addl(argument_count, sp, sp); ++ DCHECK_EQ(kPointerSizeLog2, 3); ++ } ++ } ++ ++ addl(sp, 2 * kPointerSize, sp); ++ if (do_return) { ++ // If returning, the instruction in the delay slot will be the addw below. ++ Ret(); ++ } ++} ++ ++int TurboAssembler::ActivationFrameAlignment() { ++#if V8_HOST_ARCH_SW64 ++ // Running on the real platform. Use the alignment as mandated by the local ++ // environment. ++ // Note: This will break if we ever start generating snapshots on one Sw64 ++ // platform for another Sw64 platform with a different alignment. ++ return base::OS::ActivationFrameAlignment(); ++#else ++ // If we are using the simulator then we should always align to the expected ++ // alignment. As the simulator is used to generate snapshots we do not know ++ // if the target platform will need alignment, so this is controlled from a ++ // flag. ++ return FLAG_sim_stack_alignment; ++#endif // V8_HOST_ARCH_MIPS ++} ++ ++void MacroAssembler::AssertStackIsAligned() { ++ if (FLAG_debug_code) { ++ ASM_CODE_COMMENT(this); ++ const int frame_alignment = ActivationFrameAlignment(); ++ const int frame_alignment_mask = frame_alignment - 1; ++ ++ if (frame_alignment > kPointerSize) { ++ Label alignment_as_expected; ++ DCHECK(base::bits::IsPowerOfTwo(frame_alignment)); ++ { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ And(scratch, sp, frame_alignment_mask); ++ Branch(&alignment_as_expected, eq, scratch, Operand(zero_reg)); ++ } ++ // Don't use Check here, as it will call Runtime_Abort re-entering here. ++ halt(); ++ bind(&alignment_as_expected); ++ } ++ } ++} ++ ++void TurboAssembler::SmiUntag(Register dst, const MemOperand& src) { ++ // SCOPEMARK_NAME(TurboAssembler::SmiUntag, this); ++ if (SmiValuesAre32Bits()) { ++ Ldw(dst, MemOperand(src.rm(), SmiWordOffset(src.offset()))); ++ } else { ++ DCHECK(SmiValuesAre31Bits()); ++ Ldw(dst, src); ++ SmiUntag(dst); ++ } ++} ++ ++void TurboAssembler::JumpIfSmi(Register value, Label* smi_label) { ++ DCHECK_EQ(0, kSmiTag); ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ and_(value, kSmiTagMask, scratch); ++ Branch(smi_label, eq, scratch, Operand(zero_reg)); ++} ++ ++void MacroAssembler::JumpIfNotSmi(Register value, Label* not_smi_label) { ++ DCHECK_EQ(0, kSmiTag); ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ And(scratch, value, kSmiTagMask); ++ Branch(not_smi_label, ne, scratch, Operand(zero_reg)); ++} ++ ++void TurboAssembler::AssertNotSmi(Register object) { ++ // SCOPEMARK_NAME(TurboAssembler::AssertNotSmi, this); ++ if (FLAG_debug_code) { ++ ASM_CODE_COMMENT(this); ++ STATIC_ASSERT(kSmiTag == 0); ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ and_(object, kSmiTagMask, scratch); ++ Check(ne, AbortReason::kOperandIsASmi, scratch, Operand(zero_reg)); ++ } ++} ++ ++void TurboAssembler::AssertSmi(Register object) { ++ // SCOPEMARK_NAME(TurboAssembler::AssertSmi, this); ++ if (FLAG_debug_code) { ++ ASM_CODE_COMMENT(this); ++ STATIC_ASSERT(kSmiTag == 0); ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ and_(object, kSmiTagMask, scratch); ++ Check(eq, AbortReason::kOperandIsASmi, scratch, Operand(zero_reg)); ++ } ++} ++ ++void MacroAssembler::AssertConstructor(Register object) { ++ if (FLAG_debug_code) { ++ ASM_CODE_COMMENT(this); ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ STATIC_ASSERT(kSmiTag == 0); ++ SmiTst(object, t11); ++ Check(ne, AbortReason::kOperandIsASmiAndNotAConstructor, t11, ++ Operand(zero_reg)); ++ LoadMap(t11, object); ++ Ldbu(t11, FieldMemOperand(t11, Map::kBitFieldOffset)); ++ And(t11, t11, Operand(Map::Bits1::IsConstructorBit::kMask)); ++ Check(ne, AbortReason::kOperandIsNotAConstructor, t11, Operand(zero_reg)); ++ } ++} ++ ++void MacroAssembler::AssertFunction(Register object) { ++ if (FLAG_debug_code) { ++ ASM_CODE_COMMENT(this); ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ STATIC_ASSERT(kSmiTag == 0); ++ SmiTst(object, t11); ++ Check(ne, AbortReason::kOperandIsASmiAndNotAFunction, t11, ++ Operand(zero_reg)); ++ Push(object); ++ LoadMap(object, object); ++ GetInstanceTypeRange(object, object, FIRST_JS_FUNCTION_TYPE, t11); ++ Check(ls, AbortReason::kOperandIsNotAFunction, t11, ++ Operand(LAST_JS_FUNCTION_TYPE - FIRST_JS_FUNCTION_TYPE)); ++ pop(object); ++ } ++} ++ ++void MacroAssembler::AssertCallableFunction(Register object) { ++ if (FLAG_debug_code) { ++ ASM_CODE_COMMENT(this); ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ STATIC_ASSERT(kSmiTag == 0); ++ SmiTst(object, t11); ++ Check(ne, AbortReason::kOperandIsASmiAndNotAFunction, t11, ++ Operand(zero_reg)); ++ Push(object); ++ LoadMap(object, object); ++ GetInstanceTypeRange(object, object, FIRST_CALLABLE_JS_FUNCTION_TYPE, t11); ++ Check(ls, AbortReason::kOperandIsNotACallableFunction, t11, ++ Operand(LAST_CALLABLE_JS_FUNCTION_TYPE - ++ FIRST_CALLABLE_JS_FUNCTION_TYPE)); ++ pop(object); ++ } ++} ++ ++void MacroAssembler::AssertBoundFunction(Register object) { ++ if (FLAG_debug_code) { ++ ASM_CODE_COMMENT(this); ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ STATIC_ASSERT(kSmiTag == 0); ++ SmiTst(object, t11); ++ Check(ne, AbortReason::kOperandIsASmiAndNotABoundFunction, t11, ++ Operand(zero_reg)); ++ GetObjectType(object, t11, t11); ++ Check(eq, AbortReason::kOperandIsNotABoundFunction, t11, ++ Operand(JS_BOUND_FUNCTION_TYPE)); ++ } ++} ++ ++void MacroAssembler::AssertGeneratorObject(Register object) { ++ if (!FLAG_debug_code) return; ++ ASM_CODE_COMMENT(this); ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ STATIC_ASSERT(kSmiTag == 0); ++ SmiTst(object, t11); ++ Check(ne, AbortReason::kOperandIsASmiAndNotAGeneratorObject, t11, ++ Operand(zero_reg)); ++ ++ GetObjectType(object, t11, t11); ++ ++ Label done; ++ ++ // Check if JSGeneratorObject ++ Branch(&done, eq, t11, Operand(JS_GENERATOR_OBJECT_TYPE)); ++ ++ // Check if JSAsyncFunctionObject (See MacroAssembler::CompareInstanceType) ++ Branch(&done, eq, t11, Operand(JS_ASYNC_FUNCTION_OBJECT_TYPE)); ++ ++ // Check if JSAsyncGeneratorObject ++ Branch(&done, eq, t11, Operand(JS_ASYNC_GENERATOR_OBJECT_TYPE)); ++ ++ Abort(AbortReason::kOperandIsNotAGeneratorObject); ++ ++ bind(&done); ++} ++ ++void MacroAssembler::AssertUndefinedOrAllocationSite(Register object, ++ Register scratch) { ++ if (FLAG_debug_code) { ++ ASM_CODE_COMMENT(this); ++ Label done_checking; ++ AssertNotSmi(object); ++ LoadRoot(scratch, RootIndex::kUndefinedValue); ++ Branch(&done_checking, eq, object, Operand(scratch)); ++ GetObjectType(object, scratch, scratch); ++ Assert(eq, AbortReason::kExpectedUndefinedOrCell, scratch, ++ Operand(ALLOCATION_SITE_TYPE)); ++ bind(&done_checking); ++ } ++} ++ ++void TurboAssembler::Float32Max(FPURegister dst, FPURegister src1, ++ FPURegister src2, Label* out_of_line) { ++ ASM_CODE_COMMENT(this); ++ if (src1 == src2) { ++ Move_s(dst, src1); ++ return; ++ } ++ ++ // Check if one of operands is NaN. ++ CompareIsNanF(src1, src2); ++ BranchTrueF(out_of_line); ++ ++ Label return_left, return_right, done; ++ ++ CompareF(OLT, src1, src2); ++ BranchTrueShortF(&return_right); ++ CompareF(OLT, src2, src1); ++ BranchTrueShortF(&return_left); ++ ++ // Operands are equal, but check for +/-0. ++ { ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ fimovd(src1, t11); ++ Branch(&return_left, eq, t11, Operand(zero_reg)); ++ Branch(&return_right); ++ } ++ ++ bind(&return_right); ++ if (src2 != dst) { ++ Move_s(dst, src2); ++ } ++ Branch(&done); ++ ++ bind(&return_left); ++ if (src1 != dst) { ++ Move_s(dst, src1); ++ } ++ ++ bind(&done); ++} ++ ++void TurboAssembler::Float32MaxOutOfLine(FPURegister dst, FPURegister src1, ++ FPURegister src2) { ++ // SCOPEMARK_NAME(TurboAssembler::Float32MaxOutOfLine, this); ++ fadds(src1, src2, dst); ++} ++ ++void TurboAssembler::Float32Min(FPURegister dst, FPURegister src1, ++ FPURegister src2, Label* out_of_line) { ++ ASM_CODE_COMMENT(this); ++ if (src1 == src2) { ++ Move_s(dst, src1); ++ return; ++ } ++ ++ // Check if one of operands is NaN. ++ CompareIsNanF(src1, src2); ++ BranchTrueF(out_of_line); ++ ++ Label return_left, return_right, done; ++ ++ CompareF(OLT, src1, src2); ++ BranchTrueShortF(&return_left); ++ CompareF(OLT, src2, src1); ++ BranchTrueShortF(&return_right); ++ ++ // Left equals right => check for -0. ++ { ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ fimovd(src1, t11); ++ Branch(&return_right, eq, t11, Operand(zero_reg)); ++ Branch(&return_left); ++ } ++ ++ bind(&return_right); ++ if (src2 != dst) { ++ Move_s(dst, src2); ++ } ++ Branch(&done); ++ ++ bind(&return_left); ++ if (src1 != dst) { ++ Move_s(dst, src1); ++ } ++ ++ bind(&done); ++} ++ ++void TurboAssembler::Float32MinOutOfLine(FPURegister dst, FPURegister src1, ++ FPURegister src2) { ++ // SCOPEMARK_NAME(TurboAssembler::Float32MinOutOfLine, this); ++ fadds(src1, src2, dst); ++} ++ ++void TurboAssembler::Float64Max(FPURegister dst, FPURegister src1, ++ FPURegister src2, Label* out_of_line) { ++ ASM_CODE_COMMENT(this); ++ if (src1 == src2) { ++ Move_d(dst, src1); ++ return; ++ } ++ ++ // Check if one of operands is NaN. ++ CompareIsNanF(src1, src2); ++ BranchTrueF(out_of_line); ++ ++ Label return_left, return_right, done; ++ ++ CompareF(OLT, src1, src2); ++ BranchTrueShortF(&return_right); ++ CompareF(OLT, src2, src1); ++ BranchTrueShortF(&return_left); ++ ++ // Left equals right => check for -0. ++ { ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ fimovd(src1, t11); ++ Branch(&return_left, eq, t11, Operand(zero_reg)); ++ Branch(&return_right); ++ } ++ ++ bind(&return_right); ++ if (src2 != dst) { ++ Move_d(dst, src2); ++ } ++ Branch(&done); ++ ++ bind(&return_left); ++ if (src1 != dst) { ++ Move_d(dst, src1); ++ } ++ ++ bind(&done); ++} ++ ++void TurboAssembler::Float64MaxOutOfLine(FPURegister dst, FPURegister src1, ++ FPURegister src2) { ++ // SCOPEMARK_NAME(TurboAssembler::Float64MaxOutOfLine, this); ++ faddd(src1, src2, dst); ++} ++ ++void TurboAssembler::Float64Min(FPURegister dst, FPURegister src1, ++ FPURegister src2, Label* out_of_line) { ++ ASM_CODE_COMMENT(this); ++ if (src1 == src2) { ++ Move_d(dst, src1); ++ return; ++ } ++ ++ // Check if one of operands is NaN. ++ CompareIsNanF(src1, src2); ++ BranchTrueF(out_of_line); ++ ++ Label return_left, return_right, done; ++ ++ CompareF(OLT, src1, src2); ++ BranchTrueShortF(&return_left); ++ CompareF(OLT, src2, src1); ++ BranchTrueShortF(&return_right); ++ ++ // Left equals right => check for -0. ++ { ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ fimovd(src1, t11); ++ Branch(&return_right, eq, t11, Operand(zero_reg)); ++ Branch(&return_left); ++ } ++ ++ bind(&return_right); ++ if (src2 != dst) { ++ Move_d(dst, src2); ++ } ++ Branch(&done); ++ ++ bind(&return_left); ++ if (src1 != dst) { ++ Move_d(dst, src1); ++ } ++ ++ bind(&done); ++} ++ ++void TurboAssembler::Float64MinOutOfLine(FPURegister dst, FPURegister src1, ++ FPURegister src2) { ++ // SCOPEMARK_NAME(TurboAssembler::Float64MinOutOfLine, this); ++ faddd(src1, src2, dst); ++} ++ ++static const int kRegisterPassedArguments = 6; ++ ++int TurboAssembler::CalculateStackPassedWords(int num_reg_arguments, ++ int num_double_arguments) { ++ int stack_passed_words = 0; ++ num_reg_arguments += 2 * num_double_arguments; ++ ++ //Up to eight simple arguments are passed in registers a0..a7. ++ if (num_reg_arguments > kRegisterPassedArguments) { ++ stack_passed_words += num_reg_arguments - kRegisterPassedArguments; ++ } ++ stack_passed_words += kCArgSlotCount; ++ return stack_passed_words; ++} ++ ++void TurboAssembler::PrepareCallCFunction(int num_reg_arguments, ++ int num_double_arguments, ++ Register scratch) { ++ ASM_CODE_COMMENT(this); ++ int frame_alignment = ActivationFrameAlignment(); ++ ++ // n64: Up to eight simple arguments in a0..a3, a4..a5, No argument slots. ++ // O32: Up to four simple arguments are passed in registers a0..a3. ++ // Those four arguments must have reserved argument slots on the stack for ++ // sw64, even though those argument slots are not normally used. ++ // Both ABIs: Remaining arguments are pushed on the stack, above (higher ++ // address than) the (O32) argument slots. (arg slot calculation handled by ++ // CalculateStackPassedWords()). ++ int stack_passed_arguments = ++ CalculateStackPassedWords(num_reg_arguments, num_double_arguments); ++ if (frame_alignment > kPointerSize) { ++ // Make stack end at alignment and make room for num_arguments - 4 words ++ // and the original value of sp. ++ mov(scratch, sp); ++ Subl(sp, sp, Operand((stack_passed_arguments + 1) * kPointerSize)); ++ DCHECK(base::bits::IsPowerOfTwo(frame_alignment)); ++ And(sp, sp, Operand(-frame_alignment)); ++ Stl(scratch, MemOperand(sp, stack_passed_arguments * kPointerSize)); ++ } else { ++ Subl(sp, sp, Operand(stack_passed_arguments * kPointerSize)); ++ } ++} ++ ++void TurboAssembler::PrepareCallCFunction(int num_reg_arguments, ++ Register scratch) { ++ // SCOPEMARK_NAME(TurboAssembler::PrepareCallCFunction, this); ++ PrepareCallCFunction(num_reg_arguments, 0, scratch); ++} ++ ++void TurboAssembler::CallCFunction(ExternalReference function, ++ int num_reg_arguments, ++ int num_double_arguments) { ++ ASM_CODE_COMMENT(this); ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ li(t12, function); ++ CallCFunctionHelper(t12, num_reg_arguments, num_double_arguments); ++} ++ ++void TurboAssembler::CallCFunction(Register function, int num_reg_arguments, ++ int num_double_arguments) { ++ ASM_CODE_COMMENT(this); ++ CallCFunctionHelper(function, num_reg_arguments, num_double_arguments); ++} ++ ++void TurboAssembler::CallCFunction(ExternalReference function, ++ int num_arguments) { ++ CallCFunction(function, num_arguments, 0); ++} ++ ++void TurboAssembler::CallCFunction(Register function, int num_arguments) { ++ CallCFunction(function, num_arguments, 0); ++} ++ ++void TurboAssembler::CallCFunctionHelper(Register function, ++ int num_reg_arguments, ++ int num_double_arguments) { ++ DCHECK_LE(num_reg_arguments + num_double_arguments, kMaxCParameters); ++ DCHECK(has_frame()); ++ // Make sure that the stack is aligned before calling a C function unless ++ // running in the simulator. The simulator has its own alignment check which ++ // provides more information. ++ // The argument stots are presumed to have been set up by ++ // PrepareCallCFunction. The C function must be called via t12, for sw64 ABI. ++ ++#if V8_HOST_ARCH_SW64 ++ if (FLAG_debug_code) { ++ int frame_alignment = base::OS::ActivationFrameAlignment(); ++ int frame_alignment_mask = frame_alignment - 1; ++ if (frame_alignment > kPointerSize) { ++ DCHECK(base::bits::IsPowerOfTwo(frame_alignment)); ++ Label alignment_as_expected; ++ { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ And(scratch, sp, Operand(frame_alignment_mask)); ++ Branch(&alignment_as_expected, eq, scratch, Operand(zero_reg)); ++ } ++ // Don't use Check here, as it will call Runtime_Abort possibly ++ // re-entering here. ++ halt(); // stop("Unexpected alignment in CallCFunction"); ++ bind(&alignment_as_expected); ++ } ++ } ++#endif // V8_HOST_ARCH_SW64 ++ ++ // Just call directly. The function called cannot cause a GC, or ++ // allow preemption, so the return address in the link register ++ // stays correct. ++ { ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ if (function != t12) { ++ mov(t12, function); ++ function = t12; ++ } ++ ++ // Save the frame pointer and PC so that the stack layout remains ++ // iterable, even without an ExitFrame which normally exists between JS ++ // and C frames. 't' registers are caller-saved so this is safe as a ++ // scratch register. ++ Register pc_scratch = t1; ++ Register scratch = t2; ++ DCHECK(!AreAliased(pc_scratch, scratch, function)); ++ ++ bsr(pc_scratch, 0); ++ ++ // See x64 code for reasoning about how to address the isolate data ++ // fields. ++ if (root_array_available()) { ++ Stl(pc_scratch, ++ MemOperand(kRootRegister, ++ IsolateData::fast_c_call_caller_pc_offset())); ++ Stl(fp, MemOperand(kRootRegister, ++ IsolateData::fast_c_call_caller_fp_offset())); ++ } else { ++ DCHECK_NOT_NULL(isolate()); ++ li(scratch, ++ ExternalReference::fast_c_call_caller_pc_address(isolate())); ++ Stl(pc_scratch, MemOperand(scratch)); ++ li(scratch, ++ ExternalReference::fast_c_call_caller_fp_address(isolate())); ++ Stl(fp, MemOperand(scratch)); ++ } ++ ++ Call(function); ++ ++ // We don't unset the PC; the FP is the source of truth. ++ if (root_array_available()) { ++ Stl(zero_reg, MemOperand(kRootRegister, ++ IsolateData::fast_c_call_caller_fp_offset())); ++ } else { ++ DCHECK_NOT_NULL(isolate()); ++ Register scratch = t2; ++ li(scratch, ++ ExternalReference::fast_c_call_caller_fp_address(isolate())); ++ Stl(zero_reg, MemOperand(scratch)); ++ } ++ ++ int stack_passed_arguments = ++ CalculateStackPassedWords(num_reg_arguments, num_double_arguments); ++ ++ if (base::OS::ActivationFrameAlignment() > kPointerSize) { ++ Ldl(sp, MemOperand(sp, stack_passed_arguments * kPointerSize)); ++ } else { ++ Addl(sp, sp, Operand(stack_passed_arguments * kPointerSize)); ++ } ++ ++ set_pc_for_safepoint(); ++ } ++} ++ ++#undef BRANCH_ARGS_CHECK ++ ++void TurboAssembler::CheckPageFlag(Register object, Register scratch, int mask, ++ Condition cc, Label* condition_met) { ++ ASM_CODE_COMMENT(this); ++ And(scratch, object, Operand(~kPageAlignmentMask)); ++ Ldl(scratch, MemOperand(scratch, BasicMemoryChunk::kFlagsOffset)); ++ And(scratch, scratch, Operand(mask)); ++ Branch(condition_met, cc, scratch, Operand(zero_reg)); ++} ++ ++Register GetRegisterThatIsNotOneOf(Register reg1, Register reg2, Register reg3, ++ Register reg4, Register reg5, ++ Register reg6) { ++ RegList regs = {reg1, reg2, reg3, reg4, reg5, reg6}; ++ ++ const RegisterConfiguration* config = RegisterConfiguration::Default(); ++ for (int i = 0; i < config->num_allocatable_general_registers(); ++i) { ++ int code = config->GetAllocatableGeneralCode(i); ++ Register candidate = Register::from_code(code); ++ if (regs.has(candidate)) continue; ++ return candidate; ++ } ++ UNREACHABLE(); ++} ++ ++void TurboAssembler::ComputeCodeStartAddress(Register dst) { ++ // SCOPEMARK_NAME(TurboAssembler::ComputeCodeStartAddress, this); ++ br(dst, 0); ++ int pc = pc_offset(); ++ Subl(dst, dst, Operand(pc)); ++} ++ ++void TurboAssembler::CallForDeoptimization(Builtin target, int, Label* exit, ++ DeoptimizeKind kind, Label* ret, ++ Label*) { ++ ASM_CODE_COMMENT(this); ++ BlockTrampolinePoolScope block_trampoline_pool(this); ++ Ldl(t12, ++ MemOperand(kRootRegister, IsolateData::BuiltinEntrySlotOffset(target))); ++ Call(t12); ++ DCHECK_EQ(SizeOfCodeGeneratedSince(exit), ++ (kind == DeoptimizeKind::kLazy) ? Deoptimizer::kLazyDeoptExitSize ++ : Deoptimizer::kEagerDeoptExitSize); ++} ++ ++void TurboAssembler::LoadCodeObjectEntry(Register destination, ++ Register code_object) { ++ ASM_CODE_COMMENT(this); ++ // Code objects are called differently depending on whether we are generating ++ // builtin code (which will later be embedded into the binary) or compiling ++ // user JS code at runtime. ++ // * Builtin code runs in --jitless mode and thus must not call into on-heap ++ // Code targets. Instead, we dispatch through the builtins entry table. ++ // * Codegen at runtime does not have this restriction and we can use the ++ // shorter, branchless instruction sequence. The assumption here is that ++ // targets are usually generated code and not builtin Code objects. ++ if (options().isolate_independent_code) { ++ DCHECK(root_array_available()); ++ Label if_code_is_off_heap, out; ++ ++ Register scratch = kScratchReg; ++ DCHECK(!AreAliased(destination, scratch)); ++ DCHECK(!AreAliased(code_object, scratch)); ++ ++ // Check whether the Code object is an off-heap trampoline. If so, call its ++ // (off-heap) entry point directly without going through the (on-heap) ++ // trampoline. Otherwise, just call the Code object as always. ++ Ldw(scratch, FieldMemOperand(code_object, Code::kFlagsOffset)); ++ And(scratch, scratch, Operand(Code::IsOffHeapTrampoline::kMask)); ++ Branch(&if_code_is_off_heap, ne, scratch, Operand(zero_reg)); ++ ++ // Not an off-heap trampoline object, the entry point is at ++ // Code::raw_instruction_start(). ++ Addl(destination, code_object, Code::kHeaderSize - kHeapObjectTag); ++ Branch(&out); ++ ++ // An off-heap trampoline, the entry point is loaded from the builtin entry ++ // table. ++ bind(&if_code_is_off_heap); ++ Ldw(scratch, FieldMemOperand(code_object, Code::kBuiltinIndexOffset)); ++ Lsal(destination, kRootRegister, scratch, kSystemPointerSizeLog2); ++ Ldl(destination, ++ MemOperand(destination, IsolateData::builtin_entry_table_offset())); ++ ++ bind(&out); ++ } else { ++ Addl(destination, code_object, Code::kHeaderSize - kHeapObjectTag); ++ } ++} ++ ++void TurboAssembler::CallCodeObject(Register code_object) { ++ ASM_CODE_COMMENT(this); ++ LoadCodeObjectEntry(code_object, code_object); ++ Call(code_object); ++} ++ ++void TurboAssembler::JumpCodeObject(Register code_object, JumpMode jump_mode) { ++ ASM_CODE_COMMENT(this); ++ DCHECK_EQ(JumpMode::kJump, jump_mode); ++ LoadCodeObjectEntry(code_object, code_object); ++ Jump(code_object); ++} ++ ++} // namespace internal ++} // namespace v8 ++ ++#endif // V8_TARGET_ARCH_SW64 +diff --git a/deps/v8/src/codegen/sw64/macro-assembler-sw64.h b/deps/v8/src/codegen/sw64/macro-assembler-sw64.h +new file mode 100644 +index 00000000..4cfd9180 +--- /dev/null ++++ b/deps/v8/src/codegen/sw64/macro-assembler-sw64.h +@@ -0,0 +1,1248 @@ ++// Copyright 2012 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++// ++#ifndef INCLUDED_FROM_MACRO_ASSEMBLER_H ++#error This header must be included via macro-assembler.h ++#endif ++ ++#ifndef V8_CODEGEN_SW64_MACRO_ASSEMBLER_SW64_H_ ++#define V8_CODEGEN_SW64_MACRO_ASSEMBLER_SW64_H_ ++ ++#ifdef PRODUCT ++#define SCOPEMARK /* nothing */ ++#define SCOPEMARK_NAME(name, tasm) /* nothing */ ++#else ++#define SCOPEMARK \ ++ char line[200]; sprintf(line,"%s:%d",__FILE__, __LINE__);\ ++ ScopeMark scopeMark(_tasm, line); ++ ++#define SCOPEMARK_NAME(name, tasm) \ ++ char line[200]; sprintf(line,"%s:%d",__FILE__, __LINE__);\ ++ ScopeMark scopeMark(tasm, line, #name); ++ ++#endif ++ ++#include "src/codegen/assembler.h" ++#include "src/codegen/sw64/assembler-sw64.h" ++#include "src/common/globals.h" ++#include "src/objects/tagged-index.h" ++ ++namespace v8 { ++namespace internal { ++ ++// Forward declarations. ++enum class AbortReason : uint8_t; ++ ++// Reserved Register Usage Summary. ++// ++// Registers t11, t12, and at are reserved for use by the MacroAssembler. ++// ++// The programmer should know that the MacroAssembler may clobber these three, ++// but won't touch other registers except in special cases. ++// ++// Per the SW64 ABI, register t12 must be used for indirect function call ++// via 'jalr t12' or 'jr t12' instructions. This is relied upon by gcc when ++// trying to update gp register for position-independent-code. Whenever ++// SW64 generated code calls C code, it must be via t12 register. ++ ++// Flags used for LeaveExitFrame function. ++enum LeaveExitFrameMode { EMIT_RETURN = true, NO_EMIT_RETURN = false }; ++ ++// Flags used for the li macro-assembler function. ++enum LiFlags { ++ // If the constant value can be represented in just 16 bits, then ++ // optimize the li to use a single instruction, rather than ldi/slll/ldih ++ // sequence. A number of other optimizations that emits less than ++ // maximum number of instructions exists. ++ OPTIMIZE_SIZE = 0, ++ // Always use 6 instructions (ldi/slll/ldih sequence) for release 2 or 4 ++ // instructions for release 6, even if the constant ++ // could be loaded with just one, so that this value is patchable later. ++ CONSTANT_SIZE = 1, ++ // For address loads only 4 instruction are required. Used to mark ++ // constant load that will be used as address without relocation ++ // information. It ensures predictable code size, so specific sites ++ // in code are patchable. ++ ADDRESS_LOAD = 2 ++}; ++ ++enum RAStatus { kRAHasNotBeenSaved, kRAHasBeenSaved }; ++ ++Register GetRegisterThatIsNotOneOf(Register reg1, Register reg2 = no_reg, ++ Register reg3 = no_reg, ++ Register reg4 = no_reg, ++ Register reg5 = no_reg, ++ Register reg6 = no_reg); ++ ++// ----------------------------------------------------------------------------- ++// Static helper functions. ++ ++#define SmiWordOffset(offset) (offset + kPointerSize / 2) ++ ++// Generate a MemOperand for loading a field from an object. ++inline MemOperand FieldMemOperand(Register object, int offset) { ++ return MemOperand(object, offset - kHeapObjectTag); ++} ++ ++// Generate a MemOperand for storing arguments 5..N on the stack ++// when calling CallCFunction(). ++// TODO(plind): Currently ONLY used for O32. Should be fixed for ++// n64, and used in RegExp code, and other places ++// with more than 8 arguments. ++inline MemOperand CFunctionArgumentOperand(int index) { ++ DCHECK_GT(index, kCArgSlotCount); ++ // Argument 5 takes the slot just past the four Arg-slots. ++ int offset = (index - 5) * kPointerSize + kCArgsSlotsSize; ++ return MemOperand(sp, offset); ++} ++ ++class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase { ++ public: ++ using TurboAssemblerBase::TurboAssemblerBase; ++ ++ // Activation support. ++ void EnterFrame(StackFrame::Type type); ++ void EnterFrame(StackFrame::Type type, bool load_constant_pool_pointer_reg) { ++ // Out-of-line constant pool not implemented on sw64. ++ UNREACHABLE(); ++ } ++ void LeaveFrame(StackFrame::Type type); ++ ++ void AllocateStackSpace(Register bytes) { Subl(sp, sp, bytes); } ++ ++ void AllocateStackSpace(int bytes) { ++ DCHECK_GE(bytes, 0); ++ if (bytes == 0) return; ++ Subl(sp, sp, Operand(bytes)); ++ } ++ ++ // Generates function and stub prologue code. ++ void StubPrologue(StackFrame::Type type); ++ void Prologue(); ++ ++ void InitializeRootRegister() { ++ ExternalReference isolate_root = ExternalReference::isolate_root(isolate()); ++ li(kRootRegister, Operand(isolate_root)); ++ } ++ ++ // Jump unconditionally to given label. ++ // We NEED a nop in the branch delay slot, as it used by v8, for example in ++ // CodeGenerator::ProcessDeferred(). ++ // Currently the branch delay slot is filled by the MacroAssembler. ++ // Use rather b(Label) for code generation. ++ void jmp(Label* L) { Branch(L); } ++ ++ // ------------------------------------------------------------------------- ++ // Debugging. ++ ++ void Trap(); ++ void DebugBreak(); ++ ++ // Calls Abort(msg) if the condition cc is not satisfied. ++ // Use --debug_code to enable. ++ void Assert(Condition cc, AbortReason reason, Register rs, Operand rt); ++ ++ // Like Assert(), but always enabled. ++ void Check(Condition cc, AbortReason reason, Register rs, Operand rt); ++ ++ // Print a message to stdout and abort execution. ++ void Abort(AbortReason msg); ++ ++ // Arguments macros. ++#define COND_TYPED_ARGS Condition cond, Register r1, const Operand &r2 ++#define COND_ARGS cond, r1, r2 ++ ++ // Cases when relocation is not needed. ++#define DECLARE_NORELOC_PROTOTYPE(Name, target_type) \ ++ void Name(target_type target); \ ++ void Name(target_type target, COND_TYPED_ARGS); ++ ++#define DECLARE_BRANCH_PROTOTYPES(Name) \ ++ DECLARE_NORELOC_PROTOTYPE(Name, Label*) \ ++ DECLARE_NORELOC_PROTOTYPE(Name, int32_t) ++ ++ DECLARE_BRANCH_PROTOTYPES(Branch) ++ DECLARE_BRANCH_PROTOTYPES(BranchAndLink) ++ DECLARE_BRANCH_PROTOTYPES(BranchShort) ++ ++#undef DECLARE_BRANCH_PROTOTYPES ++#undef COND_TYPED_ARGS ++#undef COND_ARGS ++ ++ void CompareF(FPUCondition cc, FPURegister cmp1, FPURegister cmp2); ++ ++ void CompareIsNanF(FPURegister cmp1, FPURegister cmp2); ++ ++ // Floating point branches ++ void BranchTrueShortF(Label* target); ++ void BranchFalseShortF(Label* target); ++ ++ void BranchTrueF(Label* target); ++ void BranchFalseF(Label* target); ++ ++ void BranchLong(int32_t offset); ++ void Branch(Label* L, Condition cond, Register rs, RootIndex index); ++ ++ static int InstrCountForLi64Bit(int64_t value); ++ inline void LiLower32BitHelper(Register rd, Operand j); ++ void li_optimized(Register rd, Operand j, LiFlags mode = OPTIMIZE_SIZE); ++ // Load int32 in the rd register. ++ void li(Register rd, Operand j, LiFlags mode = OPTIMIZE_SIZE); ++ inline void li(Register rd, int64_t j, LiFlags mode = OPTIMIZE_SIZE) { ++ li(rd, Operand(j), mode); ++ } ++ void li(Register dst, Handle value, LiFlags mode = OPTIMIZE_SIZE); ++ void li(Register dst, ExternalReference value, LiFlags mode = OPTIMIZE_SIZE); ++ void li(Register dst, const StringConstantBase* string, ++ LiFlags mode = OPTIMIZE_SIZE); ++ ++ void LoadFromConstantsTable(Register destination, int constant_index) final; ++ void LoadRootRegisterOffset(Register destination, intptr_t offset) final; ++ void LoadRootRelative(Register destination, int32_t offset) final; ++ ++ inline void Move(Register output, MemOperand operand) { Ldl(output, operand); } ++ ++// Jump, Call, and Ret pseudo instructions implementing inter-working. ++#define COND_ARGS \ ++ Condition cond = al, Register rs = zero_reg, \ ++ const Operand &rt = Operand(zero_reg) ++ ++ void Jump(Register target, COND_ARGS); ++ void Jump(intptr_t target, RelocInfo::Mode rmode, COND_ARGS); ++ void Jump(Address target, RelocInfo::Mode rmode, COND_ARGS); ++ // Deffer from li, this method save target to the memory, and then load ++ // it to register use ld, it can be used in wasm jump table for concurrent ++ // patching. ++ void PatchAndJump(Address target); ++ void Jump(Handle code, RelocInfo::Mode rmode, COND_ARGS); ++ void Jump(const ExternalReference& reference); ++ void Call(Register target, COND_ARGS); ++ void Call(Address target, RelocInfo::Mode rmode, COND_ARGS); ++ void Call(Handle code, RelocInfo::Mode rmode = RelocInfo::CODE_TARGET, ++ COND_ARGS); ++ void Call(Label* target); ++ void LoadAddress(Register dst, Label* target); ++ ++ // Load the builtin given by the Smi in |builtin_index| into the same ++ // register. ++ void LoadEntryFromBuiltinIndex(Register builtin); ++ void LoadEntryFromBuiltin(Builtin builtin, Register destination); ++ MemOperand EntryFromBuiltinAsOperand(Builtin builtin); ++ ++ void CallBuiltinByIndex(Register builtin); ++ void CallBuiltin(Builtin builtin); ++ ++ void LoadCodeObjectEntry(Register destination, Register code_object); ++ void CallCodeObject(Register code_object); ++ void JumpCodeObject(Register code_object, ++ JumpMode jump_mode = JumpMode::kJump); ++ ++ // Generates an instruction sequence s.t. the return address points to the ++ // instruction following the call. ++ // The return address on the stack is used by frame iteration. ++ void StoreReturnAddressAndCall(Register target); ++ ++ void CallForDeoptimization(Builtin target, int deopt_id, Label* exit, ++ DeoptimizeKind kind, Label* ret, ++ Label* jump_deoptimization_entry_label); ++ ++ void Ret(COND_ARGS); ++ ++ // Emit code to discard a non-negative number of pointer-sized elements ++ // from the stack, clobbering only the sp register. ++ void Drop(int count, Condition cond = cc_always, Register reg = no_reg, ++ const Operand& op = Operand(no_reg)); ++ ++ enum ArgumentsCountMode { kCountIncludesReceiver, kCountExcludesReceiver }; ++ enum ArgumentsCountType { kCountIsInteger, kCountIsSmi, kCountIsBytes }; ++ void DropArguments(Register count, ArgumentsCountType type, ++ ArgumentsCountMode mode, Register scratch = no_reg); ++ void DropArgumentsAndPushNewReceiver(Register argc, Register receiver, ++ ArgumentsCountType type, ++ ArgumentsCountMode mode, ++ Register scratch = no_reg); ++ ++ // Trivial case of DropAndRet that utilizes the delay slot. ++ void DropAndRet(int drop); ++ ++ void DropAndRet(int drop, Condition cond, Register reg, const Operand& op); ++ ++ void Ldl(Register rd, const MemOperand& rs); ++ void Stl(Register rd, const MemOperand& rs); ++ ++ void Push(Handle handle); ++ void Push(Smi smi); ++ ++ void Push(Register src) { ++ Addl(sp, sp, Operand(-kPointerSize)); ++ Stl(src, MemOperand(sp, 0)); ++ } ++ ++ // Push two registers. Pushes leftmost register first (to highest address). ++ void Push(Register src1, Register src2) { ++ Subl(sp, sp, Operand(2 * kPointerSize)); ++ Stl(src1, MemOperand(sp, 1 * kPointerSize)); ++ Stl(src2, MemOperand(sp, 0 * kPointerSize)); ++ } ++ ++ // Push three registers. Pushes leftmost register first (to highest address). ++ void Push(Register src1, Register src2, Register src3) { ++ Subl(sp, sp, Operand(3 * kPointerSize)); ++ Stl(src1, MemOperand(sp, 2 * kPointerSize)); ++ Stl(src2, MemOperand(sp, 1 * kPointerSize)); ++ Stl(src3, MemOperand(sp, 0 * kPointerSize)); ++ } ++ ++ // Push four registers. Pushes leftmost register first (to highest address). ++ void Push(Register src1, Register src2, Register src3, Register src4) { ++ Subl(sp, sp, Operand(4 * kPointerSize)); ++ Stl(src1, MemOperand(sp, 3 * kPointerSize)); ++ Stl(src2, MemOperand(sp, 2 * kPointerSize)); ++ Stl(src3, MemOperand(sp, 1 * kPointerSize)); ++ Stl(src4, MemOperand(sp, 0 * kPointerSize)); ++ } ++ ++ // Push five registers. Pushes leftmost register first (to highest address). ++ void Push(Register src1, Register src2, Register src3, Register src4, ++ Register src5) { ++ Subl(sp, sp, Operand(5 * kPointerSize)); ++ Stl(src1, MemOperand(sp, 4 * kPointerSize)); ++ Stl(src2, MemOperand(sp, 3 * kPointerSize)); ++ Stl(src3, MemOperand(sp, 2 * kPointerSize)); ++ Stl(src4, MemOperand(sp, 1 * kPointerSize)); ++ Stl(src5, MemOperand(sp, 0 * kPointerSize)); ++ } ++ ++ void Push(Register src, Condition cond, Register tst1, Register tst2) { ++ // Since we don't have conditional execution we use a Branch. ++ Branch(3, cond, tst1, Operand(tst2)); ++ Subl(sp, sp, Operand(kPointerSize)); ++ Stl(src, MemOperand(sp, 0)); ++ } ++ ++ enum PushArrayOrder { kNormal, kReverse }; ++ void PushArray(Register array, Register size, Register scratch, ++ Register scratch2, PushArrayOrder order = kNormal); ++ ++ void MaybeSaveRegisters(RegList registers); ++ void MaybeRestoreRegisters(RegList registers); ++ ++ void CallEphemeronKeyBarrier(Register object, Register slot_address, ++ SaveFPRegsMode fp_mode); ++ ++ void CallRecordWriteStubSaveRegisters( ++ Register object, Register slot_address, ++ RememberedSetAction remembered_set_action, SaveFPRegsMode fp_mode, ++ StubCallMode mode = StubCallMode::kCallBuiltinPointer); ++ void CallRecordWriteStub( ++ Register object, Register slot_address, ++ RememberedSetAction remembered_set_action, SaveFPRegsMode fp_mode, ++ StubCallMode mode = StubCallMode::kCallBuiltinPointer); ++ ++ // Push multiple registers on the stack. ++ // Registers are saved in numerical order, with higher numbered registers ++ // saved in higher memory addresses. ++ void MultiPush(RegList regs); ++ void MultiPushFPU(DoubleRegList regs); ++ void MultiPushSWSA(DoubleRegList regs); ++ ++ // Calculate how much stack space (in bytes) are required to store caller ++ // registers excluding those specified in the arguments. ++ int RequiredStackSizeForCallerSaved(SaveFPRegsMode fp_mode, ++ Register exclusion1 = no_reg, ++ Register exclusion2 = no_reg, ++ Register exclusion3 = no_reg) const; ++ ++ // Push caller saved registers on the stack, and return the number of bytes ++ // stack pointer is adjusted. ++ int PushCallerSaved(SaveFPRegsMode fp_mode, Register exclusion1 = no_reg, ++ Register exclusion2 = no_reg, ++ Register exclusion3 = no_reg); ++ // Restore caller saved registers from the stack, and return the number of ++ // bytes stack pointer is adjusted. ++ int PopCallerSaved(SaveFPRegsMode fp_mode, Register exclusion1 = no_reg, ++ Register exclusion2 = no_reg, ++ Register exclusion3 = no_reg); ++ ++ void pop(Register dst) { ++ Ldl(dst, MemOperand(sp, 0)); ++ Addl(sp, sp, Operand(kPointerSize)); ++ } ++ void Pop(Register dst) { pop(dst); } ++ ++ // Pop two registers. Pops rightmost register first (from lower address). ++ void Pop(Register src1, Register src2) { ++ DCHECK(src1 != src2); ++ Ldl(src2, MemOperand(sp, 0 * kPointerSize)); ++ Ldl(src1, MemOperand(sp, 1 * kPointerSize)); ++ Addl(sp, sp, 2 * kPointerSize); ++ } ++ ++ // Pop three registers. Pops rightmost register first (from lower address). ++ void Pop(Register src1, Register src2, Register src3) { ++ Ldl(src3, MemOperand(sp, 0 * kPointerSize)); ++ Ldl(src2, MemOperand(sp, 1 * kPointerSize)); ++ Ldl(src1, MemOperand(sp, 2 * kPointerSize)); ++ Addl(sp, sp, 3 * kPointerSize); ++ } ++ ++ // Pop four registers. Pops rightmost register first (from lower address). ++ void Pop(Register src1, Register src2, Register src3, Register src4) { ++ Ldl(src4, MemOperand(sp, 0 * kPointerSize)); ++ Ldl(src3, MemOperand(sp, 1 * kPointerSize)); ++ Ldl(src2, MemOperand(sp, 2 * kPointerSize)); ++ Ldl(src1, MemOperand(sp, 3 * kPointerSize)); ++ Addl(sp, sp, 4 * kPointerSize); ++ } ++ ++ void Pop(uint32_t count = 1) { Addl(sp, sp, Operand(count * kPointerSize)); } ++ ++ // Pops multiple values from the stack and load them in the ++ // registers specified in regs. Pop order is the opposite as in MultiPush. ++ void MultiPop(RegList regs); ++ void MultiPopFPU(DoubleRegList regs); ++ void MultiPopSWSA(DoubleRegList regs); ++ ++#define DEFINE_INSTRUCTION(instr) \ ++ void instr(Register rd, Register rs, const Operand& rt); \ ++ void instr(Register rd, Register rs, Register rt) { \ ++ instr(rd, rs, Operand(rt)); \ ++ } \ ++ void instr(Register rs, Register rt, int32_t j) { instr(rs, rt, Operand(j)); } ++ ++#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, int32_t j) { instr(rs, Operand(j)); } ++ ++ DEFINE_INSTRUCTION(Addw) ++ DEFINE_INSTRUCTION(Addl) ++ DEFINE_INSTRUCTION(Divw) ++ DEFINE_INSTRUCTION(Divwu) ++ DEFINE_INSTRUCTION(Divl) ++ DEFINE_INSTRUCTION(Divlu) ++ DEFINE_INSTRUCTION(Modw) ++ DEFINE_INSTRUCTION(Modwu) ++ DEFINE_INSTRUCTION(Modl) ++ DEFINE_INSTRUCTION(Modlu) ++ DEFINE_INSTRUCTION(Subw) ++ DEFINE_INSTRUCTION(Subl) ++ DEFINE_INSTRUCTION(Dmodu) ++ DEFINE_INSTRUCTION(Mulw) ++ DEFINE_INSTRUCTION(Mulwh) ++ DEFINE_INSTRUCTION(Mulwhu) ++ DEFINE_INSTRUCTION(Mull) ++ DEFINE_INSTRUCTION(Mullh) ++ DEFINE_INSTRUCTION(Sllw) ++ DEFINE_INSTRUCTION(Srlw) ++ DEFINE_INSTRUCTION(Sraw) ++ ++ DEFINE_INSTRUCTION(And) ++ DEFINE_INSTRUCTION(Or) ++ DEFINE_INSTRUCTION(Xor) ++ DEFINE_INSTRUCTION(Nor) ++ DEFINE_INSTRUCTION2(Neg) ++ ++ DEFINE_INSTRUCTION(Cmplt) ++ DEFINE_INSTRUCTION(Cmpult) ++ DEFINE_INSTRUCTION(Cmple) ++ DEFINE_INSTRUCTION(Cmpule) ++ DEFINE_INSTRUCTION(Cmpgt) ++ DEFINE_INSTRUCTION(Cmpugt) ++ DEFINE_INSTRUCTION(Cmpge) ++ DEFINE_INSTRUCTION(Cmpuge) ++ ++ DEFINE_INSTRUCTION(Rolw) ++ DEFINE_INSTRUCTION(Roll) ++ ++#undef DEFINE_INSTRUCTION ++#undef DEFINE_INSTRUCTION2 ++#undef DEFINE_INSTRUCTION3 ++ ++ void SmiTag(Register dst, Register src) { ++ STATIC_ASSERT(kSmiTag == 0); ++ if (SmiValuesAre32Bits()) { ++ slll(src, 32, dst); ++ } else { ++ DCHECK(SmiValuesAre31Bits()); ++ Addw(dst, src, src); ++ } ++ } ++ ++ void SmiTag(Register reg) { SmiTag(reg, reg); } ++ ++ void SmiUntag(Register dst, const MemOperand& src); ++ void SmiUntag(Register dst, Register src) { ++ if (SmiValuesAre32Bits()) { ++ sral(src, kSmiShift, dst); ++ } else { ++ DCHECK(SmiValuesAre31Bits()); ++ Sraw(dst, src, kSmiShift); ++ } ++ } ++ ++ void SmiUntag(Register reg) { SmiUntag(reg, reg); } ++ ++ // Left-shifted from int32 equivalent of Smi. ++ void SmiScale(Register dst, Register src, int scale) { ++ if (SmiValuesAre32Bits()) { ++ // The int portion is upper 32-bits of 64-bit word. ++ sral(src, kSmiShift - scale, dst); ++ } else { ++ DCHECK(SmiValuesAre31Bits()); ++ DCHECK_GE(scale, kSmiTagSize); ++ sllw(src, scale - kSmiTagSize, dst); ++ } ++ } ++ ++ // On SW64, we should sign-extend 32-bit values. ++ void SmiToInt32(Register smi) { ++ if (FLAG_enable_slow_asserts) { ++ AssertSmi(smi); ++ } ++ DCHECK(SmiValuesAre32Bits() || SmiValuesAre31Bits()); ++ SmiUntag(smi); ++ } ++ ++ // Abort execution if argument is a smi, enabled via --debug-code. ++ void AssertNotSmi(Register object); ++ void AssertSmi(Register object); ++ ++ int CalculateStackPassedWords(int num_reg_arguments, ++ int num_double_arguments); ++ ++ // Before calling a C-function from generated code, align arguments on stack ++ // and add space for the four sw64 argument slots. ++ // After aligning the frame, non-register arguments must be stored on the ++ // stack, after the argument-slots using helper: CFunctionArgumentOperand(). ++ // The argument count assumes all arguments are word sized. ++ // Some compilers/platforms require the stack to be aligned when calling ++ // C++ code. ++ // Needs a scratch register to do some arithmetic. This register will be ++ // trashed. ++ void PrepareCallCFunction(int num_reg_arguments, int num_double_registers, ++ Register scratch); ++ void PrepareCallCFunction(int num_reg_arguments, Register scratch); ++ ++ // Arguments 1-4 are placed in registers a0 through a3 respectively. ++ // Arguments 5..n are stored to stack using following: ++ // Sw(a4, CFunctionArgumentOperand(5)); ++ ++ // Calls a C function and cleans up the space for arguments allocated ++ // by PrepareCallCFunction. The called function is not allowed to trigger a ++ // garbage collection, since that might move the code and invalidate the ++ // return address (unless this is somehow accounted for by the called ++ // function). ++ void CallCFunction(ExternalReference function, int num_arguments); ++ void CallCFunction(Register function, int num_arguments); ++ void CallCFunction(ExternalReference function, int num_reg_arguments, ++ int num_double_arguments); ++ void CallCFunction(Register function, int num_reg_arguments, ++ int num_double_arguments); ++ void MovFromFloatResult(DoubleRegister dst); ++ void MovFromFloatParameter(DoubleRegister dst); ++ ++ // There are two ways of passing double arguments on SW64, depending on ++ // whether soft or hard floating point ABI is used. These functions ++ // abstract parameter passing for the three different ways we call ++ // C functions from generated code. ++ void MovToFloatParameter(DoubleRegister fsrc); ++ void MovToFloatParameters(DoubleRegister fsrc0, DoubleRegister fsrc1); ++ void MovToFloatResult(DoubleRegister fsrc); ++ ++ void MovFromGeneralResult(Register dst); ++ void MovFromGeneralParameter(Register dst); ++ void MovToGeneralParameter(Register src); ++ void MovToGeneralParameters(Register src0, Register src1); ++ void MovToGeneralResult(Register src); ++ ++ // See comments at the beginning of Builtins::Generate_CEntry. ++ inline void PrepareCEntryArgs(int num_args) { li(a0, num_args); } ++ inline void PrepareCEntryFunction(const ExternalReference& ref) { ++ li(a1, ref); ++ } ++ ++ void CheckPageFlag(Register object, Register scratch, int mask, Condition cc, ++ Label* condition_met); ++#undef COND_ARGS ++ ++ // Performs a truncating conversion of a floating point number as used by ++ // the JS bitwise operations. See ECMA-262 9.5: ToInt32. ++ // Exits with 'result' holding the answer. ++ void TruncateDoubleToI(Isolate* isolate, Zone* zone, Register result, ++ DoubleRegister double_input, StubCallMode stub_mode); ++ ++ // Conditional move. ++ void Seleq(Register rd, Register rs, Register rt); ++ void Selne(Register rd, Register rs, Register rt); ++ ++ void LoadZeroIfFPUCondition(Register dest); ++ void LoadZeroIfNotFPUCondition(Register dest); ++ ++ void LoadZeroIfConditionNotZero(Register dest, Register condition); ++ void LoadZeroIfConditionZero(Register dest, Register condition); ++ void LoadZeroOnCondition(Register rd, Register rs, const Operand& rt, ++ Condition cond); ++ ++ void Ctlzw(Register rd, Register rs); ++ void Ctlzl(Register rd, Register rs); ++ void Cttzw(Register rd, Register rs); ++ void Cttzl(Register rd, Register rs); ++ void Ctpopw(Register rd, Register rs); ++ void Ctpopl(Register rd, Register rs); ++ ++ // SW64 R2 instruction macro. ++ void Extw(Register rt, Register rs, uint16_t pos, uint16_t size); ++ void Extl(Register rt, Register rs, uint16_t pos, uint16_t size); ++ void Insw(Register rt, Register rs, uint16_t pos, uint16_t size); ++ void Insd(Register rt, Register rs, uint16_t pos, uint16_t size); ++ void ExtractBits(Register dest, Register source, Register pos, int size, ++ bool sign_extend = false); ++ void InsertBits(Register dest, Register source, Register pos, int size); ++ void Fneg(FPURegister fd, FPURegister fs); ++ ++ // SW64 R6 instruction macros. ++ void Bovc(Register rt, Register rs, Label* L); ++ void Bnvc(Register rt, Register rs, Label* L); ++ ++ // Convert single to unsigned word. ++ void Trunc_uw_s(FPURegister fd, FPURegister fs, FPURegister scratch); ++ void Trunc_uw_s(Register rd, FPURegister fs, FPURegister scratch); ++ ++ // Change endianness ++ void ByteSwapSigned(Register dest, Register src, int operand_size); ++ void ByteSwapUnsigned(Register dest, Register src, int operand_size); ++ ++ void Ldh_u(Register rd, const MemOperand& rs); ++ void Ldhu_u(Register rd, const MemOperand& rs); ++ void Sth_u(Register rd, const MemOperand& rs, Register scratch); ++ ++ void Ldw_u(Register rd, const MemOperand& rs); ++ void Ldwu_u(Register rd, const MemOperand& rs); ++ void Stw_u(Register rd, const MemOperand& rs); ++ ++ void Ldl_u(Register rd, const MemOperand& rs); ++ void Stl_u(Register rd, const MemOperand& rs); ++ ++ void Flds_u(FPURegister fd, const MemOperand& rs, Register scratch); ++ void Fsts_u(FPURegister fd, const MemOperand& rs, Register scratch); ++ ++ void Fldd_u(FPURegister fd, const MemOperand& rs, Register scratch); ++ void Fstd_u(FPURegister fd, const MemOperand& rs, Register scratch); ++ ++ void Ldb(Register rd, const MemOperand& rs); ++ void Ldbu(Register rd, const MemOperand& rs); ++ void Stb(Register rd, const MemOperand& rs); ++ ++ void Ldh(Register rd, const MemOperand& rs); ++ void Ldhu(Register rd, const MemOperand& rs); ++ void Sth(Register rd, const MemOperand& rs); ++ ++ void Ldw(Register rd, const MemOperand& rs); ++ void Ldwu(Register rd, const MemOperand& rs); ++ void Stw(Register rd, const MemOperand& rs); ++ ++ void Flds(FPURegister fd, const MemOperand& src); ++ void Fsts(FPURegister fs, const MemOperand& dst); ++ ++ void Fldd(FPURegister fd, const MemOperand& src); ++ void Fstd(FPURegister fs, const MemOperand& dst); ++ ++ void Ll(Register rd, const MemOperand& rs); ++ void Sc(Register rd, const MemOperand& rs); ++ ++ void Lld(Register rd, const MemOperand& rs); ++ void Scd(Register rd, const MemOperand& rs); ++ void Fabs(FPURegister fd, FPURegister fs); ++ // Perform a floating-point min or max operation with the ++ // (IEEE-754-compatible) semantics of SW32's Release 6 MIN.fmt/MAX.fmt. ++ // Some cases, typically NaNs or +/-0.0, are expected to be rare and are ++ // handled in out-of-line code. The specific behaviour depends on supported ++ // instructions. ++ // ++ // These functions assume (and assert) that src1!=src2. It is permitted ++ // for the result to alias either input register. ++ void Float32Max(FPURegister dst, FPURegister src1, FPURegister src2, ++ Label* out_of_line); ++ void Float32Min(FPURegister dst, FPURegister src1, FPURegister src2, ++ Label* out_of_line); ++ void Float64Max(FPURegister dst, FPURegister src1, FPURegister src2, ++ Label* out_of_line); ++ void Float64Min(FPURegister dst, FPURegister src1, FPURegister src2, ++ Label* out_of_line); ++ ++ // Generate out-of-line cases for the macros above. ++ void Float32MaxOutOfLine(FPURegister dst, FPURegister src1, FPURegister src2); ++ void Float32MinOutOfLine(FPURegister dst, FPURegister src1, FPURegister src2); ++ void Float64MaxOutOfLine(FPURegister dst, FPURegister src1, FPURegister src2); ++ void Float64MinOutOfLine(FPURegister dst, FPURegister src1, FPURegister src2); ++ ++ bool IsDoubleZeroRegSet() { return has_double_zero_reg_set_; } ++ ++ void mov(Register rd, Register rt) { ++ if (rt != rd) bis(zero_reg, rt, rd); ++ } ++ ++ inline void Move(Register dst, Handle handle) { li(dst, handle); } ++ inline void Move(Register dst, Smi smi) { li(dst, Operand(smi)); } ++ ++ inline void Move(Register dst, Register src) { ++ if (dst != src) { ++ mov(dst, src); ++ } ++ } ++ ++ inline void Move(FPURegister dst, FPURegister src) { Move_d(dst, src); } ++ ++ inline void Move(Register dst_low, Register dst_high, FPURegister src) { ++ fimovd(src, dst_low); ++ sral(dst_low, 32, dst_high); ++ addw(dst_low, 0, dst_low); ++ } ++ ++ inline void Move(Register dst, FPURegister src) { fimovd(src, dst); } ++ ++ inline void Move(FPURegister dst, Register src) { ifmovd(src, dst); } ++ ++ inline void FmoveHigh(Register dst_high, FPURegister src) { ++ fimovd(src, dst_high); ++ sral(dst_high, 32, dst_high); ++ } ++ ++ inline void FmoveHigh(FPURegister dst, Register src_high) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ Register scratch1 = t8; ++ DCHECK(src_high != scratch && src_high != scratch1); ++ fimovd(dst, scratch); ++ zapnot(scratch, 0xf, scratch); ++ slll(src_high, 32, scratch1); ++ or_(scratch, scratch1, scratch); ++ ifmovd(scratch, dst); ++ } ++ ++ inline void FmoveLow(Register dst_low, FPURegister src) { ++ fimovd(src, dst_low); ++ addw(dst_low, 0, dst_low); ++ } ++ ++ void FmoveLow(FPURegister dst, Register src_low); ++ ++ inline void Move(FPURegister dst, Register src_low, Register src_high) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ DCHECK(src_high != scratch && src_low != scratch); ++ slll(src_high, 32, scratch); ++ or_(scratch, src_low, scratch); ++ ifmovd(scratch, dst); ++ } ++ ++ inline void Move_d(FPURegister dst, FPURegister src) { ++ if (dst != src) { ++ fmovd(src, dst); ++ } ++ } ++ ++ inline void Move_s(FPURegister dst, FPURegister src) { ++ if (dst != src) { ++ fmovs(src, dst); ++ } ++ } ++ ++ void Move(FPURegister dst, float imm) { Move(dst, bit_cast(imm)); } ++ void Move(FPURegister dst, double imm) { Move(dst, bit_cast(imm)); } ++ void Move(FPURegister dst, uint32_t src); ++ void Move(FPURegister dst, uint64_t src); ++ ++ // DaddOverflow sets overflow register to a negative value if ++ // overflow occured, otherwise it is zero or positive ++ void DaddOverflow(Register dst, Register left, const Operand& right, ++ Register overflow); ++ // SublOverflow sets overflow register to a negative value if ++ // overflow occured, otherwise it is zero or positive ++ void SublOverflow(Register dst, Register left, const Operand& right, ++ Register overflow); ++ // MulwOverflow sets overflow register to zero if no overflow occured ++ void MulwOverflow(Register dst, Register left, const Operand& right, ++ Register overflow); ++ ++ // Number of instructions needed for calculation of switch table entry address ++ static const int kSwitchTablePrologueSize = 5; // 4 + 1 (ALIGN may 1 nop) ++ ++ // GetLabelFunction must be lambda '[](size_t index) -> Label*' or a ++ // functor/function with 'Label *func(size_t index)' declaration. ++ template ++ void GenerateSwitchTable(Register index, size_t case_count, ++ Func GetLabelFunction); ++ ++ // Load an object from the root table. ++ void LoadRoot(Register destination, RootIndex index) final; ++ void LoadRoot(Register destination, RootIndex index, Condition cond, ++ Register src1, const Operand& src2); ++ ++ void LoadMap(Register destination, Register object); ++ ++ // If the value is a NaN, canonicalize the value else, do nothing. ++ void FPUCanonicalizeNaN(const DoubleRegister dst, const DoubleRegister src); ++ ++ // --------------------------------------------------------------------------- ++ // FPU macros. These do not handle special cases like NaN or +- inf. ++ ++ // Convert unsigned word to double. ++ void Cvt_d_uw(FPURegister fd, FPURegister fs); ++ void Cvt_d_uw(FPURegister fd, Register rs); ++ ++ // Convert unsigned long to double. ++ void Cvt_d_ul(FPURegister fd, FPURegister fs); ++ void Cvt_d_ul(FPURegister fd, Register rs); ++ ++ // Convert unsigned word to float. ++ void Cvt_s_uw(FPURegister fd, FPURegister fs); ++ void Cvt_s_uw(FPURegister fd, Register rs); ++ ++ // Convert unsigned long to float. ++ void Cvt_s_ul(FPURegister fd, FPURegister fs); ++ void Cvt_s_ul(FPURegister fd, Register rs); ++ ++ // Convert double to unsigned word. ++ void Trunc_uw_d(FPURegister fd, FPURegister fs, FPURegister scratch); ++ void Trunc_uw_d(Register rd, FPURegister fs, FPURegister scratch); ++ ++ // Convert double to unsigned long. ++ void Trunc_ul_d(FPURegister fd, FPURegister fs, FPURegister scratch, ++ Register result = no_reg); ++ void Trunc_ul_d(Register rd, FPURegister fs, FPURegister scratch, ++ Register result = no_reg); ++ ++ // Convert single to unsigned long. ++ void Trunc_ul_s(FPURegister fd, FPURegister fs, FPURegister scratch, ++ Register result = no_reg); ++ void Trunc_ul_s(Register rd, FPURegister fs, FPURegister scratch, ++ Register result = no_reg); ++ ++ // Round double functions ++ void Truncd(FPURegister fd, FPURegister fs); ++ void Roundd(FPURegister fd, FPURegister fs); ++ void Floord(FPURegister fd, FPURegister fs); ++ void Ceild(FPURegister fd, FPURegister fs); ++ ++//use fridx/frisx intructions in CORE4 ++ void Fridn_d_d(FPURegister fd, FPURegister fs); ++ void Frisn_s_s(FPURegister fd, FPURegister fs); ++ void Fridz_d_d(FPURegister fd, FPURegister fs); ++ void Frisz_s_s(FPURegister fd, FPURegister fs); ++ void Fridp_d_d(FPURegister fd, FPURegister fs); ++ void Frisp_s_s(FPURegister fd, FPURegister fs); ++ void Fridg_d_d(FPURegister fd, FPURegister fs); ++ void Frisg_s_s(FPURegister fd, FPURegister fs); ++ ++ // Round float functions ++ void Truncs(FPURegister fd, FPURegister fs); ++ void Rounds(FPURegister fd, FPURegister fs); ++ void Floors(FPURegister fd, FPURegister fs); ++ void Ceils(FPURegister fd, FPURegister fs); ++ ++ void SWSARoundW(SWSARegister dst, SWSARegister src, FPURoundingMode mode); ++ void SWSARoundD(SWSARegister dst, SWSARegister src, FPURoundingMode mode); ++ ++ // Jump the register contains a smi. ++ void JumpIfSmi(Register value, Label* smi_label); ++ ++ void JumpIfEqual(Register a, int32_t b, Label* dest) { ++ li(kScratchReg, Operand(b)); ++ Branch(dest, eq, a, Operand(kScratchReg)); ++ } ++ ++ void JumpIfLessThan(Register a, int32_t b, Label* dest) { ++ li(kScratchReg, Operand(b)); ++ Branch(dest, lt, a, Operand(kScratchReg)); ++ } ++ ++ // Push a standard frame, consisting of ra, fp, context and JS function. ++ void PushStandardFrame(Register function_reg); ++ ++ // Get the actual activation frame alignment for target environment. ++ static int ActivationFrameAlignment(); ++ ++ // Load Scaled Address instructions. Parameter sa (shift argument) must be ++ // between [1, 31] (inclusive). On pre-r6 architectures the scratch register ++ // may be clobbered. ++ void Lsaw(Register rd, Register rs, Register rt, uint8_t sa, ++ Register scratch = at); ++ void Lsal(Register rd, Register rs, Register rt, uint8_t sa, ++ Register scratch = at); ++ ++ // Compute the start of the generated instruction stream from the current PC. ++ // This is an alternative to embedding the {CodeObject} handle as a reference. ++ void ComputeCodeStartAddress(Register dst); ++ ++ // Control-flow integrity: ++ ++ // Define a function entrypoint. This doesn't emit any code for this ++ // architecture, as control-flow integrity is not supported for it. ++ void CodeEntry() {} ++ // Define an exception handler. ++ void ExceptionHandler() {} ++ // Define an exception handler and bind a label. ++ void BindExceptionHandler(Label* label) { bind(label); } ++ ++ protected: ++ inline Register GetRtAsRegisterHelper(const Operand& rt, Register scratch); ++ inline int32_t GetOffset(int32_t offset, Label* L, OffsetSize bits); ++ ++ private: ++ bool has_double_zero_reg_set_ = false; ++ ++ // Performs a truncating conversion of a floating point number as used by ++ // the JS bitwise operations. See ECMA-262 9.5: ToInt32. Goes to 'done' if it ++ // succeeds, otherwise falls through if result is saturated. On return ++ // 'result' either holds answer, or is clobbered on fall through. ++ void TryInlineTruncateDoubleToI(Register result, DoubleRegister input, ++ Label* done); ++ ++ void CallCFunctionHelper(Register function, int num_reg_arguments, ++ int num_double_arguments); ++ ++ // TODO(mips) Reorder parameters so out parameters come last. ++ bool CalculateOffset(Label* L, int32_t* offset, OffsetSize bits); ++ bool CalculateOffset(Label* L, int32_t* offset, OffsetSize bits, ++ Register* scratch, const Operand& rt); ++ ++ void BranchShortHelper(int32_t offset, Label* L); ++ void BranchShortHelper(int16_t offset, Label* L); ++ ++ bool BranchShortHelper(int32_t offset, Label* L, Condition cond, ++ Register rs, const Operand& rt); ++ bool BranchShortHelper(int16_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 BranchAndLinkShortHelper(int32_t offset, Label* L); ++ void BranchAndLinkShort(int32_t offset); ++ void BranchAndLinkShort(Label* L); ++ bool BranchAndLinkShortHelper(int32_t offset, Label* L, Condition cond, ++ Register rs, const Operand& rt); ++ bool BranchAndLinkShortHelper(int16_t offset, Label* L, Condition cond, ++ Register rs, const Operand& rt); ++ bool BranchAndLinkShortCheck(int32_t offset, Label* L, Condition cond, Register rs, const Operand& rt); ++ void BranchLong(Label* L); ++ void BranchAndLinkLong(Label* L); ++ ++ template ++ void RoundDouble(FPURegister dst, FPURegister src, FPURoundingMode mode, ++ RoundFunc round); ++ ++ template ++ void RoundFloat(FPURegister dst, FPURegister src, FPURoundingMode mode, ++ RoundFunc round); ++ ++ // Push a fixed frame, consisting of ra, fp. ++ void PushCommonFrame(Register marker_reg = no_reg); ++}; ++ ++// MacroAssembler implements a collection of frequently used macros. ++class V8_EXPORT_PRIVATE MacroAssembler : public TurboAssembler { ++ public: ++ using TurboAssembler::TurboAssembler; ++ ++ // It assumes that the arguments are located below the stack pointer. ++ // argc is the number of arguments not including the receiver. ++ // TODO(victorgomes): Remove this function once we stick with the reversed ++ // arguments order. ++ void LoadReceiver(Register dest, Register argc) { ++ Ldl(dest, MemOperand(sp, 0)); ++ } ++ ++ void StoreReceiver(Register rec, Register argc, Register scratch) { ++ Stl(rec, MemOperand(sp, 0)); ++ } ++ ++ bool IsNear(Label* L, Condition cond, int rs_reg); ++ ++ // Swap two registers. If the scratch register is omitted then a slightly ++ // less efficient form using xor instead of mov is emitted. ++ void Swap(Register reg1, Register reg2, Register scratch = no_reg); ++ ++ void PushRoot(RootIndex index) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ LoadRoot(scratch, index); ++ Push(scratch); ++ } ++ ++ // Compare the object in a register to a value and jump if they are equal. ++ void JumpIfRoot(Register with, RootIndex index, Label* if_equal) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ LoadRoot(scratch, index); ++ Branch(if_equal, eq, with, Operand(scratch)); ++ } ++ ++ // Compare the object in a register to a value and jump if they are not equal. ++ void JumpIfNotRoot(Register with, RootIndex index, Label* if_not_equal) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ LoadRoot(scratch, index); ++ Branch(if_not_equal, ne, with, Operand(scratch)); ++ } ++ ++ // Checks if value is in range [lower_limit, higher_limit] using a single ++ // comparison. ++ void JumpIfIsInRange(Register value, unsigned lower_limit, ++ unsigned higher_limit, Label* on_in_range); ++ ++ // --------------------------------------------------------------------------- ++ // GC Support ++ ++ // Notify the garbage collector that we wrote a pointer into an object. ++ // |object| is the object being stored into, |value| is the object being ++ // stored. value and scratch registers are clobbered by the operation. ++ // The offset is the offset from the start of the object, not the offset from ++ // the tagged HeapObject pointer. For use with FieldOperand(reg, off). ++ void RecordWriteField( ++ Register object, int offset, Register value, Register scratch, ++ RAStatus ra_status, SaveFPRegsMode save_fp, ++ RememberedSetAction remembered_set_action = RememberedSetAction::kEmit, ++ SmiCheck smi_check = SmiCheck::kInline); ++ ++ // For a given |object| notify the garbage collector that the slot |address| ++ // has been written. |value| is the object being stored. The value and ++ // address registers are clobbered by the operation. ++ void RecordWrite( ++ Register object, Register address, Register value, RAStatus ra_status, ++ SaveFPRegsMode save_fp, ++ RememberedSetAction remembered_set_action = RememberedSetAction::kEmit, ++ SmiCheck smi_check = SmiCheck::kInline); ++ ++ void Pref(int32_t hint, const MemOperand& rs); ++ ++ // --------------------------------------------------------------------------- ++ // Pseudo-instructions. ++ ++ // Convert double to unsigned long. ++ void Trunc_l_ud(FPURegister fd, FPURegister fs, FPURegister scratch); ++ ++ void Trunc_l_d(FPURegister fd, FPURegister fs); ++ void Round_l_d(FPURegister fd, FPURegister fs); ++ void Floor_l_d(FPURegister fd, FPURegister fs); ++ void Ceil_l_d(FPURegister fd, FPURegister fs); ++ ++ void Trunc_w_d(FPURegister fd, FPURegister fs); ++ void Round_w_d(FPURegister fd, FPURegister fs); ++ void Floor_w_d(FPURegister fd, FPURegister fs); ++ void Ceil_w_d(FPURegister fd, FPURegister fs); ++ ++ void Madd_s(FPURegister fd, FPURegister fr, FPURegister fs, FPURegister ft, ++ FPURegister scratch); ++ void Madd_d(FPURegister fd, FPURegister fr, FPURegister fs, FPURegister ft, ++ FPURegister scratch); ++ void Msub_s(FPURegister fd, FPURegister fr, FPURegister fs, FPURegister ft, ++ FPURegister scratch); ++ void Msub_d(FPURegister fd, FPURegister fr, FPURegister fs, FPURegister ft, ++ FPURegister scratch); ++ ++ // Enter exit frame. ++ // argc - argument count to be dropped by LeaveExitFrame. ++ // save_doubles - saves FPU registers on stack, currently disabled. ++ // stack_space - extra stack space. ++ void EnterExitFrame(bool save_doubles, int stack_space = 0, ++ StackFrame::Type frame_type = StackFrame::EXIT); ++ ++ // Leave the current exit frame. ++ void LeaveExitFrame(bool save_doubles, Register arg_count, ++ bool do_return = NO_EMIT_RETURN, ++ bool argument_count_is_length = false); ++ ++ // Make sure the stack is aligned. Only emits code in debug mode. ++ void AssertStackIsAligned(); ++ ++ // Load the global proxy from the current context. ++ void LoadGlobalProxy(Register dst) { ++ LoadNativeContextSlot(dst, Context::GLOBAL_PROXY_INDEX); ++ } ++ ++ void LoadNativeContextSlot(Register dst, int index); ++ ++ // Load the initial map from the global function. The registers ++ // function and map can be the same, function is then overwritten. ++ void LoadGlobalFunctionInitialMap(Register function, Register map, ++ Register scratch); ++ ++ // ------------------------------------------------------------------------- ++ // JavaScript invokes. ++ ++ // Invoke the JavaScript function code by either calling or jumping. ++ void InvokeFunctionCode(Register function, Register new_target, ++ Register expected_parameter_count, ++ Register actual_parameter_count, InvokeType type); ++ ++ // On function call, call into the debugger if necessary. ++ void CheckDebugHook(Register fun, Register new_target, ++ Register expected_parameter_count, ++ Register actual_parameter_count); ++ ++ // Invoke the JavaScript function in the given register. Changes the ++ // current context to the context in the function before invoking. ++ void InvokeFunctionWithNewTarget(Register function, Register new_target, ++ Register actual_parameter_count, ++ InvokeType type); ++ void InvokeFunction(Register function, Register expected_parameter_count, ++ Register actual_parameter_count, InvokeType type); ++ ++ // Exception handling. ++ ++ // Push a new stack handler and link into stack handler chain. ++ void PushStackHandler(); ++ ++ // Unlink the stack handler on top of the stack from the stack handler chain. ++ // Must preserve the result register. ++ void PopStackHandler(); ++ ++ // ------------------------------------------------------------------------- ++ // Support functions. ++ ++ void GetObjectType(Register function, Register map, Register type_reg); ++ ++ void GetInstanceTypeRange(Register map, Register type_reg, ++ InstanceType lower_limit, Register range); ++ ++ // ------------------------------------------------------------------------- ++ // Runtime calls. ++ ++ // Call a runtime routine. ++ void CallRuntime(const Runtime::Function* f, int num_arguments, ++ SaveFPRegsMode save_doubles = SaveFPRegsMode::kIgnore); ++ ++ // Convenience function: Same as above, but takes the fid instead. ++ void CallRuntime(Runtime::FunctionId fid, ++ SaveFPRegsMode save_doubles = SaveFPRegsMode::kIgnore) { ++ const Runtime::Function* function = Runtime::FunctionForId(fid); ++ CallRuntime(function, function->nargs, save_doubles); ++ } ++ ++ // Convenience function: Same as above, but takes the fid instead. ++ void CallRuntime(Runtime::FunctionId fid, int num_arguments, ++ SaveFPRegsMode save_doubles = SaveFPRegsMode::kIgnore) { ++ CallRuntime(Runtime::FunctionForId(fid), num_arguments, save_doubles); ++ } ++ ++ // Convenience function: tail call a runtime routine (jump). ++ void TailCallRuntime(Runtime::FunctionId fid); ++ ++ // Jump to the builtin routine. ++ void JumpToExternalReference(const ExternalReference& builtin, ++ bool builtin_exit_frame = false); ++ ++ // Generates a trampoline to jump to the off-heap instruction stream. ++ void JumpToOffHeapInstructionStream(Address entry); ++ ++ // --------------------------------------------------------------------------- ++ // In-place weak references. ++ void LoadWeakValue(Register out, Register in, Label* target_if_cleared); ++ ++ // ------------------------------------------------------------------------- ++ // StatsCounter support. ++ ++ void IncrementCounter(StatsCounter* counter, int value, Register scratch1, ++ Register scratch2) { ++ if (!FLAG_native_code_counters) return; ++ EmitIncrementCounter(counter, value, scratch1, scratch2); ++ } ++ void EmitIncrementCounter(StatsCounter* counter, int value, Register scratch1, ++ Register scratch2); ++ void DecrementCounter(StatsCounter* counter, int value, Register scratch1, ++ Register scratch2) { ++ if (!FLAG_native_code_counters) return; ++ EmitDecrementCounter(counter, value, scratch1, scratch2); ++ } ++ void EmitDecrementCounter(StatsCounter* counter, int value, Register scratch1, ++ Register scratch2); ++ ++ // ------------------------------------------------------------------------- ++ // Stack limit utilities ++ ++ enum StackLimitKind { kInterruptStackLimit, kRealStackLimit }; ++ void LoadStackLimit(Register destination, StackLimitKind kind); ++ void StackOverflowCheck(Register num_args, Register scratch1, ++ Register scratch2, Label* stack_overflow); ++ ++ // --------------------------------------------------------------------------- ++ // Smi utilities. ++ ++ // Test if the register contains a smi. ++ inline void SmiTst(Register value, Register scratch) { ++ And(scratch, value, Operand(kSmiTagMask)); ++ } ++ ++ // Jump if the register contains a non-smi. ++ void JumpIfNotSmi(Register value, Label* not_smi_label); ++ ++ // Abort execution if argument is not a Constructor, enabled via --debug-code. ++ void AssertConstructor(Register object); ++ ++ // Abort execution if argument is not a JSFunction, enabled via --debug-code. ++ void AssertFunction(Register object); ++ ++ // Abort execution if argument is not a callable JSFunction, enabled via ++ // --debug-code. ++ void AssertCallableFunction(Register object); ++ ++ // Abort execution if argument is not a JSBoundFunction, ++ // enabled via --debug-code. ++ void AssertBoundFunction(Register object); ++ ++ // Abort execution if argument is not a JSGeneratorObject (or subclass), ++ // enabled via --debug-code. ++ void AssertGeneratorObject(Register object); ++ ++ // Abort execution if argument is not undefined or an AllocationSite, enabled ++ // via --debug-code. ++ void AssertUndefinedOrAllocationSite(Register object, Register scratch); ++ ++ template ++ void DecodeField(Register dst, Register src) { ++ Extw(dst, src, Field::kShift, Field::kSize); ++ } ++ ++ template ++ void DecodeField(Register reg) { ++ DecodeField(reg, reg); ++ } ++ ++ private: ++ // Helper functions for generating invokes. ++ void InvokePrologue(Register expected_parameter_count, ++ Register actual_parameter_count, Label* done, ++ InvokeType type); ++ ++ DISALLOW_IMPLICIT_CONSTRUCTORS(MacroAssembler); ++}; ++ ++template ++void TurboAssembler::GenerateSwitchTable(Register index, size_t case_count, ++ Func GetLabelFunction) { ++ // Ensure that dd-ed labels following this instruction use 8 bytes aligned ++ // addresses. ++ BlockTrampolinePoolFor(static_cast(case_count) * 2 + ++ kSwitchTablePrologueSize); ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ Align(8); ++ int instr_num = 3; // Added 20190117 ++ br(scratch, 0); ++ s8addl(index, scratch, ++ scratch); // get_mem = cur_pc + index * 8 (kPointerSizeLog2); ++ Ldl(scratch, MemOperand(scratch, instr_num * v8::internal::kInstrSize)); ++ Assembler::jmp(zero_reg, scratch, 0); ++ for (size_t index = 0; index < case_count; ++index) { ++ dd(GetLabelFunction(index)); ++ } ++} ++ ++#define ACCESS_MASM(masm) masm-> ++ ++} // namespace internal ++} // namespace v8 ++ ++#endif // V8_CODEGEN_SW64_MACRO_ASSEMBLER_SW64_H_ +diff --git a/deps/v8/src/codegen/sw64/register-sw64.h b/deps/v8/src/codegen/sw64/register-sw64.h +new file mode 100644 +index 00000000..d06011c8 +--- /dev/null ++++ b/deps/v8/src/codegen/sw64/register-sw64.h +@@ -0,0 +1,302 @@ ++// Copyright 2018 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef V8_CODEGEN_SW64_REGISTER_SW64_H_ ++#define V8_CODEGEN_SW64_REGISTER_SW64_H_ ++ ++#include "src/codegen/register-base.h" ++#include "src/codegen/sw64/constants-sw64.h" ++ ++namespace v8 { ++namespace internal { ++ ++// clang-format off ++#define GENERAL_REGISTERS(V) \ ++ V(v0) \ ++ V(t0) V(t1) V(t2) V(t3) V(t4) V(t5) V(t6) V(t7) \ ++ V(s0) V(s1) V(s2) V(s3) V(s4) V(s5) V(fp) \ ++ V(a0) V(a1) V(a2) V(a3) V(a4) V(a5) \ ++ V(t8) V(t9) V(t10) V(t11) \ ++ V(ra) \ ++ V(t12) \ ++ V(at) \ ++ V(gp) \ ++ V(sp) \ ++ V(zero_reg) ++ ++// t7, t8 used as two scratch regs instead of s3, s4; so they ++// should not be added to allocatable lists. ++#define ALLOCATABLE_GENERAL_REGISTERS(V) \ ++ V(a0) V(a1) V(a2) V(a3) V(a4) V(a5) \ ++ V(t0) V(t1) V(t2) V(t3) V(t5) V(t6) V(t9) V(t10) V(s5) \ ++ V(v0) V(t4) ++ ++#define DOUBLE_REGISTERS(V) \ ++ V(f0) V(f1) V(f2) V(f3) V(f4) V(f5) V(f6) V(f7) \ ++ V(f8) V(f9) V(f10) V(f11) V(f12) V(f13) V(f14) V(f15) \ ++ V(f16) V(f17) V(f18) V(f19) V(f20) V(f21) V(f22) V(f23) \ ++ V(f24) V(f25) V(f26) V(f27) V(f28) V(f29) V(f30) V(f31) ++ ++// Currently, SW64 just use even float point register, except ++// for C function param registers. ++//SKTODO ++#define DOUBLE_USE_REGISTERS(V) \ ++ V(f0) V(f2) V(f4) V(f6) V(f8) V(f10) V(f12) V(f13) \ ++ V(f14) V(f15) V(f16) V(f17) V(f18) V(f19) V(f20) V(f22) \ ++ V(f24) V(f26) V(f28) V(f30) ++ ++#define FLOAT_REGISTERS DOUBLE_REGISTERS ++#define SIMD128_REGISTERS(V) \ ++ V(w0) V(w1) V(w2) V(w3) V(w4) V(w5) V(w6) V(w7) \ ++ V(w8) V(w9) V(w10) V(w11) V(w12) V(w13) V(w14) V(w15) \ ++ V(w16) V(w17) V(w18) V(w19) V(w20) V(w21) V(w22) V(w23) \ ++ V(w24) V(w25) V(w26) V(w27) V(w28) V(w29) V(w30) V(w31) ++ ++/* f27-f30 scratch fregisters */ ++#define ALLOCATABLE_DOUBLE_REGISTERS(V) \ ++ V(f0) V(f1) V(f2) V(f3) V(f4) V(f5) V(f6) V(f7) \ ++ V(f8) V(f9) V(f10) V(f11) V(f12) V(f13) V(f14) V(f15) \ ++ V(f16) V(f17) V(f18) V(f19) V(f20) V(f21) V(f22) V(f23) \ ++ V(f24) V(f25) V(f26) ++// clang-format on ++ ++// Note that the bit values must match those used in actual instruction ++// encoding. ++const int kNumRegs = 32; ++ ++// CPU Registers. ++// ++// 1) We would prefer to use an enum, but enum values are assignment- ++// compatible with int, which has caused code-generation bugs. ++// ++// 2) We would prefer to use a class instead of a struct but we don't like ++// the register initialization to depend on the particular initialization ++// order (which appears to be different on OS X, Linux, and Windows for the ++// installed versions of C++ we tried). Using a struct permits C-style ++// "initialization". Also, the Register objects cannot be const as this ++// forces initialization stubs in MSVC, making us dependent on initialization ++// order. ++// ++// 3) By not using an enum, we are possibly preventing the compiler from ++// doing certain constant folds, which may significantly reduce the ++// code generated for some assembly instructions (because they boil down ++// to a few constants). If this is a problem, we could change the code ++// such that we use an enum in optimized mode, and the struct in debug ++// mode. This way we get the compile-time error checking in debug mode ++// and best performance in optimized code. ++ ++// ----------------------------------------------------------------------------- ++// Implementation of Register and FPURegister. ++ ++enum RegisterCode { ++#define REGISTER_CODE(R) kRegCode_##R, ++ GENERAL_REGISTERS(REGISTER_CODE) ++#undef REGISTER_CODE ++ kRegAfterLast ++}; ++ ++class Register : public RegisterBase { ++ public: ++ static constexpr int kMantissaOffset = 0; ++ static constexpr int kExponentOffset = 4; ++ ++ private: ++ friend class RegisterBase; ++ explicit constexpr Register(int code) : RegisterBase(code) {} ++}; ++ ++// s5: context register ++// t7: lithium scratch ++// t8: lithium scratch2 ++#define DECLARE_REGISTER(R) \ ++ constexpr Register R = Register::from_code(kRegCode_##R); ++GENERAL_REGISTERS(DECLARE_REGISTER) ++#undef DECLARE_REGISTER ++ ++constexpr Register no_reg = Register::no_reg(); ++ ++int ToNumber(Register reg); ++ ++Register ToRegister(int num); ++ ++// Returns the number of padding slots needed for stack pointer alignment. ++constexpr int ArgumentPaddingSlots(int argument_count) { ++ // No argument padding required. ++ return 0; ++} ++ ++constexpr AliasingKind kFPAliasing = AliasingKind::kOverlap; ++constexpr bool kSimdMaskRegisters = false; ++ ++enum DoubleRegisterCode { ++#define REGISTER_CODE(R) kDoubleCode_##R, ++ DOUBLE_REGISTERS(REGISTER_CODE) ++#undef REGISTER_CODE ++ kDoubleAfterLast ++}; ++ ++// Coprocessor register. ++class FPURegister : public RegisterBase { ++ public: ++ // TODO(plind): Warning, inconsistent numbering here. kNumFPURegisters refers ++ // to number of 32-bit FPU regs, but kNumAllocatableRegisters refers to ++ // number of Double regs (64-bit regs, or FPU-reg-pairs). ++ ++ private: ++ friend class RegisterBase; ++ explicit constexpr FPURegister(int code) : RegisterBase(code) {} ++}; ++ ++enum SWSARegisterCode { ++#define REGISTER_CODE(R) kSwsaCode_##R, ++ SIMD128_REGISTERS(REGISTER_CODE) ++#undef REGISTER_CODE ++ kSwsaAfterLast ++}; ++ ++// SW64 SIMD (SWSA) register ++class SWSARegister : public RegisterBase { ++ friend class RegisterBase; ++ explicit constexpr SWSARegister(int code) : RegisterBase(code) {} ++}; ++ ++// A few double registers are reserved: one as a scratch register and one to ++// hold 0.0. ++// f31: 0.0 ++// f30: scratch register. ++ ++// V8 now supports the O32 ABI, and the FPU Registers are organized as 32 ++// 32-bit registers, f0 through f31. When used as 'double' they are used ++// in pairs, starting with the even numbered register. So a double operation ++// on f0 really uses f0 and f1. ++// (Modern sw64 hardware also supports 32 64-bit registers, via setting ++// (privileged) Status Register FR bit to 1. This is used by the N32 ABI, ++// but it is not in common use. Someday we will want to support this in v8.) ++ ++// For O32 ABI, Floats and Doubles refer to same set of 32 32-bit registers. ++using FloatRegister = FPURegister; ++ ++using DoubleRegister = FPURegister; ++ ++#define DECLARE_DOUBLE_REGISTER(R) \ ++ constexpr DoubleRegister R = DoubleRegister::from_code(kDoubleCode_##R); ++DOUBLE_REGISTERS(DECLARE_DOUBLE_REGISTER) ++#undef DECLARE_DOUBLE_REGISTER ++ ++constexpr DoubleRegister no_dreg = DoubleRegister::no_reg(); ++ ++// SIMD registers. ++using Simd128Register = SWSARegister; ++ ++#define DECLARE_SIMD128_REGISTER(R) \ ++ constexpr Simd128Register R = Simd128Register::from_code(kSwsaCode_##R); ++SIMD128_REGISTERS(DECLARE_SIMD128_REGISTER) ++#undef DECLARE_SIMD128_REGISTER ++ ++const Simd128Register no_swsareg = Simd128Register::no_reg(); ++ ++// Register aliases. ++// cp is assumed to be a callee saved register. ++constexpr Register kRootRegister = s4; ++constexpr Register cp = s5; ++constexpr Register kScratchReg = t7; ++constexpr Register kScratchReg2 = t8; ++constexpr DoubleRegister kDoubleRegZero = f31; ++constexpr DoubleRegister kScratchDoubleReg = ++ f29; // change f30 to f29, use f28, f29 as scratch ++constexpr DoubleRegister kScratchDoubleReg1 = f28; ++constexpr DoubleRegister kScratchDoubleReg2 = f27; ++// Used on sw64r3 for compare operations. ++// We use the last non-callee saved odd register for N64 ABI ++constexpr DoubleRegister kDoubleCompareReg = f30; // change f28 to f30 ++// SWSA zero and scratch regs must have the same numbers as FPU zero and scratch ++constexpr Simd128Register kSimd128RegZero = w31; // may be used as scratch ++constexpr Simd128Register kSimd128ScratchReg = w30; ++ ++// FPU (coprocessor 1) control registers. ++// Currently only FPCR (#31) is implemented. ++struct FPUControlRegister { ++ bool is_valid() const { return reg_code == kFPCRRegister; } ++ bool is(FPUControlRegister creg) const { return reg_code == creg.reg_code; } ++ int code() const { ++ DCHECK(is_valid()); ++ return reg_code; ++ } ++ int bit() const { ++ DCHECK(is_valid()); ++ return 1 << reg_code; ++ } ++ void setcode(int f) { ++ reg_code = f; ++ DCHECK(is_valid()); ++ } ++ // Unfortunately we can't make this private in a struct. ++ int reg_code; ++}; ++ ++constexpr FPUControlRegister no_fpucreg = {kInvalidFPUControlRegister}; ++constexpr FPUControlRegister FPCR = {kFPCRRegister}; ++ ++// SWSA control registers ++struct SWSAControlRegister { ++ bool is_valid() const { ++ return (reg_code == kSWSAIRRegister) || (reg_code == kSWSACSRRegister); ++ } ++ bool is(SWSAControlRegister creg) const { return reg_code == creg.reg_code; } ++ int code() const { ++ DCHECK(is_valid()); ++ return reg_code; ++ } ++ int bit() const { ++ DCHECK(is_valid()); ++ return 1 << reg_code; ++ } ++ void setcode(int f) { ++ reg_code = f; ++ DCHECK(is_valid()); ++ } ++ // Unfortunately we can't make this private in a struct. ++ int reg_code; ++}; ++ ++constexpr SWSAControlRegister no_swsacreg = {kInvalidSWSAControlRegister}; ++constexpr SWSAControlRegister SWSAIR = {kSWSAIRRegister}; ++constexpr SWSAControlRegister SWSACSR = {kSWSACSRRegister}; ++ ++// Define {RegisterName} methods for the register types. ++DEFINE_REGISTER_NAMES(Register, GENERAL_REGISTERS) ++DEFINE_REGISTER_NAMES(FPURegister, DOUBLE_REGISTERS) ++DEFINE_REGISTER_NAMES(SWSARegister, SIMD128_REGISTERS) ++ ++// Give alias names to registers for calling conventions. ++constexpr Register kReturnRegister0 = v0; ++constexpr Register kReturnRegister1 = a5; // v1; ++constexpr Register kReturnRegister2 = a0; ++constexpr Register kJSFunctionRegister = a1; ++constexpr Register kContextRegister = s5; // s7; ++constexpr Register kAllocateSizeRegister = a0; ++constexpr Register kInterpreterAccumulatorRegister = v0; ++constexpr Register kInterpreterBytecodeOffsetRegister = t0; ++constexpr Register kInterpreterBytecodeArrayRegister = t1; ++constexpr Register kInterpreterDispatchTableRegister = t2; ++ ++constexpr Register kJavaScriptCallArgCountRegister = a0; ++constexpr Register kJavaScriptCallCodeStartRegister = a2; ++constexpr Register kJavaScriptCallTargetRegister = kJSFunctionRegister; ++constexpr Register kJavaScriptCallNewTargetRegister = a3; ++constexpr Register kJavaScriptCallExtraArg1Register = a2; ++ ++constexpr Register kOffHeapTrampolineRegister = at; ++constexpr Register kRuntimeCallFunctionRegister = a1; ++constexpr Register kRuntimeCallArgCountRegister = a0; ++constexpr Register kRuntimeCallArgvRegister = a2; ++constexpr Register kWasmInstanceRegister = a0; ++constexpr Register kWasmCompileLazyFuncIndexRegister = t0; ++ ++constexpr DoubleRegister kFPReturnRegister0 = f0; ++ ++} // namespace internal ++} // namespace v8 ++ ++#endif // V8_SW64_REGISTER_SW64_H_ +diff --git a/deps/v8/src/codegen/sw64/reglist-sw64.h b/deps/v8/src/codegen/sw64/reglist-sw64.h +new file mode 100644 +index 00000000..22340534 +--- /dev/null ++++ b/deps/v8/src/codegen/sw64/reglist-sw64.h +@@ -0,0 +1,47 @@ ++// 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 V8_CODEGEN_SW64_REGLIST_SW64_H_ ++#define V8_CODEGEN_SW64_REGLIST_SW64_H_ ++ ++#include "src/codegen/sw64/constants-sw64.h" ++#include "src/codegen/register-arch.h" ++#include "src/codegen/reglist-base.h" ++ ++namespace v8 { ++namespace internal { ++ ++using RegList = RegListBase; ++using DoubleRegList = RegListBase; ++ASSERT_TRIVIALLY_COPYABLE(RegList); ++ASSERT_TRIVIALLY_COPYABLE(DoubleRegList); ++ ++const RegList kJSCallerSaved = {v0, t0, t1, t2, t3, t4, t5, t6, t7, ++ a0, a1, a2, a3, a4, a5, t8, t9, t10}; ++ ++const int kNumJSCallerSaved = 18; ++ ++// Callee-saved registers preserved when switching from C to JavaScript. ++const RegList kCalleeSaved = {s0, // s0 ++ s1, // s1 ++ s2, // s2 ++ s3, // s3 ++ s4, // s4 (roots in Javascript code) ++ s5, // s5 (cp in Javascript code) ++ fp}; // fp/s6 ++ ++const int kNumCalleeSaved = 7; ++ ++const DoubleRegList kCalleeSavedFPU = {f2, f3, f4, f5, f6, f7, f8, f9}; ++ ++const int kNumCalleeSavedFPU = 8; ++ ++const DoubleRegList kCallerSavedFPU = {f0, f1, f10, f11, f12, f13, f14, f15, ++ f16, f17, f18, f19, f20, f21, f22, f23, ++ f24, f25, f26, f27, f28, f29, f30}; ++ ++} // namespace internal ++} // namespace v8 ++ ++#endif // V8_CODEGEN_SW64_REGLIST_SW64_H_ +diff --git a/deps/v8/src/common/globals.h b/deps/v8/src/common/globals.h +index 1b9f9a94..7ac7ce1a 100644 +--- a/deps/v8/src/common/globals.h ++++ b/deps/v8/src/common/globals.h +@@ -52,6 +52,9 @@ namespace internal { + #if (V8_TARGET_ARCH_MIPS64 && !V8_HOST_ARCH_MIPS64) + #define USE_SIMULATOR 1 + #endif ++#if (V8_TARGET_ARCH_SW64 && !V8_HOST_ARCH_SW64) ++#define USE_SIMULATOR 1 ++#endif + #if (V8_TARGET_ARCH_S390 && !V8_HOST_ARCH_S390) + #define USE_SIMULATOR 1 + #endif +diff --git a/deps/v8/src/compiler/backend/instruction-codes.h b/deps/v8/src/compiler/backend/instruction-codes.h +index b06b5222..6b9adc6b 100644 +--- a/deps/v8/src/compiler/backend/instruction-codes.h ++++ b/deps/v8/src/compiler/backend/instruction-codes.h +@@ -19,6 +19,8 @@ + #include "src/compiler/backend/mips64/instruction-codes-mips64.h" + #elif V8_TARGET_ARCH_LOONG64 + #include "src/compiler/backend/loong64/instruction-codes-loong64.h" ++#elif V8_TARGET_ARCH_SW64 ++#include "src/compiler/backend/sw64/instruction-codes-sw64.h" + #elif V8_TARGET_ARCH_X64 + #include "src/compiler/backend/x64/instruction-codes-x64.h" + #elif V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_PPC64 +diff --git a/deps/v8/src/compiler/backend/instruction-selector.cc b/deps/v8/src/compiler/backend/instruction-selector.cc +index 7da0bce3..c4ba391e 100644 +--- a/deps/v8/src/compiler/backend/instruction-selector.cc ++++ b/deps/v8/src/compiler/backend/instruction-selector.cc +@@ -2721,7 +2721,7 @@ void InstructionSelector::VisitWord32AtomicPairCompareExchange(Node* node) { + + #if !V8_TARGET_ARCH_X64 && !V8_TARGET_ARCH_ARM64 && !V8_TARGET_ARCH_MIPS64 && \ + !V8_TARGET_ARCH_S390 && !V8_TARGET_ARCH_PPC64 && \ +- !V8_TARGET_ARCH_RISCV64 && !V8_TARGET_ARCH_LOONG64 ++ !V8_TARGET_ARCH_RISCV64 && !V8_TARGET_ARCH_LOONG64 && !V8_TARGET_ARCH_SW64 + void InstructionSelector::VisitWord64AtomicLoad(Node* node) { UNIMPLEMENTED(); } + + void InstructionSelector::VisitWord64AtomicStore(Node* node) { +@@ -2761,12 +2761,12 @@ void InstructionSelector::VisitI64x2ReplaceLaneI32Pair(Node* node) { + + #if !V8_TARGET_ARCH_X64 && !V8_TARGET_ARCH_S390X && !V8_TARGET_ARCH_PPC64 + #if !V8_TARGET_ARCH_ARM64 +-#if !V8_TARGET_ARCH_MIPS64 && !V8_TARGET_ARCH_LOONG64 && !V8_TARGET_ARCH_RISCV64 ++#if !V8_TARGET_ARCH_MIPS64 && !V8_TARGET_ARCH_LOONG64 && !V8_TARGET_ARCH_RISCV64 && !V8_TARGET_ARCH_SW64 + void InstructionSelector::VisitI64x2Splat(Node* node) { UNIMPLEMENTED(); } + void InstructionSelector::VisitI64x2ExtractLane(Node* node) { UNIMPLEMENTED(); } + void InstructionSelector::VisitI64x2ReplaceLane(Node* node) { UNIMPLEMENTED(); } + #endif // !V8_TARGET_ARCH_MIPS64 && !V8_TARGET_ARCH_LOONG64 && +- // !V8_TARGET_ARCH_RISCV64 ++ // !V8_TARGET_ARCH_RISCV64 && !V8_TARGET_ARCH_SW64 + #endif // !V8_TARGET_ARCH_ARM64 + #endif // !V8_TARGET_ARCH_X64 && !V8_TARGET_ARCH_S390X && !V8_TARGET_ARCH_PPC64 + +@@ -2778,7 +2778,7 @@ void InstructionSelector::VisitF32x4Qfma(Node* node) { UNIMPLEMENTED(); } + void InstructionSelector::VisitF32x4Qfms(Node* node) { UNIMPLEMENTED(); } + #endif // !V8_TARGET_ARCH_X64 && !V8_TARGET_ARCH_S390X && !V8_TARGET_ARCH_PPC64 + // && !V8_TARGET_ARCH_ARM64 && !V8_TARGET_ARCH_IA32 && +- // !V8_TARGET_ARCH_RISCV64 ++ // !V8_TARGET_ARCH_RISCV64 && !V8_TARGET_ARCH_SW64 + + #if !V8_TARGET_ARCH_X64 && !V8_TARGET_ARCH_IA32 && !V8_TARGET_ARCH_ARM64 && \ + !V8_TARGET_ARCH_RISCV64 && !V8_TARGET_ARCH_ARM +diff --git a/deps/v8/src/compiler/backend/sw64/code-generator-sw64.cc b/deps/v8/src/compiler/backend/sw64/code-generator-sw64.cc +new file mode 100644 +index 00000000..51c80cd2 +--- /dev/null ++++ b/deps/v8/src/compiler/backend/sw64/code-generator-sw64.cc +@@ -0,0 +1,2918 @@ ++// Copyright 2014 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "src/codegen/assembler-inl.h" ++#include "src/codegen/callable.h" ++#include "src/codegen/optimized-compilation-info.h" ++#include "src/codegen/sw64/constants-sw64.h" ++#include "src/compiler/backend/code-generator-impl.h" ++#include "src/compiler/backend/code-generator.h" ++#include "src/compiler/backend/gap-resolver.h" ++#include "src/compiler/node-matchers.h" ++#include "src/compiler/osr.h" ++#include "src/heap/memory-chunk.h" ++ ++#if V8_ENABLE_WEBASSEMBLY ++#include "src/wasm/wasm-code-manager.h" ++#endif // V8_ENABLE_WEBASSEMBLY ++ ++namespace v8 { ++namespace internal { ++namespace compiler { ++ ++#define __ tasm()-> ++ ++// TODO(plind): consider renaming these macros. ++#define TRACE_MSG(msg) \ ++ PrintF("code_gen: \'%s\' in function %s at line %d\n", msg, __FUNCTION__, \ ++ __LINE__) ++ ++#define TRACE_UNIMPL() \ ++ PrintF("UNIMPLEMENTED code_generator_sw64: %s at line %d\n", __FUNCTION__, \ ++ __LINE__) ++ ++// Adds Sw64-specific methods to convert InstructionOperands. ++class Sw64OperandConverter final : public InstructionOperandConverter { ++ public: ++ Sw64OperandConverter(CodeGenerator* gen, Instruction* instr) ++ : InstructionOperandConverter(gen, instr) {} ++ ++ FloatRegister OutputSingleRegister(size_t index = 0) { ++ return ToSingleRegister(instr_->OutputAt(index)); ++ } ++ ++ FloatRegister InputSingleRegister(size_t index) { ++ return ToSingleRegister(instr_->InputAt(index)); ++ } ++ ++ FloatRegister ToSingleRegister(InstructionOperand* op) { ++ // Single (Float) and Double register namespace is same on SW64, ++ // both are typedefs of FPURegister. ++ return ToDoubleRegister(op); ++ } ++ ++ Register InputOrZeroRegister(size_t index) { ++ if (instr_->InputAt(index)->IsImmediate()) { ++ DCHECK_EQ(0, InputInt32(index)); ++ return zero_reg; ++ } ++ return InputRegister(index); ++ } ++ ++ DoubleRegister InputOrZeroDoubleRegister(size_t index) { ++ if (instr_->InputAt(index)->IsImmediate()) return kDoubleRegZero; ++ ++ return InputDoubleRegister(index); ++ } ++ ++ DoubleRegister InputOrZeroSingleRegister(size_t index) { ++ if (instr_->InputAt(index)->IsImmediate()) return kDoubleRegZero; ++ ++ return InputSingleRegister(index); ++ } ++ ++ Operand InputImmediate(size_t index) { ++ Constant constant = ToConstant(instr_->InputAt(index)); ++ switch (constant.type()) { ++ case Constant::kInt32: ++ return Operand(constant.ToInt32()); ++ case Constant::kInt64: ++ return Operand(constant.ToInt64()); ++ case Constant::kFloat32: ++ return Operand::EmbeddedNumber(constant.ToFloat32()); ++ case Constant::kFloat64: ++ return Operand::EmbeddedNumber(constant.ToFloat64().value()); ++ case Constant::kExternalReference: ++ case Constant::kCompressedHeapObject: ++ case Constant::kHeapObject: ++ // TODO(plind): Maybe we should handle ExtRef & HeapObj here? ++ // maybe not done on arm due to const pool ?? ++ break; ++ case Constant::kDelayedStringConstant: ++ return Operand::EmbeddedStringConstant( ++ constant.ToDelayedStringConstant()); ++ case Constant::kRpoNumber: ++ UNREACHABLE(); // TODO(titzer): RPO immediates on sw64? ++ } ++ UNREACHABLE(); ++ } ++ ++ Operand InputOperand(size_t index) { ++ InstructionOperand* op = instr_->InputAt(index); ++ if (op->IsRegister()) { ++ return Operand(ToRegister(op)); ++ } ++ return InputImmediate(index); ++ } ++ ++ MemOperand MemoryOperand(size_t* first_index) { ++ const size_t index = *first_index; ++ switch (AddressingModeField::decode(instr_->opcode())) { ++ case kMode_None: ++ break; ++ case kMode_MRI: ++ *first_index += 2; ++ return MemOperand(InputRegister(index + 0), InputInt32(index + 1)); ++ case kMode_MRR: ++ // TODO(plind): r6 address mode, to be implemented ... ++ UNREACHABLE(); ++ } ++ UNREACHABLE(); ++ } ++ ++ MemOperand MemoryOperand(size_t index = 0) { return MemoryOperand(&index); } ++ ++ MemOperand ToMemOperand(InstructionOperand* op) const { ++ DCHECK_NOT_NULL(op); ++ DCHECK(op->IsStackSlot() || op->IsFPStackSlot()); ++ return SlotToMemOperand(AllocatedOperand::cast(op)->index()); ++ } ++ ++ MemOperand SlotToMemOperand(int slot) const { ++ FrameOffset offset = frame_access_state()->GetFrameOffset(slot); ++ return MemOperand(offset.from_stack_pointer() ? sp : fp, offset.offset()); ++ } ++}; ++ ++static inline bool HasRegisterInput(Instruction* instr, size_t index) { ++ return instr->InputAt(index)->IsRegister(); ++} ++ ++namespace { ++ ++class OutOfLineRecordWrite final : public OutOfLineCode { ++ public: ++ OutOfLineRecordWrite(CodeGenerator* gen, Register object, Register index, ++ Register value, Register scratch0, Register scratch1, ++ RecordWriteMode mode, StubCallMode stub_mode) ++ : OutOfLineCode(gen), ++ object_(object), ++ index_(index), ++ value_(value), ++ scratch0_(scratch0), ++ scratch1_(scratch1), ++ mode_(mode), ++#if V8_ENABLE_WEBASSEMBLY ++ stub_mode_(stub_mode), ++#endif // V8_ENABLE_WEBASSEMBLY ++ must_save_lr_(!gen->frame_access_state()->has_frame()), ++ zone_(gen->zone()) { ++ DCHECK(!AreAliased(object, index, scratch0, scratch1)); ++ DCHECK(!AreAliased(value, index, scratch0, scratch1)); ++ } ++ ++ void Generate() final { ++ __ CheckPageFlag(value_, scratch0_, ++ MemoryChunk::kPointersToHereAreInterestingMask, eq, ++ exit()); ++ __ Addl(scratch1_, object_, index_); ++ RememberedSetAction const remembered_set_action = ++ mode_ > RecordWriteMode::kValueIsMap || ++ FLAG_use_full_record_write_builtin ++ ? RememberedSetAction::kEmit ++ : RememberedSetAction::kOmit; ++ SaveFPRegsMode const save_fp_mode = frame()->DidAllocateDoubleRegisters() ++ ? SaveFPRegsMode::kSave ++ : SaveFPRegsMode::kIgnore; ++ if (must_save_lr_) { ++ // We need to save and restore ra if the frame was elided. ++ __ Push(ra); ++ } ++ if (mode_ == RecordWriteMode::kValueIsEphemeronKey) { ++ __ CallEphemeronKeyBarrier(object_, scratch1_, save_fp_mode); ++#if V8_ENABLE_WEBASSEMBLY ++ } else if (stub_mode_ == StubCallMode::kCallWasmRuntimeStub) { ++ // A direct call to a wasm runtime stub defined in this module. ++ // Just encode the stub index. This will be patched when the code ++ // is added to the native module and copied into wasm code space. ++ __ CallRecordWriteStubSaveRegisters(object_, scratch1_, ++ remembered_set_action, save_fp_mode, ++ StubCallMode::kCallWasmRuntimeStub); ++#endif // V8_ENABLE_WEBASSEMBLY ++ } else { ++ __ CallRecordWriteStubSaveRegisters(object_, scratch1_, ++ remembered_set_action, save_fp_mode); ++ } ++ if (must_save_lr_) { ++ __ Pop(ra); ++ } ++ } ++ ++ private: ++ Register const object_; ++ Register const index_; ++ Register const value_; ++ Register const scratch0_; ++ Register const scratch1_; ++ RecordWriteMode const mode_; ++#if V8_ENABLE_WEBASSEMBLY ++ StubCallMode const stub_mode_; ++#endif // V8_ENABLE_WEBASSEMBLY ++ bool must_save_lr_; ++ Zone* zone_; ++}; ++ ++#define CREATE_OOL_CLASS(ool_name, tasm_ool_name, T) \ ++ class ool_name final : public OutOfLineCode { \ ++ public: \ ++ ool_name(CodeGenerator* gen, T dst, T src1, T src2) \ ++ : OutOfLineCode(gen), dst_(dst), src1_(src1), src2_(src2) {} \ ++ \ ++ void Generate() final { __ tasm_ool_name(dst_, src1_, src2_); } \ ++ \ ++ private: \ ++ T const dst_; \ ++ T const src1_; \ ++ T const src2_; \ ++ } ++ ++CREATE_OOL_CLASS(OutOfLineFloat32Max, Float32MaxOutOfLine, FPURegister); ++CREATE_OOL_CLASS(OutOfLineFloat32Min, Float32MinOutOfLine, FPURegister); ++CREATE_OOL_CLASS(OutOfLineFloat64Max, Float64MaxOutOfLine, FPURegister); ++CREATE_OOL_CLASS(OutOfLineFloat64Min, Float64MinOutOfLine, FPURegister); ++ ++#undef CREATE_OOL_CLASS ++ ++Condition FlagsConditionToConditionCmp(FlagsCondition condition) { ++ switch (condition) { ++ case kEqual: ++ return eq; ++ case kNotEqual: ++ return ne; ++ case kSignedLessThan: ++ return lt; ++ case kSignedGreaterThanOrEqual: ++ return ge; ++ case kSignedLessThanOrEqual: ++ return le; ++ case kSignedGreaterThan: ++ return gt; ++ case kUnsignedLessThan: ++ return lo; ++ case kUnsignedGreaterThanOrEqual: ++ return hs; ++ case kUnsignedLessThanOrEqual: ++ return ls; ++ case kUnsignedGreaterThan: ++ return hi; ++ case kUnorderedEqual: ++ case kUnorderedNotEqual: ++ break; ++ default: ++ break; ++ } ++ UNREACHABLE(); ++} ++ ++Condition FlagsConditionToConditionTst(FlagsCondition condition) { ++ switch (condition) { ++ case kNotEqual: ++ return ne; ++ case kEqual: ++ return eq; ++ default: ++ break; ++ } ++ UNREACHABLE(); ++} ++ ++Condition FlagsConditionToConditionOvf(FlagsCondition condition) { ++ switch (condition) { ++ case kOverflow: ++ return ne; ++ case kNotOverflow: ++ return eq; ++ default: ++ break; ++ } ++ UNREACHABLE(); ++} ++ ++FPUCondition FlagsConditionToConditionCmpFPU(bool* predicate, ++ FlagsCondition condition) { ++ switch (condition) { ++ case kEqual: ++ *predicate = true; ++ return EQ; ++ case kNotEqual: ++ *predicate = false; ++ return EQ; ++ case kUnsignedLessThan: ++ *predicate = true; ++ return OLT; ++ case kUnsignedGreaterThanOrEqual: ++ *predicate = false; ++ return OLT; ++ case kUnsignedLessThanOrEqual: ++ *predicate = true; ++ return OLE; ++ case kUnsignedGreaterThan: ++ *predicate = false; ++ return OLE; ++ case kUnorderedEqual: ++ case kUnorderedNotEqual: ++ *predicate = true; ++ break; ++ default: ++ *predicate = true; ++ break; ++ } ++ UNREACHABLE(); ++} ++ ++} // namespace ++ ++#define ASSEMBLE_ATOMIC_LOAD_INTEGER(asm_instr) \ ++ do { \ ++ __ asm_instr(i.OutputRegister(), i.MemoryOperand()); \ ++ /*__ memb();*/ \ ++ } while (0) ++ ++#define ASSEMBLE_ATOMIC_STORE_INTEGER(asm_instr) \ ++ do { \ ++ /*__ memb();*/ \ ++ __ asm_instr(i.InputOrZeroRegister(2), i.MemoryOperand()); \ ++ /*__ memb();*/ \ ++ } while (0) ++ ++#define ASSEMBLE_ATOMIC_BINOP(load_linked, store_conditional, bin_instr) \ ++ do { \ ++ Label binop; \ ++ __ Addl(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1)); \ ++ /*__ memb();*/ \ ++ __ bind(&binop); \ ++ __ load_linked(i.OutputRegister(0), 0, i.TempRegister(0)); \ ++ __ ldi(gp, 1, zero_reg); \ ++ __ wr_f(gp); \ ++ __ bin_instr(i.TempRegister(1), i.OutputRegister(0), \ ++ Operand(i.InputRegister(2))); \ ++ __ Align(8); \ ++ __ store_conditional(i.TempRegister(1), 0, i.TempRegister(0)); \ ++ __ rd_f(i.TempRegister(1)); \ ++ __ BranchShort(&binop, eq, i.TempRegister(1), Operand(zero_reg)); \ ++ /*__ memb();*/ \ ++ } while (0) ++ ++#define ASSEMBLE_ATOMIC_BINOP_EXT(load_linked, store_conditional, sign_extend, \ ++ size, bin_instr, representation) \ ++ do { \ ++ Label binop; \ ++ __ addl(i.InputRegister(0), i.InputRegister(1), i.TempRegister(0)); \ ++ if (representation == 32) { \ ++ __ and_(i.TempRegister(0), 0x3, i.TempRegister(3)); \ ++ } else { \ ++ DCHECK_EQ(representation, 64); \ ++ __ and_(i.TempRegister(0), 0x7, i.TempRegister(3)); \ ++ } \ ++ __ Subl(i.TempRegister(0), i.TempRegister(0), Operand(i.TempRegister(3))); \ ++ __ slll(i.TempRegister(3), 3, i.TempRegister(3)); \ ++ /*__ memb();*/ \ ++ __ bind(&binop); \ ++ __ load_linked(i.TempRegister(1), 0, i.TempRegister(0)); \ ++ __ ldi(gp, 1, zero_reg); \ ++ __ wr_f(gp); \ ++ __ ExtractBits(i.OutputRegister(0), i.TempRegister(1), i.TempRegister(3), \ ++ size, sign_extend); \ ++ __ bin_instr(i.TempRegister(2), i.OutputRegister(0), \ ++ Operand(i.InputRegister(2))); \ ++ __ InsertBits(i.TempRegister(1), i.TempRegister(2), i.TempRegister(3), \ ++ size); \ ++ __ Align(8); \ ++ __ store_conditional(i.TempRegister(1), 0, i.TempRegister(0)); \ ++ __ rd_f(i.TempRegister(1)); \ ++ __ BranchShort(&binop, eq, i.TempRegister(1), Operand(zero_reg)); \ ++ /*__ memb();*/ \ ++ } while (0) ++ ++#define ASSEMBLE_ATOMIC_EXCHANGE_INTEGER(load_linked, store_conditional) \ ++ do { \ ++ Label exchange; \ ++ /*__ memb();*/ \ ++ __ bind(&exchange); \ ++ __ addl(i.InputRegister(0), i.InputRegister(1), i.TempRegister(0)); \ ++ __ load_linked(i.OutputRegister(0), 0, i.TempRegister(0)); \ ++ __ ldi(gp, 1, zero_reg); \ ++ __ wr_f(gp); \ ++ __ mov(i.TempRegister(1), i.InputRegister(2)); \ ++ __ Align(8); \ ++ __ store_conditional(i.TempRegister(1), 0, i.TempRegister(0)); \ ++ __ rd_f(i.TempRegister(1)); \ ++ __ BranchShort(&exchange, eq, i.TempRegister(1), Operand(zero_reg)); \ ++ /*__ memb();*/ \ ++ } while (0) ++ ++#define ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT( \ ++ load_linked, store_conditional, sign_extend, size, representation) \ ++ do { \ ++ Label exchange; \ ++ __ addl(i.InputRegister(0), i.InputRegister(1), i.TempRegister(0)); \ ++ if (representation == 32) { \ ++ __ and_(i.TempRegister(0), 0x3, i.TempRegister(1)); \ ++ } else { \ ++ DCHECK_EQ(representation, 64); \ ++ __ and_(i.TempRegister(0), 0x7, i.TempRegister(1)); \ ++ } \ ++ __ Subl(i.TempRegister(0), i.TempRegister(0), Operand(i.TempRegister(1))); \ ++ __ Sllw(i.TempRegister(1), i.TempRegister(1), 3); \ ++ /*__ memb();*/ \ ++ __ bind(&exchange); \ ++ __ load_linked(i.TempRegister(2), 0, i.TempRegister(0)); \ ++ __ ldi(gp, 1, zero_reg); \ ++ __ wr_f(gp); \ ++ __ ExtractBits(i.OutputRegister(0), i.TempRegister(2), i.TempRegister(1), \ ++ size, sign_extend); \ ++ __ InsertBits(i.TempRegister(2), i.InputRegister(2), i.TempRegister(1), \ ++ size); \ ++ __ Align(8); \ ++ __ store_conditional(i.TempRegister(2), 0, i.TempRegister(0)); \ ++ __ rd_f(i.TempRegister(2)); \ ++ __ BranchShort(&exchange, eq, i.TempRegister(2), Operand(zero_reg)); \ ++ /*__ memb();*/ \ ++ } while (0) ++ ++#define ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER(load_linked, \ ++ store_conditional) \ ++ do { \ ++ Label compareExchange; \ ++ Label exit; \ ++ __ addl(i.InputRegister(0), i.InputRegister(1), i.TempRegister(0)); \ ++ /*__ memb();*/ \ ++ __ bind(&compareExchange); \ ++ __ load_linked(i.OutputRegister(0), 0, i.TempRegister(0)); \ ++ __ cmpeq(i.OutputRegister(0), i.InputRegister(2), i.TempRegister(1)); \ ++ __ wr_f(i.TempRegister(1)); \ ++ __ mov(i.TempRegister(2), i.InputRegister(3)); \ ++ __ Align(8); \ ++ __ store_conditional(i.TempRegister(2), 0, i.TempRegister(0)); \ ++ __ rd_f(i.TempRegister(2)); \ ++ __ beq(i.TempRegister(1), &exit); \ ++ __ beq(i.TempRegister(2), &compareExchange); \ ++ __ bind(&exit); \ ++ /*__ memb();*/ \ ++ } while (0) ++ ++#define ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT( \ ++ load_linked, store_conditional, sign_extend, size, representation) \ ++ do { \ ++ Label compareExchange; \ ++ Label exit; \ ++ __ addl(i.InputRegister(0), i.InputRegister(1), i.TempRegister(0)); \ ++ if (representation == 32) { \ ++ __ and_(i.TempRegister(0), 0x3, i.TempRegister(1)); \ ++ } else { \ ++ DCHECK_EQ(representation, 64); \ ++ __ and_(i.TempRegister(0), 0x7, i.TempRegister(1)); \ ++ } \ ++ __ Subl(i.TempRegister(0), i.TempRegister(0), Operand(i.TempRegister(1))); \ ++ __ slll(i.TempRegister(1), 3, i.TempRegister(1)); \ ++ /*__ memb();*/ \ ++ __ bind(&compareExchange); \ ++ __ load_linked(i.TempRegister(2), 0, i.TempRegister(0)); \ ++ __ mov(i.OutputRegister(0), i.TempRegister(2)); \ ++ __ cmpeq(i.OutputRegister(0), i.InputRegister(2), gp); \ ++ __ ExtractBits(i.OutputRegister(0), i.TempRegister(2), i.TempRegister(1), \ ++ size, sign_extend); \ ++ __ ExtractBits(i.InputRegister(2), i.InputRegister(2), zero_reg, size, \ ++ sign_extend); \ ++ __ wr_f(gp); \ ++ __ InsertBits(i.TempRegister(2), i.InputRegister(3), i.TempRegister(1), \ ++ size); \ ++ __ Align(8); \ ++ __ store_conditional(i.TempRegister(2), 0, i.TempRegister(0)); \ ++ __ rd_f(i.TempRegister(2)); \ ++ __ beq(gp, &exit); \ ++ __ beq(i.TempRegister(2), &compareExchange); \ ++ __ bind(&exit); \ ++ /*__ memb();*/ \ ++ } while (0) ++ ++#define ASSEMBLE_IEEE754_BINOP(name) \ ++ do { \ ++ FrameScope scope(tasm(), StackFrame::MANUAL); \ ++ __ PrepareCallCFunction(0, 2, kScratchReg); \ ++ __ MovToFloatParameters(i.InputDoubleRegister(0), \ ++ i.InputDoubleRegister(1)); \ ++ __ CallCFunction(ExternalReference::ieee754_##name##_function(), 0, 2); \ ++ /* Move the result in the double result register. */ \ ++ __ MovFromFloatResult(i.OutputDoubleRegister()); \ ++ } while (0) ++ ++#define ASSEMBLE_IEEE754_UNOP(name) \ ++ do { \ ++ FrameScope scope(tasm(), StackFrame::MANUAL); \ ++ __ PrepareCallCFunction(0, 1, kScratchReg); \ ++ __ MovToFloatParameter(i.InputDoubleRegister(0)); \ ++ __ CallCFunction(ExternalReference::ieee754_##name##_function(), 0, 1); \ ++ /* Move the result in the double result register. */ \ ++ __ MovFromFloatResult(i.OutputDoubleRegister()); \ ++ } while (0) ++ ++// SKTODO ++#if 0 ++#define ASSEMBLE_F64X2_ARITHMETIC_BINOP(op) \ ++ do { \ ++ __ op(i.OutputSimd128Register(), i.InputSimd128Register(0), \ ++ i.InputSimd128Register(1)); \ ++ } while (0) ++ ++#endif ++ ++void CodeGenerator::AssembleDeconstructFrame() { ++ __ mov(sp, fp); ++ __ Pop(ra, fp); ++} ++ ++void CodeGenerator::AssemblePrepareTailCall() { ++ if (frame_access_state()->has_frame()) { ++ __ Ldl(ra, MemOperand(fp, StandardFrameConstants::kCallerPCOffset)); ++ __ Ldl(fp, MemOperand(fp, StandardFrameConstants::kCallerFPOffset)); ++ } ++ frame_access_state()->SetFrameAccessToSP(); ++} ++ ++namespace { ++ ++void AdjustStackPointerForTailCall(TurboAssembler* tasm, ++ FrameAccessState* state, ++ int new_slot_above_sp, ++ bool allow_shrinkage = true) { ++ int current_sp_offset = state->GetSPToFPSlotCount() + ++ StandardFrameConstants::kFixedSlotCountAboveFp; ++ int stack_slot_delta = new_slot_above_sp - current_sp_offset; ++ if (stack_slot_delta > 0) { ++ tasm->Subl(sp, sp, stack_slot_delta * kSystemPointerSize); ++ state->IncreaseSPDelta(stack_slot_delta); ++ } else if (allow_shrinkage && stack_slot_delta < 0) { ++ tasm->Addl(sp, sp, -stack_slot_delta * kSystemPointerSize); ++ state->IncreaseSPDelta(stack_slot_delta); ++ } ++} ++ ++} // namespace ++ ++void CodeGenerator::AssembleTailCallBeforeGap(Instruction* instr, ++ int first_unused_slot_offset) { ++ AdjustStackPointerForTailCall(tasm(), frame_access_state(), ++ first_unused_slot_offset, false); ++} ++ ++void CodeGenerator::AssembleTailCallAfterGap(Instruction* instr, ++ int first_unused_slot_offset) { ++ AdjustStackPointerForTailCall(tasm(), frame_access_state(), ++ first_unused_slot_offset); ++} ++ ++// Check that {kJavaScriptCallCodeStartRegister} is correct. ++void CodeGenerator::AssembleCodeStartRegisterCheck() { ++ __ ComputeCodeStartAddress(kScratchReg); ++ __ Assert(eq, AbortReason::kWrongFunctionCodeStart, ++ kJavaScriptCallCodeStartRegister, Operand(kScratchReg)); ++} ++ ++// Check if the code object is marked for deoptimization. If it is, then it ++// jumps to the CompileLazyDeoptimizedCode builtin. In order to do this we need ++// to: ++// 1. read from memory the word that contains that bit, which can be found in ++// the flags in the referenced {CodeDataContainer} object; ++// 2. test kMarkedForDeoptimizationBit in those flags; and ++// 3. if it is not zero then it jumps to the builtin. ++void CodeGenerator::BailoutIfDeoptimized() { ++ int offset = Code::kCodeDataContainerOffset - Code::kHeaderSize; ++ __ Ldl(kScratchReg, MemOperand(kJavaScriptCallCodeStartRegister, offset)); ++ __ Ldw(kScratchReg, ++ FieldMemOperand(kScratchReg, ++ CodeDataContainer::kKindSpecificFlagsOffset)); ++ __ And(kScratchReg, kScratchReg, ++ Operand(1 << Code::kMarkedForDeoptimizationBit)); ++ __ Jump(BUILTIN_CODE(isolate(), CompileLazyDeoptimizedCode), ++ RelocInfo::CODE_TARGET, ne, kScratchReg, Operand(zero_reg)); ++} ++ ++// Assembles an instruction after register allocation, producing machine code. ++CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction( ++ Instruction* instr) { ++ Sw64OperandConverter i(this, instr); ++ InstructionCode opcode = instr->opcode(); ++ ArchOpcode arch_opcode = ArchOpcodeField::decode(opcode); ++ switch (arch_opcode) { ++ case kArchCallCodeObject: { ++ if (instr->InputAt(0)->IsImmediate()) { ++ __ Call(i.InputCode(0), RelocInfo::CODE_TARGET); ++ } else { ++ Register reg = i.InputRegister(0); ++ DCHECK_IMPLIES( ++ instr->HasCallDescriptorFlag(CallDescriptor::kFixedTargetRegister), ++ reg == kJavaScriptCallCodeStartRegister); ++ __ Addl(reg, reg, Code::kHeaderSize - kHeapObjectTag); ++ __ Call(reg); ++ } ++ RecordCallPosition(instr); ++ frame_access_state()->ClearSPDelta(); ++ break; ++ } ++ case kArchCallBuiltinPointer: { ++ DCHECK(!instr->InputAt(0)->IsImmediate()); ++ Register builtin_index = i.InputRegister(0); ++ __ CallBuiltinByIndex(builtin_index); ++ RecordCallPosition(instr); ++ frame_access_state()->ClearSPDelta(); ++ break; ++ } ++#if V8_ENABLE_WEBASSEMBLY ++ case kArchCallWasmFunction: { ++ if (instr->InputAt(0)->IsImmediate()) { ++ Constant constant = i.ToConstant(instr->InputAt(0)); ++ Address wasm_code = static_cast
(constant.ToInt64()); ++ __ Call(wasm_code, constant.rmode()); ++ } else { ++ __ Addl(kScratchReg, i.InputRegister(0), 0); ++ __ Call(kScratchReg); ++ } ++ RecordCallPosition(instr); ++ frame_access_state()->ClearSPDelta(); ++ break; ++ } ++ case kArchTailCallWasm: { ++ if (instr->InputAt(0)->IsImmediate()) { ++ Constant constant = i.ToConstant(instr->InputAt(0)); ++ Address wasm_code = static_cast
(constant.ToInt64()); ++ __ Jump(wasm_code, constant.rmode()); ++ } else { ++ __ addl(i.InputRegister(0), 0, kScratchReg); ++ __ Jump(kScratchReg); ++ } ++ frame_access_state()->ClearSPDelta(); ++ frame_access_state()->SetFrameAccessToDefault(); ++ break; ++ } ++#endif // V8_ENABLE_WEBASSEMBLY ++ case kArchTailCallCodeObject: { ++ if (instr->InputAt(0)->IsImmediate()) { ++ __ Jump(i.InputCode(0), RelocInfo::CODE_TARGET); ++ } else { ++ Register reg = i.InputRegister(0); ++ DCHECK_IMPLIES( ++ instr->HasCallDescriptorFlag(CallDescriptor::kFixedTargetRegister), ++ reg == kJavaScriptCallCodeStartRegister); ++ __ Addl(reg, reg, Code::kHeaderSize - kHeapObjectTag); ++ __ Jump(reg); ++ } ++ frame_access_state()->ClearSPDelta(); ++ frame_access_state()->SetFrameAccessToDefault(); ++ break; ++ } ++ case kArchTailCallAddress: { ++ CHECK(!instr->InputAt(0)->IsImmediate()); ++ Register reg = i.InputRegister(0); ++ DCHECK_IMPLIES( ++ instr->HasCallDescriptorFlag(CallDescriptor::kFixedTargetRegister), ++ reg == kJavaScriptCallCodeStartRegister); ++ __ Jump(reg); ++ frame_access_state()->ClearSPDelta(); ++ frame_access_state()->SetFrameAccessToDefault(); ++ break; ++ } ++ case kArchCallJSFunction: { ++ Register func = i.InputRegister(0); ++ if (FLAG_debug_code) { ++ // Check the function's context matches the context argument. ++ __ Ldl(kScratchReg, FieldMemOperand(func, JSFunction::kContextOffset)); ++ __ Assert(eq, AbortReason::kWrongFunctionContext, cp, ++ Operand(kScratchReg)); ++ } ++ static_assert(kJavaScriptCallCodeStartRegister == a2, "ABI mismatch"); ++ __ Ldl(a2, FieldMemOperand(func, JSFunction::kCodeOffset)); ++ __ Addl(a2, a2, Operand(Code::kHeaderSize - kHeapObjectTag)); ++ __ Call(a2); ++ RecordCallPosition(instr); ++ frame_access_state()->ClearSPDelta(); ++ break; ++ } ++ case kArchPrepareCallCFunction: { ++ int const num_parameters = MiscField::decode(instr->opcode()); ++ __ PrepareCallCFunction(num_parameters, kScratchReg); ++ // Frame alignment requires using FP-relative frame addressing. ++ frame_access_state()->SetFrameAccessToFP(); ++ break; ++ } ++ case kArchSaveCallerRegisters: { ++ fp_mode_ = ++ static_cast(MiscField::decode(instr->opcode())); ++ DCHECK(fp_mode_ == SaveFPRegsMode::kIgnore || ++ fp_mode_ == SaveFPRegsMode::kSave); ++ // kReturnRegister0 should have been saved before entering the stub. ++ int bytes = __ PushCallerSaved(fp_mode_, kReturnRegister0); ++ DCHECK(IsAligned(bytes, kSystemPointerSize)); ++ DCHECK_EQ(0, frame_access_state()->sp_delta()); ++ frame_access_state()->IncreaseSPDelta(bytes / kSystemPointerSize); ++ DCHECK(!caller_registers_saved_); ++ caller_registers_saved_ = true; ++ break; ++ } ++ case kArchRestoreCallerRegisters: { ++ DCHECK(fp_mode_ == ++ static_cast(MiscField::decode(instr->opcode()))); ++ DCHECK(fp_mode_ == SaveFPRegsMode::kIgnore || ++ fp_mode_ == SaveFPRegsMode::kSave); ++ // Don't overwrite the returned value. ++ int bytes = __ PopCallerSaved(fp_mode_, kReturnRegister0); ++ frame_access_state()->IncreaseSPDelta(-(bytes / kSystemPointerSize)); ++ DCHECK_EQ(0, frame_access_state()->sp_delta()); ++ DCHECK(caller_registers_saved_); ++ caller_registers_saved_ = false; ++ break; ++ } ++ case kArchPrepareTailCall: ++ AssemblePrepareTailCall(); ++ break; ++ case kArchCallCFunction: { ++ int const num_parameters = MiscField::decode(instr->opcode()); ++#if V8_ENABLE_WEBASSEMBLY ++ Label start_call; ++ bool isWasmCapiFunction = ++ linkage()->GetIncomingDescriptor()->IsWasmCapiFunction(); ++ // from start_call to return address. ++ int offset = __ root_array_available() ? 64 : 112; ++#endif // V8_ENABLE_WEBASSEMBLY ++#if V8_HOST_ARCH_SW64 ++ if (FLAG_debug_code) { ++ offset += 16; ++ } ++#endif ++#if V8_ENABLE_WEBASSEMBLY ++ if (isWasmCapiFunction) { ++ // Put the return address in a stack slot. ++ __ mov(kScratchReg, ra); ++ __ bind(&start_call); ++ __ br(ra, 0); // __ nal(); // __ nop(); ++ __ Addl(ra, ra, offset - 4); // 4 = br(ra, 0); ++ __ stl(ra, MemOperand(fp, WasmExitFrameConstants::kCallingPCOffset)); ++ __ mov(ra, kScratchReg); ++ } ++#endif // V8_ENABLE_WEBASSEMBLY ++ if (instr->InputAt(0)->IsImmediate()) { ++ ExternalReference ref = i.InputExternalReference(0); ++ __ CallCFunction(ref, num_parameters); ++ } else { ++ Register func = i.InputRegister(0); ++ __ CallCFunction(func, num_parameters); ++ } ++#if V8_ENABLE_WEBASSEMBLY ++ if (isWasmCapiFunction) { ++ CHECK_EQ(offset, __ SizeOfCodeGeneratedSince(&start_call)); ++ RecordSafepoint(instr->reference_map()); ++ } ++#endif // V8_ENABLE_WEBASSEMBLY ++ frame_access_state()->SetFrameAccessToDefault(); ++ // Ideally, we should decrement SP delta to match the change of stack ++ // pointer in CallCFunction. However, for certain architectures (e.g. ++ // ARM), there may be more strict alignment requirement, causing old SP ++ // to be saved on the stack. In those cases, we can not calculate the SP ++ // delta statically. ++ frame_access_state()->ClearSPDelta(); ++ if (caller_registers_saved_) { ++ // Need to re-memb SP delta introduced in kArchSaveCallerRegisters. ++ // Here, we assume the sequence to be: ++ // kArchSaveCallerRegisters; ++ // kArchCallCFunction; ++ // kArchRestoreCallerRegisters; ++ int bytes = ++ __ RequiredStackSizeForCallerSaved(fp_mode_, kReturnRegister0); ++ frame_access_state()->IncreaseSPDelta(bytes / kSystemPointerSize); ++ } ++ break; ++ } ++ case kArchJmp: ++ AssembleArchJump(i.InputRpo(0)); ++ break; ++ case kArchBinarySearchSwitch: ++ AssembleArchBinarySearchSwitch(instr); ++ break; ++ case kArchTableSwitch: ++ AssembleArchTableSwitch(instr); ++ break; ++ case kArchAbortCSADcheck: ++ DCHECK(i.InputRegister(0) == a0); ++ { ++ // We don't actually want to generate a pile of code for this, so just ++ // claim there is a stack frame, without generating one. ++ FrameScope scope(tasm(), StackFrame::NO_FRAME_TYPE); ++ __ Call(isolate()->builtins()->code_handle(Builtin::kAbortCSADcheck), ++ RelocInfo::CODE_TARGET); ++ } ++ __ halt(); // stop("kArchDebugAbort"); ++ break; ++ case kArchDebugBreak: ++ __ halt(); // stop("kArchDebugBreak"); ++ break; ++ case kArchComment: ++ __ RecordComment(reinterpret_cast(i.InputInt64(0))); ++ break; ++ case kArchNop: ++ case kArchThrowTerminator: ++ // don't emit code for nops. ++ break; ++ case kArchDeoptimize: { ++ DeoptimizationExit* exit = ++ BuildTranslation(instr, -1, 0, 0, OutputFrameStateCombine::Ignore()); ++ __ Branch(exit->label()); ++ break; ++ } ++ case kArchRet: ++ AssembleReturn(instr->InputAt(0)); ++ break; ++ case kArchStackPointerGreaterThan: { ++ Register lhs_register = sp; ++ uint32_t offset; ++ if (ShouldApplyOffsetToStackCheck(instr, &offset)) { ++ lhs_register = i.TempRegister(1); ++ __ Subl(lhs_register, sp, offset); ++ } ++ __ Cmpult(i.TempRegister(0), i.InputRegister(0), lhs_register); ++ break; ++ } ++ case kArchStackCheckOffset: ++ __ Move(i.OutputRegister(), Smi::FromInt(GetStackCheckOffset())); ++ break; ++ case kArchFramePointer: ++ __ mov(i.OutputRegister(), fp); ++ break; ++ case kArchParentFramePointer: ++ if (frame_access_state()->has_frame()) { ++ __ Ldl(i.OutputRegister(), MemOperand(fp, 0)); ++ } else { ++ __ mov(i.OutputRegister(), fp); ++ } ++ break; ++ case kArchTruncateDoubleToI: ++ __ TruncateDoubleToI(isolate(), zone(), i.OutputRegister(), ++ i.InputDoubleRegister(0), DetermineStubCallMode()); ++ break; ++ case kArchStoreWithWriteBarrier: // Fall through. ++ case kArchAtomicStoreWithWriteBarrier: { ++ RecordWriteMode mode = ++ static_cast(MiscField::decode(instr->opcode())); ++ Register object = i.InputRegister(0); ++ Register index = i.InputRegister(1); ++ Register value = i.InputRegister(2); ++ Register scratch0 = i.TempRegister(0); ++ Register scratch1 = i.TempRegister(1); ++ auto ool = zone()->New(this, object, index, value, ++ scratch0, scratch1, mode, ++ DetermineStubCallMode()); ++ __ Addl(kScratchReg, object, index); ++ // SKTODO sync ++ if (arch_opcode == kArchStoreWithWriteBarrier) { ++ __ Stl(value, MemOperand(kScratchReg)); ++ } else { ++ DCHECK_EQ(kArchAtomicStoreWithWriteBarrier, arch_opcode); ++ __ memb(); ++ __ Stl(value, MemOperand(kScratchReg)); ++ __ memb(); ++ } ++ if (mode > RecordWriteMode::kValueIsPointer) { ++ __ JumpIfSmi(value, ool->exit()); ++ } ++ __ CheckPageFlag(object, scratch0, ++ MemoryChunk::kPointersFromHereAreInterestingMask, ne, ++ ool->entry()); ++ __ bind(ool->exit()); ++ break; ++ } ++ case kArchStackSlot: { ++ FrameOffset offset = ++ frame_access_state()->GetFrameOffset(i.InputInt32(0)); ++ Register base_reg = offset.from_stack_pointer() ? sp : fp; ++ __ Addl(i.OutputRegister(), base_reg, Operand(offset.offset())); ++ if (FLAG_debug_code) { ++ // Verify that the output_register is properly aligned ++ __ And(kScratchReg, i.OutputRegister(), ++ Operand(kSystemPointerSize - 1)); ++ __ Assert(eq, AbortReason::kAllocationIsNotDoubleAligned, kScratchReg, ++ Operand(zero_reg)); ++ } ++ break; ++ } ++ case kIeee754Float64Acos: ++ ASSEMBLE_IEEE754_UNOP(acos); ++ break; ++ case kIeee754Float64Acosh: ++ ASSEMBLE_IEEE754_UNOP(acosh); ++ break; ++ case kIeee754Float64Asin: ++ ASSEMBLE_IEEE754_UNOP(asin); ++ break; ++ case kIeee754Float64Asinh: ++ ASSEMBLE_IEEE754_UNOP(asinh); ++ break; ++ case kIeee754Float64Atan: ++ ASSEMBLE_IEEE754_UNOP(atan); ++ break; ++ case kIeee754Float64Atanh: ++ ASSEMBLE_IEEE754_UNOP(atanh); ++ break; ++ case kIeee754Float64Atan2: ++ ASSEMBLE_IEEE754_BINOP(atan2); ++ break; ++ case kIeee754Float64Cos: ++ ASSEMBLE_IEEE754_UNOP(cos); ++ break; ++ case kIeee754Float64Cosh: ++ ASSEMBLE_IEEE754_UNOP(cosh); ++ break; ++ case kIeee754Float64Cbrt: ++ ASSEMBLE_IEEE754_UNOP(cbrt); ++ break; ++ case kIeee754Float64Exp: ++ ASSEMBLE_IEEE754_UNOP(exp); ++ break; ++ case kIeee754Float64Expm1: ++ ASSEMBLE_IEEE754_UNOP(expm1); ++ break; ++ case kIeee754Float64Log: ++ ASSEMBLE_IEEE754_UNOP(log); ++ break; ++ case kIeee754Float64Log1p: ++ ASSEMBLE_IEEE754_UNOP(log1p); ++ break; ++ case kIeee754Float64Log2: ++ ASSEMBLE_IEEE754_UNOP(log2); ++ break; ++ case kIeee754Float64Log10: ++ ASSEMBLE_IEEE754_UNOP(log10); ++ break; ++ case kIeee754Float64Pow: ++ ASSEMBLE_IEEE754_BINOP(pow); ++ break; ++ case kIeee754Float64Sin: ++ ASSEMBLE_IEEE754_UNOP(sin); ++ break; ++ case kIeee754Float64Sinh: ++ ASSEMBLE_IEEE754_UNOP(sinh); ++ break; ++ case kIeee754Float64Tan: ++ ASSEMBLE_IEEE754_UNOP(tan); ++ break; ++ case kIeee754Float64Tanh: ++ ASSEMBLE_IEEE754_UNOP(tanh); ++ break; ++ case kSw64Addw: ++ __ Addw(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); ++ break; ++ case kSw64Addl: ++ __ Addl(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); ++ break; ++ case kSw64AddlOvf: ++ __ DaddOverflow(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1), ++ kScratchReg); ++ break; ++ case kSw64Subw: ++ __ Subw(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); ++ break; ++ case kSw64Subl: ++ __ Subl(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); ++ break; ++ case kSw64SublOvf: ++ __ SublOverflow(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1), ++ kScratchReg); ++ break; ++ case kSw64Mulw: ++ __ Mulw(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); ++ break; ++ case kSw64MulwOvf: ++ __ MulwOverflow(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1), ++ kScratchReg); ++ break; ++ case kSw64MulwHigh: ++ __ Mulwh(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); ++ break; ++ case kSw64MulwHighU: ++ __ Mulwhu(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); ++ break; ++ case kSw64MullHigh: ++ __ Mullh(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); ++ break; ++ break; ++ case kSw64Divw: { ++ __ Divw(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); ++ __ seleq(i.InputRegister(1), i.InputRegister(1), i.InputRegister(0), ++ i.OutputRegister()); ++ break; ++ } ++ case kSw64Divwu: { ++ __ Divwu(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); ++ // __ addw(i.OutputRegister(), 0, i.OutputRegister()); ++ __ seleq(i.InputRegister(1), i.InputRegister(1), i.InputRegister(0), ++ i.OutputRegister()); ++ break; ++ } ++ case kSw64Modw: { ++ __ Modw(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); ++ break; ++ } ++ case kSw64Modwu: { ++ __ Modwu(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); ++ break; ++ } ++ case kSw64Mull: ++ __ Mull(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); ++ break; ++ case kSw64Divl: { ++ Label ldiv, exit; ++ __ slll(i.InputRegister(0), 0xb, at); ++ __ sral(at, 0xb, at); ++ __ cmpeq(i.InputRegister(0), at, at); ++ __ beq(at, &ldiv); ++ __ Divl(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); ++ __ seleq(i.InputRegister(1), i.InputRegister(1), i.InputRegister(0), ++ i.OutputRegister()); ++ __ beq(zero_reg, &exit); ++ ++ __ bind(&ldiv); ++ RegList saved_regs = (kJSCallerSaved | ra); ++ saved_regs.clear(i.OutputRegister()); ++ __ MultiPush(saved_regs); ++ FrameScope scope(tasm(), StackFrame::MANUAL); ++ __ PrepareCallCFunction(2, 0, kScratchReg); ++ __ MovToGeneralParameters(i.InputRegister(0), i.InputRegister(1)); ++ __ CallCFunction(ExternalReference::math_sw_ddiv_function(), 2, 0); ++ __ MovFromGeneralResult(i.OutputRegister()); ++ __ MultiPop(saved_regs); ++ ++ __ bind(&exit); ++ break; ++ } ++ case kSw64Divlu: { ++ Label ldivu, exit; ++ __ blt(i.InputRegister(0), &ldivu); ++ __ blt(i.InputRegister(1), &ldivu); ++ __ srll(i.InputRegister(0), 0x35, at); ++ __ bne(at, &ldivu); ++ __ Divlu(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); ++ __ seleq(i.InputRegister(1), i.InputRegister(1), i.InputRegister(0), ++ i.OutputRegister()); ++ __ beq(zero_reg, &exit); ++ ++ __ bind(&ldivu); ++ RegList saved_regs = (kJSCallerSaved | ra); ++ saved_regs.clear(i.OutputRegister()); ++ __ MultiPush(saved_regs); ++ FrameScope scope(tasm(), StackFrame::MANUAL); ++ __ PrepareCallCFunction(2, 0, kScratchReg); ++ __ MovToGeneralParameters(i.InputRegister(0), i.InputRegister(1)); ++ __ CallCFunction(ExternalReference::math_sw_ddivu_function(), 2, 0); ++ __ MovFromGeneralResult(i.OutputRegister()); ++ __ MultiPop(saved_regs); ++ ++ __ bind(&exit); ++ break; ++ } ++ case kSw64Modl: { ++ Label modl, exit; ++ __ slll(i.InputRegister(0), 0xb, at); ++ __ sral(at, 0xb, at); ++ __ cmpeq(i.InputRegister(0), at, at); ++ __ beq(at, &modl); ++ __ Modl(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); ++ __ beq(zero_reg, &exit); ++ ++ __ bind(&modl); ++ RegList saved_regs = (kJSCallerSaved | ra); ++ saved_regs.clear(i.OutputRegister()); ++ __ MultiPush(saved_regs); ++ FrameScope scope(tasm(), StackFrame::MANUAL); ++ __ PrepareCallCFunction(2, 0, kScratchReg); ++ __ MovToGeneralParameters(i.InputRegister(0), i.InputRegister(1)); ++ __ CallCFunction(ExternalReference::math_sw_dmod_function(), 2, 0); ++ __ MovFromGeneralResult(i.OutputRegister()); ++ __ MultiPop(saved_regs); ++ ++ __ bind(&exit); ++ break; ++ } ++ case kSw64Modlu: { ++ Label modlu, exit; ++ __ blt(i.InputRegister(0), &modlu); ++ __ blt(i.InputRegister(1), &modlu); ++ __ srll(i.InputRegister(0), 0x35, at); ++ __ bne(at, &modlu); ++ __ Modlu(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); ++ __ beq(zero_reg, &exit); ++ ++ __ bind(&modlu); ++ RegList saved_regs = (kJSCallerSaved | ra); ++ saved_regs.clear(i.OutputRegister()); ++ __ MultiPush(saved_regs); ++ FrameScope scope(tasm(), StackFrame::MANUAL); ++ __ PrepareCallCFunction(2, 0, kScratchReg); ++ __ MovToGeneralParameters(i.InputRegister(0), i.InputRegister(1)); ++ __ CallCFunction(ExternalReference::math_sw_dmodu_function(), 2, 0); ++ __ MovFromGeneralResult(i.OutputRegister()); ++ __ MultiPop(saved_regs); ++ __ beq(zero_reg, &exit); ++ ++ __ bind(&exit); ++ break; ++ } ++ case kSw64Lsal: ++ DCHECK(instr->InputAt(2)->IsImmediate()); ++ __ Lsal(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1), ++ i.InputInt8(2)); ++ break; ++ case kSw64Lsaw: ++ DCHECK(instr->InputAt(2)->IsImmediate()); ++ __ Lsaw(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1), ++ i.InputInt8(2)); ++ break; ++ case kSw64Andl: ++ case kSw64Andw: ++ __ And(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); ++ break; ++ case kSw64Orl: ++ case kSw64Orw: ++ __ Or(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); ++ break; ++ case kSw64Norl: ++ case kSw64Norw: ++ __ Nor(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); ++ break; ++ case kSw64Xorl: ++ __ Xor(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); ++ break; ++ case kSw64Xorw: ++ __ Xor(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); ++ __ addw(i.OutputRegister(), 0x0, i.OutputRegister()); ++ break; ++ case kSw64Ctlzw: ++ __ Ctlzw(i.OutputRegister(), i.InputRegister(0)); ++ break; ++ case kSw64Ctlzl: ++ __ ctlz(i.InputRegister(0), i.OutputRegister()); ++ break; ++ case kSw64Cttzw: { ++ __ Cttzw(i.OutputRegister(), i.InputRegister(0)); ++ } break; ++ case kSw64Cttzl: { ++ __ Cttzl(i.OutputRegister(), i.InputRegister(0)); ++ } break; ++ case kSw64Ctpopw: { ++ __ Ctpopw(i.OutputRegister(), i.InputRegister(0)); ++ } break; ++ case kSw64Ctpopl: { ++ __ Ctpopl(i.OutputRegister(), i.InputRegister(0)); ++ } break; ++ case kSw64Sllw: ++ if (instr->InputAt(1)->IsRegister()) { ++ __ Sllw(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1)); ++ } else { ++ int64_t imm = i.InputOperand(1).immediate(); ++ if (imm == 0) { ++ __ addw(i.InputRegister(0), 0, i.OutputRegister()); ++ } else { ++ __ Sllw(i.OutputRegister(), i.InputRegister(0), ++ static_cast(imm)); ++ } ++ } ++ break; ++ case kSw64Srlw: ++ if (instr->InputAt(1)->IsRegister()) { ++ __ Srlw(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1)); ++ } else { ++ int64_t imm = i.InputOperand(1).immediate(); ++ __ Srlw(i.OutputRegister(), i.InputRegister(0), ++ static_cast(imm)); ++ } ++ break; ++ case kSw64Sraw: ++ if (instr->InputAt(1)->IsRegister()) { ++ __ Sraw(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1)); ++ } else { ++ int64_t imm = i.InputOperand(1).immediate(); ++ __ Sraw(i.OutputRegister(), i.InputRegister(0), ++ static_cast(imm)); ++ } ++ break; ++ case kSw64Extw: ++ __ Extw(i.OutputRegister(), i.InputRegister(0), i.InputInt8(1), ++ i.InputInt8(2)); ++ break; ++ case kSw64InsZerow: ++ if (instr->InputAt(1)->IsImmediate() && i.InputInt8(1) == 0) { ++ // __ Insw(i.OutputRegister(), zero_reg, i.InputInt8(1), ++ // i.InputInt8(2)); ++ if (i.InputInt8(2) == 8) { ++ __ zap(i.OutputRegister(), 0x1, i.OutputRegister()); ++ __ addw(i.OutputRegister(), 0, i.OutputRegister()); ++ } else if (i.InputInt8(2) == 16) { ++ __ zap(i.OutputRegister(), 0x3, i.OutputRegister()); ++ __ addw(i.OutputRegister(), 0, i.OutputRegister()); ++ } else if (i.InputInt8(2) == 24) { ++ __ zap(i.OutputRegister(), 0x7, i.OutputRegister()); ++ __ addw(i.OutputRegister(), 0, i.OutputRegister()); ++ } else if (i.InputInt8(2) == 32) { ++ __ bis(i.OutputRegister(), zero_reg, i.OutputRegister()); ++ } else { ++ long bitsize = (0x1L << i.InputInt8(2)) - 1; ++ if (is_uint8(bitsize)) { ++ __ bic(i.OutputRegister(), bitsize, i.OutputRegister()); ++ } else { ++ __ li(t11, bitsize); ++ __ bic(i.OutputRegister(), t11, i.OutputRegister()); ++ } ++ __ addw(i.OutputRegister(), 0, i.OutputRegister()); ++ } ++ } else { ++ __ Insw(i.OutputRegister(), i.InputRegister(0), i.InputInt8(1), ++ i.InputInt8(2)); ++ } ++ break; ++ case kSw64Extl: { ++ __ Extl(i.OutputRegister(), i.InputRegister(0), i.InputInt8(1), ++ i.InputInt8(2)); ++ break; ++ } ++ case kSw64InsZerod: ++ if (instr->InputAt(1)->IsImmediate() && i.InputInt8(1) == 0) { ++ // __ Insd(i.OutputRegister(), zero_reg, i.InputInt8(1), ++ // i.InputInt8(2)); ++ if (i.InputInt8(2) == 8) { ++ __ zap(i.OutputRegister(), 0x1, i.OutputRegister()); ++ } else if (i.InputInt8(2) == 16) { ++ __ zap(i.OutputRegister(), 0x3, i.OutputRegister()); ++ } else if (i.InputInt8(2) == 24) { ++ __ zap(i.OutputRegister(), 0x7, i.OutputRegister()); ++ } else if (i.InputInt8(2) == 32) { ++ __ zap(i.OutputRegister(), 0xf, i.OutputRegister()); ++ } else { ++ long bitsize = (0x1L << i.InputInt8(2)) - 1; ++ if (is_uint8(bitsize)) { ++ __ bic(i.OutputRegister(), bitsize, i.OutputRegister()); ++ } else { ++ __ li(t11, bitsize); ++ __ bic(i.OutputRegister(), t11, i.OutputRegister()); ++ } ++ } ++ } else { ++ __ Insd(i.OutputRegister(), i.InputRegister(0), i.InputInt8(1), ++ i.InputInt8(2)); ++ } ++ break; ++ case kSw64Slll: ++ if (instr->InputAt(1)->IsRegister()) { ++ __ slll(i.InputRegister(0), i.InputRegister(1), i.OutputRegister()); ++ } else { ++ int64_t imm = i.InputOperand(1).immediate(); ++ __ slll(i.InputRegister(0), static_cast(imm), ++ i.OutputRegister()); ++ } ++ break; ++ case kSw64Srll: ++ if (instr->InputAt(1)->IsRegister()) { ++ __ srll(i.InputRegister(0), i.InputRegister(1), i.OutputRegister()); ++ } else { ++ int64_t imm = i.InputOperand(1).immediate(); ++ __ srll(i.InputRegister(0), static_cast(imm), ++ i.OutputRegister()); ++ } ++ break; ++ case kSw64Sral: ++ if (instr->InputAt(1)->IsRegister()) { ++ __ sral(i.InputRegister(0), i.InputRegister(1), i.OutputRegister()); ++ } else { ++ int64_t imm = i.InputOperand(1).immediate(); ++ __ sral(i.InputRegister(0), imm, i.OutputRegister()); ++ } ++ break; ++ case kSw64Rolw: ++ __ Rolw(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); ++ break; ++ case kSw64Roll: ++ __ Roll(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); ++ break; ++ case kSw64Tst: ++ __ And(kScratchReg, i.InputRegister(0), i.InputOperand(1)); ++ // Pseudo-instruction used for cmp/branch. No opcode emitted here. ++ break; ++ case kSw64Cmp: ++ // Pseudo-instruction used for cmp/branch. No opcode emitted here. ++ break; ++ case kSw64Mov: ++ // TODO(plind): Should we combine mov/li like this, or use separate instr? ++ // - Also see x64 ASSEMBLE_BINOP & RegisterOrOperandType ++ if (HasRegisterInput(instr, 0)) { ++ __ mov(i.OutputRegister(), i.InputRegister(0)); ++ } else { ++ __ li(i.OutputRegister(), i.InputOperand(0)); ++ } ++ break; ++ ++ case kSw64Fcmps: { ++ FPURegister left = i.InputOrZeroSingleRegister(0); ++ FPURegister right = i.InputOrZeroSingleRegister(1); ++ bool predicate; ++ FPUCondition cc = ++ FlagsConditionToConditionCmpFPU(&predicate, instr->flags_condition()); ++ __ CompareF(cc, left, right); ++ } break; ++ case kSw64Fadds: ++ // TODO(plind): add special case: combine mult & add. ++ __ fadds(i.InputDoubleRegister(0), i.InputDoubleRegister(1), ++ i.OutputDoubleRegister()); ++ break; ++ case kSw64Fsubs: ++ __ fsubs(i.InputDoubleRegister(0), i.InputDoubleRegister(1), ++ i.OutputDoubleRegister()); ++ break; ++ case kSw64Fmuls: ++ // TODO(plind): add special case: right op is -1.0, see arm port. ++ __ fmuls(i.InputDoubleRegister(0), i.InputDoubleRegister(1), ++ i.OutputDoubleRegister()); ++ break; ++ case kSw64Fdivs: ++ __ fdivs(i.InputDoubleRegister(0), i.InputDoubleRegister(1), ++ i.OutputDoubleRegister()); ++ break; ++ case kSw64Frecs: ++ __ frecs(i.InputDoubleRegister(0), i.OutputDoubleRegister()); ++ break; ++ case kSw64Frecd: ++ __ frecd(i.InputDoubleRegister(0), i.OutputDoubleRegister()); ++ break; ++ case kSw64Fmods: { ++ // TODO(bmeurer): We should really get rid of this special instruction, ++ // and generate a CallAddress instruction instead. ++ FrameScope scope(tasm(), StackFrame::MANUAL); ++ __ PrepareCallCFunction(0, 2, kScratchReg); ++ __ MovToFloatParameters(i.InputDoubleRegister(0), ++ i.InputDoubleRegister(1)); ++ // TODO(balazs.kilvady): implement mod_two_floats_operation(isolate()) ++ __ CallCFunction(ExternalReference::mod_two_doubles_operation(), 0, 2); ++ // Move the result in the double result register. ++ __ MovFromFloatResult(i.OutputSingleRegister()); ++ break; ++ } ++ case kSw64Fabss: ++ case kSw64Fabsd: ++ __ Fabs(i.OutputDoubleRegister(), i.InputDoubleRegister(0)); ++ break; ++ case kSw64Fnegs: ++ case kSw64Fnegd: ++ __ Fneg(i.OutputDoubleRegister(), i.InputDoubleRegister(0)); ++ break; ++ case kSw64Fsqrts: { ++ __ fsqrts(i.InputDoubleRegister(0), i.OutputDoubleRegister()); ++ break; ++ } ++ ++ case kSw64Fcmpd: { ++ FPURegister left = i.InputOrZeroDoubleRegister(0); ++ FPURegister right = i.InputOrZeroDoubleRegister(1); ++ bool predicate; ++ FPUCondition cc = ++ FlagsConditionToConditionCmpFPU(&predicate, instr->flags_condition()); ++ __ CompareF(cc, left, right); ++ } break; ++ case kSw64Faddd: ++ // TODO(plind): add special case: combine mult & add. ++ __ faddd(i.InputDoubleRegister(0), i.InputDoubleRegister(1), ++ i.OutputDoubleRegister()); ++ break; ++ case kSw64Fsubd: ++ __ fsubd(i.InputDoubleRegister(0), i.InputDoubleRegister(1), ++ i.OutputDoubleRegister()); ++ break; ++ case kSw64Fmuld: ++ // TODO(plind): add special case: right op is -1.0, see arm port. ++ __ fmuld(i.InputDoubleRegister(0), i.InputDoubleRegister(1), ++ i.OutputDoubleRegister()); ++ break; ++ case kSw64Fdivd: ++ __ fdivd(i.InputDoubleRegister(0), i.InputDoubleRegister(1), ++ i.OutputDoubleRegister()); ++ break; ++ case kSw64Fmodd: { ++ // TODO(bmeurer): We should really get rid of this special instruction, ++ // and generate a CallAddress instruction instead. ++ FrameScope scope(tasm(), StackFrame::MANUAL); ++ __ PrepareCallCFunction(0, 2, kScratchReg); ++ __ MovToFloatParameters(i.InputDoubleRegister(0), ++ i.InputDoubleRegister(1)); ++ __ CallCFunction(ExternalReference::mod_two_doubles_operation(), 0, 2); ++ // Move the result in the double result register. ++ __ MovFromFloatResult(i.OutputDoubleRegister()); ++ break; ++ } ++ case kSw64Fsqrtd: { ++ __ fsqrtd(i.InputDoubleRegister(0), i.OutputDoubleRegister()); ++ break; ++ } ++ ++ case kSw64Float64RoundDown: { ++ __ Floord(i.OutputDoubleRegister(), i.InputDoubleRegister(0)); ++ break; ++ } ++ case kSw64Float32RoundDown: { ++ __ Floors(i.OutputSingleRegister(), i.InputSingleRegister(0)); ++ break; ++ } ++ case kSw64Float64RoundTruncate: { ++ __ Truncd(i.OutputDoubleRegister(), i.InputDoubleRegister(0)); ++ break; ++ } ++ case kSw64Float32RoundTruncate: { ++ __ Truncs(i.OutputSingleRegister(), i.InputSingleRegister(0)); ++ break; ++ } ++ case kSw64Float64RoundUp: { ++ __ Ceild(i.OutputDoubleRegister(), i.InputDoubleRegister(0)); ++ break; ++ } ++ case kSw64Float32RoundUp: { ++ __ Ceils(i.OutputSingleRegister(), i.InputSingleRegister(0)); ++ break; ++ } ++ case kSw64Float64RoundTiesEven: { ++ __ Roundd(i.OutputDoubleRegister(), i.InputDoubleRegister(0)); ++ break; ++ } ++ case kSw64Float32RoundTiesEven: { ++ __ Rounds(i.OutputSingleRegister(), i.InputSingleRegister(0)); ++ break; ++ } ++ case kSw64Float32Max: { ++ FPURegister dst = i.OutputSingleRegister(); ++ FPURegister src1 = i.InputSingleRegister(0); ++ FPURegister src2 = i.InputSingleRegister(1); ++ auto ool = zone()->New(this, dst, src1, src2); ++ __ Float32Max(dst, src1, src2, ool->entry()); ++ __ bind(ool->exit()); ++ break; ++ } ++ case kSw64Float64Max: { ++ FPURegister dst = i.OutputDoubleRegister(); ++ FPURegister src1 = i.InputDoubleRegister(0); ++ FPURegister src2 = i.InputDoubleRegister(1); ++ auto ool = zone()->New(this, dst, src1, src2); ++ __ Float64Max(dst, src1, src2, ool->entry()); ++ __ bind(ool->exit()); ++ break; ++ } ++ case kSw64Float32Min: { ++ FPURegister dst = i.OutputSingleRegister(); ++ FPURegister src1 = i.InputSingleRegister(0); ++ FPURegister src2 = i.InputSingleRegister(1); ++ auto ool = zone()->New(this, dst, src1, src2); ++ __ Float32Min(dst, src1, src2, ool->entry()); ++ __ bind(ool->exit()); ++ break; ++ } ++ case kSw64Float64Min: { ++ FPURegister dst = i.OutputDoubleRegister(); ++ FPURegister src1 = i.InputDoubleRegister(0); ++ FPURegister src2 = i.InputDoubleRegister(1); ++ auto ool = zone()->New(this, dst, src1, src2); ++ __ Float64Min(dst, src1, src2, ool->entry()); ++ __ bind(ool->exit()); ++ break; ++ } ++ case kSw64Float64SilenceNaN: ++ __ FPUCanonicalizeNaN(i.OutputDoubleRegister(), i.InputDoubleRegister(0)); ++ break; ++ case kSw64F64ToF32: ++ __ fcvtds(i.InputDoubleRegister(0), i.OutputSingleRegister()); ++ break; ++ case kSw64F32ToF64: ++ __ fcvtsd(i.InputSingleRegister(0), i.OutputDoubleRegister()); ++ break; ++ case kSw64I32ToF32: { ++ FPURegister scratch = kScratchDoubleReg; ++ __ ifmovs(i.InputRegister(0), scratch); ++ __ fcvtws(scratch, i.OutputDoubleRegister()); ++ break; ++ } ++ case kSw64UI32ToF32: { ++ __ Cvt_s_uw(i.OutputDoubleRegister(), i.InputRegister(0)); ++ break; ++ } ++ case kSw64I64ToF32: { ++ FPURegister scratch = kScratchDoubleReg; ++ __ ifmovd(i.InputRegister(0), scratch); ++ __ fcvtls(scratch, i.OutputDoubleRegister()); ++ break; ++ } ++ case kSw64UI64ToF32: { ++ __ Cvt_s_ul(i.OutputDoubleRegister(), i.InputRegister(0)); ++ break; ++ } ++ case kSw64I32ToF64: { ++ FPURegister scratch = kScratchDoubleReg; ++ __ ifmovs(i.InputRegister(0), scratch); ++ __ fcvtwd(scratch, i.OutputDoubleRegister()); ++ break; ++ } ++ case kSw64UI32ToF64: { ++ __ Cvt_d_uw(i.OutputDoubleRegister(), i.InputRegister(0)); ++ break; ++ } ++ case kSw64I64ToF64: { ++ FPURegister scratch = kScratchDoubleReg; ++ __ ifmovd(i.InputRegister(0), scratch); ++ __ fcvtld(scratch, i.OutputDoubleRegister()); ++ break; ++ } ++ case kSw64UI64ToF64: { ++ __ Cvt_d_ul(i.OutputDoubleRegister(), i.InputRegister(0)); ++ break; ++ } ++ case kSw64F64ToI32_n: { ++ FPURegister scratch = kScratchDoubleReg; ++ __ ffloordw(i.InputDoubleRegister(0), scratch); ++ __ fimovs(scratch, i.OutputRegister()); ++ break; ++ } ++ case kSw64F64ToI32_p: { ++ FPURegister scratch = kScratchDoubleReg; ++ __ fceildw(i.InputDoubleRegister(0), scratch); ++ __ fimovs(scratch, i.OutputRegister()); ++ break; ++ } ++ case kSw64F64ToI32_g: { ++ FPURegister scratch = kScratchDoubleReg; ++ __ frounddw(i.InputDoubleRegister(0), scratch); ++ __ fimovs(scratch, i.OutputRegister()); ++ break; ++ } ++ case kSw64F64ToI32: { ++ FPURegister scratch = kScratchDoubleReg; ++ // Other arches use round to zero here, so we follow. ++ __ ftruncdw(i.InputDoubleRegister(0), scratch); ++ __ fimovs(scratch, i.OutputRegister()); ++ break; ++ } ++ case kSw64F32ToI32_n: { ++ FPURegister scratch = kScratchDoubleReg; ++ __ ffloorsw(i.InputDoubleRegister(0), scratch); ++ __ fimovs(scratch, i.OutputRegister()); ++ break; ++ } ++ case kSw64F32ToI32_p: { ++ FPURegister scratch = kScratchDoubleReg; ++ __ fceilsw(i.InputDoubleRegister(0), scratch); ++ __ fimovs(scratch, i.OutputRegister()); ++ break; ++ } ++ case kSw64F32ToI32_g: { ++ FPURegister scratch = kScratchDoubleReg; ++ __ froundsw(i.InputDoubleRegister(0), scratch); ++ __ fimovs(scratch, i.OutputRegister()); ++ break; ++ } ++ case kSw64F32ToI32: { ++ FPURegister scratch = kScratchDoubleReg; ++ __ ftruncsw(i.InputDoubleRegister(0), scratch); ++ __ fimovs(scratch, i.OutputRegister()); ++ // Avoid INT32_MAX as an overflow indicator and use INT32_MIN instead, ++ // because INT32_MIN allows easier out-of-bounds detection. ++ bool set_overflow_to_min_i32 = MiscField::decode(instr->opcode()); ++ if (set_overflow_to_min_i32) { ++ __ Addw(kScratchReg, i.OutputRegister(), Operand(1)); ++ __ cmplt(kScratchReg, i.OutputRegister(), kScratchReg2); ++ __ Selne(i.OutputRegister(), kScratchReg, kScratchReg2); ++ } ++ break; ++ } ++ case kSw64F32ToI64: { ++ FPURegister scratch = kScratchDoubleReg; ++ FPURegister scratch1 = kScratchDoubleReg1; ++ FPURegister scratch2 = kScratchDoubleReg2; ++ Register temp = kScratchReg; ++ Register result = kScratchReg2; ++ ++ bool load_status = instr->OutputCount() > 1; ++ if (load_status) { ++ // Save FPCR. ++ __ rfpcr(scratch2); ++ // SW64 neednot clear FPCR in 20150513. ++ // in order to have same effection, we should do four steps in sw: ++ // 1) set fpcr = 0 ++ // 2) Rounding: sw(10), round-to-even ++ // 3) set trap bit: sw(62~61,51~49), exception controlled by fpcr but ++ // not trap 4) set exception mode: sw(00) setfpec0 ++ if ((strcmp(FLAG_sw64_arch, "sw8a") == 0) || ++ CpuFeatures::IsSupported(SW_8A)) { ++ __ li(temp, sFPCR_8aControlMask | sFPCRRound1Mask); // 1), 2), 3) ++ } else { ++ __ li(temp, sFPCRControlMask | sFPCRRound1Mask); // 1), 2), 3) ++ } ++ __ ifmovd(temp, scratch1); ++ __ wfpcr(scratch1); ++ __ setfpec1(); // 4) ++ } ++ // Other arches use round to zero here, so we follow. ++ __ fcvtsd(i.InputDoubleRegister(0), scratch); ++ __ fcvtdl_z(scratch, scratch1); ++ __ fimovd(scratch1, i.OutputRegister()); ++ ++ if (load_status) { ++ __ rfpcr(scratch1); ++ __ fimovd(scratch1, result); ++ ++ // Check for overflow and NaNs. ++ if ((strcmp(FLAG_sw64_arch, "sw8a") == 0) || ++ CpuFeatures::IsSupported(SW_8A)) { ++ __ li(temp, sFPCROverflowFlagMask | sFPCRUnderflowFlagMask | ++ sFPCRInvalidOpFlagMask | ++ sFPCROverflowIntegerFlagMask | sFPCRInexactFlagBit); ++ } else { ++ __ li(temp, sFPCROverflowFlagMask | sFPCRUnderflowFlagMask | ++ sFPCRInvalidOpFlagMask); ++ } ++ __ and_(result, temp, result); ++ __ Cmplt(result, zero_reg, result); ++ __ xor_(result, 1, result); ++ __ mov(i.OutputRegister(1), result); ++ // Restore FPCR ++ __ wfpcr(scratch2); ++ __ setfpec1(); ++ } ++ break; ++ } ++ case kSw64F64ToI64: { ++ FPURegister scratch = kScratchDoubleReg; ++ FPURegister scratch1 = kScratchDoubleReg1; ++ FPURegister scratch2 = kScratchDoubleReg2; ++ Register temp = kScratchReg; ++ Register result = kScratchReg2; ++ ++ bool set_overflow_to_min_i64 = MiscField::decode(instr->opcode()); ++ bool load_status = instr->OutputCount() > 1; ++ DCHECK_IMPLIES(set_overflow_to_min_i64, instr->OutputCount() == 1); ++ if (load_status) { ++ // Save FPCR. ++ __ rfpcr(scratch2); ++ // SW64 neednot clear FPCR in 20150513. ++ // in order to have same effection, we should do four steps in sw: ++ // 1) set fpcr = 0 ++ // 2) Rounding: sw(10), round-to-even ++ // 3) set trap bit: sw(62~61,51~49), exception controlled by fpcr but ++ // not trap 4) set exception mode: sw(00) setfpec0 ++ if ((strcmp(FLAG_sw64_arch, "sw8a") == 0) || ++ CpuFeatures::IsSupported(SW_8A)) { ++ __ li(temp, sFPCR_8aControlMask | sFPCRRound1Mask); // 1), 2), 3) ++ } else { ++ __ li(temp, sFPCRControlMask | sFPCRRound1Mask); // 1), 2), 3) ++ } ++ __ ifmovd(temp, scratch1); ++ __ wfpcr(scratch1); ++ __ setfpec1(); // 4) ++ } ++ // Other arches use round to zero here, so we follow. ++ __ ftruncdl(i.InputDoubleRegister(0), scratch); ++ __ fimovd(scratch, i.OutputRegister(0)); ++ if (load_status) { ++ __ rfpcr(scratch1); ++ __ fimovd(scratch1, result); ++ ++ // Check for overflow and NaNs. ++ if ((strcmp(FLAG_sw64_arch, "sw8a") == 0) || ++ CpuFeatures::IsSupported(SW_8A)) { ++ __ li(temp, sFPCROverflowFlagMask | sFPCRUnderflowFlagMask | ++ sFPCRInvalidOpFlagMask | ++ sFPCROverflowIntegerFlagMask | sFPCRInexactFlagBit); ++ } else { ++ __ li(temp, sFPCROverflowFlagMask | sFPCRUnderflowFlagMask | ++ sFPCRInvalidOpFlagMask); ++ } ++ __ and_(result, temp, result); ++ __ Cmplt(result, zero_reg, result); ++ __ xor_(result, 1, result); ++ __ mov(i.OutputRegister(1), result); ++ // Restore FPCR ++ __ wfpcr(scratch2); ++ __ setfpec1(); ++ if (set_overflow_to_min_i64) { ++ // Avoid INT64_MAX as an overflow indicator and use INT64_MIN instead, ++ // because INT64_MIN allows easier out-of-bounds detection. ++ __ Addl(kScratchReg, i.OutputRegister(), Operand(1)); ++ __ cmplt(kScratchReg, i.OutputRegister(), kScratchReg2); ++ __ Selne(i.OutputRegister(), kScratchReg, kScratchReg2); ++ } ++ } ++ break; ++ } ++ case kSw64F64ToUI32: { ++ FPURegister scratch = kScratchDoubleReg; ++ __ Trunc_uw_d(i.OutputRegister(), i.InputDoubleRegister(0), scratch); ++ break; ++ } ++ case kSw64F32ToUI32: { ++ FPURegister scratch = kScratchDoubleReg; ++ __ Trunc_uw_s(i.OutputRegister(), i.InputDoubleRegister(0), scratch); ++ bool set_overflow_to_min_i32 = MiscField::decode(instr->opcode()); ++ if (set_overflow_to_min_i32) { ++ // Avoid UINT32_MAX as an overflow indicator and use 0 instead, ++ // because 0 allows easier out-of-bounds detection. ++ __ Addw(kScratchReg, i.OutputRegister(), Operand(1)); ++ __ Seleq(i.OutputRegister(), zero_reg, kScratchReg); ++ } ++ break; ++ } ++ case kSw64F32ToUI64: { ++ FPURegister scratch = kScratchDoubleReg; ++ Register result = instr->OutputCount() > 1 ? i.OutputRegister(1) : no_reg; ++ __ Trunc_ul_s(i.OutputRegister(), i.InputDoubleRegister(0), scratch, ++ result); ++ break; ++ } ++ case kSw64F64ToUI64: { ++ FPURegister scratch = kScratchDoubleReg; ++ Register result = instr->OutputCount() > 1 ? i.OutputRegister(1) : no_reg; ++ __ Trunc_ul_d(i.OutputRegister(0), i.InputDoubleRegister(0), scratch, ++ result); ++ break; ++ } ++ case kSw64BitcastDL: ++ __ fimovd(i.InputDoubleRegister(0), i.OutputRegister()); ++ break; ++ case kSw64BitcastLD: ++ __ ifmovd(i.InputRegister(0), i.OutputDoubleRegister()); ++ break; ++ case kSw64BitcastSW: ++ __ fimovs(i.InputDoubleRegister(0), i.OutputRegister()); ++ break; ++ case kSw64BitcastWS: ++ __ ifmovs(i.InputRegister(0), i.OutputDoubleRegister()); ++ break; ++ case kSw64Float64ExtractLowWord32: ++ __ FmoveLow(i.OutputRegister(), i.InputDoubleRegister(0)); ++ break; ++ case kSw64Float64ExtractHighWord32: ++ __ FmoveHigh(i.OutputRegister(), i.InputDoubleRegister(0)); ++ break; ++ case kSw64Float64InsertLowWord32: ++ __ FmoveLow(i.OutputDoubleRegister(), i.InputRegister(1)); ++ break; ++ case kSw64Float64InsertHighWord32: ++ __ FmoveHigh(i.OutputDoubleRegister(), i.InputRegister(1)); ++ break; ++ case kSw64Sextb: ++ __ sextb(i.InputRegister(0), i.OutputRegister()); ++ break; ++ case kSw64Sexth: ++ __ sexth(i.InputRegister(0), i.OutputRegister()); ++ break; ++ case kSw64Ldb: ++ __ Ldb(i.OutputRegister(), i.MemoryOperand()); ++ break; ++ case kSw64Ldbu: ++ __ Ldbu(i.OutputRegister(), i.MemoryOperand()); ++ break; ++ case kSw64Stb: { ++ __ Stb(i.InputOrZeroRegister(2), i.MemoryOperand()); ++ break; ++ } ++ case kSw64Ldh: ++ __ Ldh(i.OutputRegister(), i.MemoryOperand()); ++ break; ++ case kSw64Ldh_u: ++ __ Ldh_u(i.OutputRegister(), i.MemoryOperand()); ++ break; ++ case kSw64Ldhu: ++ __ Ldhu(i.OutputRegister(), i.MemoryOperand()); ++ break; ++ case kSw64Ldhu_u: ++ __ Ldhu_u(i.OutputRegister(), i.MemoryOperand()); ++ break; ++ case kSw64Sth: { ++ __ Sth(i.InputOrZeroRegister(2), i.MemoryOperand()); ++ break; ++ } ++ case kSw64Sth_u: { ++ __ Sth_u(i.InputOrZeroRegister(2), i.MemoryOperand(), kScratchReg); ++ break; ++ } ++ case kSw64Ldw: ++ __ Ldw(i.OutputRegister(), i.MemoryOperand()); ++ break; ++ case kSw64Ldw_u: ++ __ Ldw_u(i.OutputRegister(), i.MemoryOperand()); ++ break; ++ case kSw64Ldwu: ++ __ Ldwu(i.OutputRegister(), i.MemoryOperand()); ++ break; ++ case kSw64Ldwu_u: ++ __ Ldwu_u(i.OutputRegister(), i.MemoryOperand()); ++ break; ++ case kSw64Stw: { ++ __ Stw(i.InputOrZeroRegister(2), i.MemoryOperand()); ++ break; ++ } ++ case kSw64Stw_u: { ++ __ Stw_u(i.InputOrZeroRegister(2), i.MemoryOperand()); ++ break; ++ } ++ case kSw64Ldl: ++ __ Ldl(i.OutputRegister(), i.MemoryOperand()); ++ break; ++ case kSw64Ldl_u: ++ __ Ldl_u(i.OutputRegister(), i.MemoryOperand()); ++ break; ++ case kSw64Stl: { ++ __ Stl(i.InputOrZeroRegister(2), i.MemoryOperand()); ++ break; ++ } ++ case kSw64Stl_u: { ++ __ Stl_u(i.InputOrZeroRegister(2), i.MemoryOperand()); ++ break; ++ } ++ case kSw64Flds: { ++ __ Flds(i.OutputSingleRegister(), i.MemoryOperand()); ++ break; ++ } ++ case kSw64Flds_u: { ++ __ Flds_u(i.OutputSingleRegister(), i.MemoryOperand(), kScratchReg); ++ break; ++ } ++ case kSw64Fsts: { ++ size_t index = 0; ++ MemOperand operand = i.MemoryOperand(&index); ++ FPURegister ft = i.InputOrZeroSingleRegister(index); ++ __ Fsts(ft, operand); ++ break; ++ } ++ case kSw64Fsts_u: { ++ size_t index = 0; ++ MemOperand operand = i.MemoryOperand(&index); ++ FPURegister ft = i.InputOrZeroSingleRegister(index); ++ __ Fsts_u(ft, operand, kScratchReg); ++ break; ++ } ++ case kSw64Fldd: ++ __ Fldd(i.OutputDoubleRegister(), i.MemoryOperand()); ++ break; ++ case kSw64Fldd_u: ++ __ Fldd_u(i.OutputDoubleRegister(), i.MemoryOperand(), kScratchReg); ++ break; ++ case kSw64Fstd: { ++ FPURegister ft = i.InputOrZeroDoubleRegister(2); ++ if (ft == kDoubleRegZero && !__ IsDoubleZeroRegSet()) { ++ __ Move(kDoubleRegZero, 0.0); ++ } ++ __ Fstd(ft, i.MemoryOperand()); ++ break; ++ } ++ case kSw64Fstd_u: { ++ FPURegister ft = i.InputOrZeroDoubleRegister(2); ++ if (ft == kDoubleRegZero && !__ IsDoubleZeroRegSet()) { ++ __ Move(kDoubleRegZero, 0.0); ++ } ++ __ Fstd_u(ft, i.MemoryOperand(), kScratchReg); ++ break; ++ } ++ case kSw64Memb: { ++ __ memb(); ++ break; ++ } ++ case kSw64Push: ++ if (instr->InputAt(0)->IsFPRegister()) { ++ __ Fstd(i.InputDoubleRegister(0), MemOperand(sp, -kDoubleSize)); ++ __ Subw(sp, sp, Operand(kDoubleSize)); ++ frame_access_state()->IncreaseSPDelta(kDoubleSize / kSystemPointerSize); ++ } else { ++ __ Push(i.InputRegister(0)); ++ frame_access_state()->IncreaseSPDelta(1); ++ } ++ break; ++ case kSw64Peek: { ++ int reverse_slot = i.InputInt32(0); ++ int offset = ++ FrameSlotToFPOffset(frame()->GetTotalFrameSlotCount() - reverse_slot); ++ if (instr->OutputAt(0)->IsFPRegister()) { ++ LocationOperand* op = LocationOperand::cast(instr->OutputAt(0)); ++ if (op->representation() == MachineRepresentation::kFloat64) { ++ __ Fldd(i.OutputDoubleRegister(), MemOperand(fp, offset)); ++ } else if (op->representation() == MachineRepresentation::kFloat32) { ++ __ Flds(i.OutputSingleRegister(0), MemOperand(fp, offset)); ++ } ++ } else { ++ __ Ldl(i.OutputRegister(0), MemOperand(fp, offset)); ++ } ++ break; ++ } ++ case kSw64StackClaim: { ++ __ Subl(sp, sp, Operand(i.InputInt32(0))); ++ frame_access_state()->IncreaseSPDelta(i.InputInt32(0) / kPointerSize); ++ break; ++ } ++ case kSw64Poke: { ++ if (instr->InputAt(0)->IsFPRegister()) { ++ if (instr->InputAt(0)->IsDoubleRegister()) { ++ __ Fstd(i.InputDoubleRegister(0), MemOperand(sp, i.InputInt32(1))); ++ } else { ++ __ Fsts(i.InputFloatRegister(0), MemOperand(sp, i.InputInt32(1))); ++ } ++ } else { ++ __ Stl(i.InputRegister(0), MemOperand(sp, i.InputInt32(1))); ++ } ++ break; ++ } ++ case kSw64ByteSwap64: { ++ __ ByteSwapSigned(i.OutputRegister(0), i.InputRegister(0), 8); ++ break; ++ } ++ case kSw64ByteSwap32: { ++ __ ByteSwapSigned(i.OutputRegister(0), i.InputRegister(0), 4); ++ break; ++ } ++ ++ case kAtomicLoadInt8: ++ DCHECK_EQ(AtomicWidthField::decode(opcode), AtomicWidth::kWord32); ++ ASSEMBLE_ATOMIC_LOAD_INTEGER(Ldb); ++ break; ++ case kAtomicLoadUint8: ++ ASSEMBLE_ATOMIC_LOAD_INTEGER(Ldbu); ++ break; ++ case kAtomicLoadInt16: ++ DCHECK_EQ(AtomicWidthField::decode(opcode), AtomicWidth::kWord32); ++ ASSEMBLE_ATOMIC_LOAD_INTEGER(Ldh); ++ break; ++ case kAtomicLoadUint16: ++ ASSEMBLE_ATOMIC_LOAD_INTEGER(Ldhu); ++ break; ++ case kAtomicLoadWord32: ++ if (AtomicWidthField::decode(opcode) == AtomicWidth::kWord32) ++ ASSEMBLE_ATOMIC_LOAD_INTEGER(Ldw); ++ else ++ ASSEMBLE_ATOMIC_LOAD_INTEGER(Ldwu); ++ break; ++ case kSw64Word64AtomicLoadUint64: ++ ASSEMBLE_ATOMIC_LOAD_INTEGER(Ldl); ++ break; ++ case kAtomicStoreWord8: ++ ASSEMBLE_ATOMIC_STORE_INTEGER(Stb); ++ break; ++ case kAtomicStoreWord16: ++ ASSEMBLE_ATOMIC_STORE_INTEGER(Sth); ++ break; ++ case kAtomicStoreWord32: ++ ASSEMBLE_ATOMIC_STORE_INTEGER(Stw); ++ break; ++ case kSw64Word64AtomicStoreWord64: ++ ASSEMBLE_ATOMIC_STORE_INTEGER(Stl); ++ break; ++ case kSw64StoreCompressTagged: ++ __ Stl(i.InputOrZeroRegister(2), i.MemoryOperand()); ++ break; ++ case kAtomicExchangeInt8: ++ DCHECK_EQ(AtomicWidthField::decode(opcode), AtomicWidth::kWord32); ++ ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(lldw, lstw, true, 8, 32); ++ break; ++ case kAtomicExchangeUint8: ++ switch (AtomicWidthField::decode(opcode)) { ++ case AtomicWidth::kWord32: ++ ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(lldw, lstw, false, 8, 32); ++ break; ++ case AtomicWidth::kWord64: ++ ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(lldl, lstl, false, 8, 64); ++ break; ++ } ++ break; ++ case kAtomicExchangeInt16: ++ DCHECK_EQ(AtomicWidthField::decode(opcode), AtomicWidth::kWord32); ++ ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(lldw, lstw, true, 16, 32); ++ break; ++ case kAtomicExchangeUint16: ++ switch (AtomicWidthField::decode(opcode)) { ++ case AtomicWidth::kWord32: ++ ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(lldw, lstw, false, 16, 32); ++ break; ++ case AtomicWidth::kWord64: ++ ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(lldl, lstl, false, 16, 64); ++ break; ++ } ++ break; ++ case kAtomicExchangeWord32: ++ switch (AtomicWidthField::decode(opcode)) { ++ case AtomicWidth::kWord32: ++ ASSEMBLE_ATOMIC_EXCHANGE_INTEGER(lldw, lstw); ++ break; ++ case AtomicWidth::kWord64: ++ ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(lldl, lstl, false, 32, 64); ++ break; ++ } ++ break; ++ case kSw64Word64AtomicExchangeUint64: ++ ASSEMBLE_ATOMIC_EXCHANGE_INTEGER(lldl, lstl); ++ break; ++ case kAtomicCompareExchangeInt8: ++ DCHECK_EQ(AtomicWidthField::decode(opcode), AtomicWidth::kWord32); ++ ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT(lldw, lstw, true, 8, 32); ++ break; ++ case kAtomicCompareExchangeUint8: ++ switch (AtomicWidthField::decode(opcode)) { ++ case AtomicWidth::kWord32: ++ ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT(lldw, lstw, false, 8, ++ 32); ++ break; ++ case AtomicWidth::kWord64: ++ ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT(lldl, lstl, false, 8, ++ 64); ++ break; ++ } ++ break; ++ case kAtomicCompareExchangeInt16: ++ DCHECK_EQ(AtomicWidthField::decode(opcode), AtomicWidth::kWord32); ++ ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT(lldw, lstw, true, 16, 32); ++ break; ++ case kAtomicCompareExchangeUint16: ++ switch (AtomicWidthField::decode(opcode)) { ++ case AtomicWidth::kWord32: ++ ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT(lldw, lstw, false, 16, ++ 32); ++ break; ++ case AtomicWidth::kWord64: ++ ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT(lldl, lstl, false, 16, ++ 64); ++ break; ++ } ++ break; ++ case kAtomicCompareExchangeWord32: ++ switch (AtomicWidthField::decode(opcode)) { ++ case AtomicWidth::kWord32: ++ __ addw(i.InputRegister(2), 0x0, i.InputRegister(2)); ++ ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER(lldw, lstw); ++ break; ++ case AtomicWidth::kWord64: ++ ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT(lldl, lstl, false, 32, ++ 64); ++ break; ++ } ++ break; ++ case kSw64Word64AtomicCompareExchangeUint64: ++ ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER(lldl, lstl); ++ break; ++#define ATOMIC_BINOP_CASE(op, inst32, inst64) \ ++ case kAtomic##op##Int8: \ ++ DCHECK_EQ(AtomicWidthField::decode(opcode), AtomicWidth::kWord32); \ ++ ASSEMBLE_ATOMIC_BINOP_EXT(lldw, lstw, true, 8, inst32, 32); \ ++ break; \ ++ case kAtomic##op##Uint8: \ ++ switch (AtomicWidthField::decode(opcode)) { \ ++ case AtomicWidth::kWord32: \ ++ ASSEMBLE_ATOMIC_BINOP_EXT(lldw, lstw, false, 8, inst32, 32); \ ++ break; \ ++ case AtomicWidth::kWord64: \ ++ ASSEMBLE_ATOMIC_BINOP_EXT(lldl, lstl, false, 8, inst64, 64); \ ++ break; \ ++ } \ ++ break; \ ++ case kAtomic##op##Int16: \ ++ DCHECK_EQ(AtomicWidthField::decode(opcode), AtomicWidth::kWord32); \ ++ ASSEMBLE_ATOMIC_BINOP_EXT(lldw, lstw, true, 16, inst32, 32); \ ++ break; \ ++ case kAtomic##op##Uint16: \ ++ switch (AtomicWidthField::decode(opcode)) { \ ++ case AtomicWidth::kWord32: \ ++ ASSEMBLE_ATOMIC_BINOP_EXT(lldw, lstw, false, 16, inst32, 32); \ ++ break; \ ++ case AtomicWidth::kWord64: \ ++ ASSEMBLE_ATOMIC_BINOP_EXT(lldl, lstl, false, 16, inst64, 64); \ ++ break; \ ++ } \ ++ break; \ ++ case kAtomic##op##Word32: \ ++ switch (AtomicWidthField::decode(opcode)) { \ ++ case AtomicWidth::kWord32: \ ++ ASSEMBLE_ATOMIC_BINOP(lldw, lstw, inst32); \ ++ break; \ ++ case AtomicWidth::kWord64: \ ++ ASSEMBLE_ATOMIC_BINOP_EXT(lldl, lstl, false, 32, inst64, 64); \ ++ break; \ ++ } \ ++ break; \ ++ case kSw64Word64Atomic##op##Uint64: \ ++ ASSEMBLE_ATOMIC_BINOP(lldl, lstl, inst64); \ ++ break; ++ ATOMIC_BINOP_CASE(Add, Addw, Addl) ++ ATOMIC_BINOP_CASE(Sub, Subw, Subl) ++ ATOMIC_BINOP_CASE(And, And, And) ++ ATOMIC_BINOP_CASE(Or, Or, Or) ++ ATOMIC_BINOP_CASE(Xor, Xor, Xor) ++#undef ATOMIC_BINOP_CASE ++ case kSw64AssertEqual: ++ __ Assert(eq, static_cast(i.InputOperand(2).immediate()), ++ i.InputRegister(0), Operand(i.InputRegister(1))); ++ break; ++ case kSw64S128Const: ++ case kSw64S128Zero: ++ case kSw64I32x4Splat: ++ case kSw64I32x4ExtractLane: ++ case kSw64I32x4Add: ++ case kSw64I32x4ReplaceLane: ++ case kSw64I32x4Sub: ++ case kSw64F64x2Abs: ++ case kSw64SwsaLd: ++ case kSw64SwsaSt: ++ default: ++ break; ++ } ++ return kSuccess; ++} ++ ++#define UNSUPPORTED_COND(opcode, condition) \ ++ StdoutStream{} << "Unsupported " << #opcode << " condition: \"" << condition \ ++ << "\""; \ ++ UNIMPLEMENTED(); ++ ++void AssembleBranchToLabels(CodeGenerator* gen, TurboAssembler* tasm, ++ Instruction* instr, FlagsCondition condition, ++ Label* tlabel, Label* flabel, bool fallthru) { ++#undef __ ++#define __ tasm-> ++ Sw64OperandConverter i(gen, instr); ++ ++ Condition cc = kNoCondition; ++ // SW64 does not have condition code flags, so compare and branch are ++ // implemented differently than on the other arch's. The compare operations ++ // emit sw64 pseudo-instructions, which are handled here by branch ++ // instructions that do the actual comparison. Essential that the input ++ // registers to compare pseudo-op are not modified before this branch op, as ++ // they are tested here. ++ ++ if (instr->arch_opcode() == kSw64Tst) { ++ cc = FlagsConditionToConditionTst(condition); ++ __ Branch(tlabel, cc, kScratchReg, Operand(zero_reg)); ++ } else if (instr->arch_opcode() == kSw64Addl || ++ instr->arch_opcode() == kSw64Subl) { ++ cc = FlagsConditionToConditionOvf(condition); ++ // __ sral(i.OutputRegister(), 32 ,kScratchReg); ++ // __ Sraw(kScratchReg2, i.OutputRegister(), 31); ++ // __ Branch(tlabel, cc, kScratchReg2, Operand(kScratchReg)); ++ __ addw(i.OutputRegister(), 0, kScratchReg2); // optimize from octane ++ __ Branch(tlabel, cc, i.OutputRegister(), Operand(kScratchReg2)); ++ } else if (instr->arch_opcode() == kSw64AddlOvf || ++ instr->arch_opcode() == kSw64SublOvf) { ++ switch (condition) { ++ // Overflow occurs if overflow register is negative ++ case kOverflow: ++ __ Branch(tlabel, lt, kScratchReg, Operand(zero_reg)); ++ break; ++ case kNotOverflow: ++ __ Branch(tlabel, ge, kScratchReg, Operand(zero_reg)); ++ break; ++ default: ++ UNSUPPORTED_COND(instr->arch_opcode(), condition); ++ } ++ } else if (instr->arch_opcode() == kSw64MulwOvf) { ++ // Overflow occurs if overflow register is not zero ++ switch (condition) { ++ case kOverflow: ++ __ Branch(tlabel, ne, kScratchReg, Operand(zero_reg)); ++ break; ++ case kNotOverflow: ++ __ Branch(tlabel, eq, kScratchReg, Operand(zero_reg)); ++ break; ++ default: ++ UNSUPPORTED_COND(kSw64MulwOvf, condition); ++ } ++ } else if (instr->arch_opcode() == kSw64Cmp) { ++ cc = FlagsConditionToConditionCmp(condition); ++ __ Branch(tlabel, cc, i.InputRegister(0), i.InputOperand(1)); ++ } else if (instr->arch_opcode() == kArchStackPointerGreaterThan) { ++ cc = FlagsConditionToConditionCmp(condition); ++ DCHECK((cc == ls) || (cc == hi)); ++ if (cc == ls) { ++ __ xor_(i.TempRegister(0), 1, i.TempRegister(0)); ++ } ++ __ Branch(tlabel, ne, i.TempRegister(0), Operand(zero_reg)); ++ } else if (instr->arch_opcode() == kSw64Fcmps || ++ instr->arch_opcode() == kSw64Fcmpd) { ++ bool predicate; ++ FlagsConditionToConditionCmpFPU(&predicate, condition); ++ if (predicate) { ++ __ BranchTrueF(tlabel); ++ } else { ++ __ BranchFalseF(tlabel); ++ } ++ } else { ++ PrintF("AssembleArchBranch Unimplemented arch_opcode: %d\n", ++ instr->arch_opcode()); ++ UNIMPLEMENTED(); ++ } ++ if (!fallthru) __ Branch(flabel); // no fallthru to flabel. ++#undef __ ++#define __ tasm()-> ++} ++ ++// Assembles branches after an instruction. ++void CodeGenerator::AssembleArchBranch(Instruction* instr, BranchInfo* branch) { ++ Label* tlabel = branch->true_label; ++ Label* flabel = branch->false_label; ++ ++ AssembleBranchToLabels(this, tasm(), instr, branch->condition, tlabel, flabel, ++ branch->fallthru); ++} ++ ++#undef UNSUPPORTED_COND ++ ++void CodeGenerator::AssembleArchDeoptBranch(Instruction* instr, ++ BranchInfo* branch) { ++ AssembleArchBranch(instr, branch); ++} ++ ++void CodeGenerator::AssembleArchJumpRegardlessOfAssemblyOrder( ++ RpoNumber target) { ++ __ Branch(GetLabel(target)); ++} ++ ++#if V8_ENABLE_WEBASSEMBLY ++void CodeGenerator::AssembleArchTrap(Instruction* instr, ++ FlagsCondition condition) { ++ class OutOfLineTrap final : public OutOfLineCode { ++ public: ++ OutOfLineTrap(CodeGenerator* gen, Instruction* instr) ++ : OutOfLineCode(gen), instr_(instr), gen_(gen) {} ++ void Generate() final { ++ Sw64OperandConverter i(gen_, instr_); ++ TrapId trap_id = ++ static_cast(i.InputInt32(instr_->InputCount() - 1)); ++ GenerateCallToTrap(trap_id); ++ } ++ ++ private: ++ void GenerateCallToTrap(TrapId trap_id) { ++ if (trap_id == TrapId::kInvalid) { ++ // We cannot test calls to the runtime in cctest/test-run-wasm. ++ // Therefore we emit a call to C here instead of a call to the runtime. ++ // We use the context register as the scratch register, because we do ++ // not have a context here. ++ __ PrepareCallCFunction(0, 0, cp); ++ __ CallCFunction( ++ ExternalReference::wasm_call_trap_callback_for_testing(), 0); ++ __ LeaveFrame(StackFrame::WASM); ++ auto call_descriptor = gen_->linkage()->GetIncomingDescriptor(); ++ int pop_count = static_cast(call_descriptor->ParameterSlotCount()); ++ pop_count += (pop_count & 1); // align ++ __ Drop(pop_count); ++ __ Ret(); ++ } else { ++ gen_->AssembleSourcePosition(instr_); ++ // A direct call to a wasm runtime stub defined in this module. ++ // Just encode the stub index. This will be patched when the code ++ // is added to the native module and copied into wasm code space. ++ __ Call(static_cast
(trap_id), RelocInfo::WASM_STUB_CALL); ++ ReferenceMap* reference_map = ++ gen_->zone()->New(gen_->zone()); ++ gen_->RecordSafepoint(reference_map); ++ if (FLAG_debug_code) { ++ __ halt(); ++ } ++ } ++ } ++ Instruction* instr_; ++ CodeGenerator* gen_; ++ }; ++ auto ool = zone()->New(this, instr); ++ Label* tlabel = ool->entry(); ++ AssembleBranchToLabels(this, tasm(), instr, condition, tlabel, nullptr, true); ++} ++#endif // V8_ENABLE_WEBASSEMBLY ++ ++// Assembles boolean materializations after an instruction. ++void CodeGenerator::AssembleArchBoolean(Instruction* instr, ++ FlagsCondition condition) { ++ Sw64OperandConverter i(this, instr); ++ ++ // Materialize a full 32-bit 1 or 0 value. The result register is always the ++ // last output of the instruction. ++ DCHECK_NE(0u, instr->OutputCount()); ++ Register result = i.OutputRegister(instr->OutputCount() - 1); ++ Condition cc = kNoCondition; ++ // SW64 does not have condition code flags, so compare and branch are ++ // implemented differently than on the other arch's. The compare operations ++ // emit sw64 pseudo-instructions, which are checked and handled here. ++ ++ if (instr->arch_opcode() == kSw64Tst) { ++ cc = FlagsConditionToConditionTst(condition); ++ if (cc == eq) { ++ __ Cmpult(result, kScratchReg, 1); ++ } else { ++ __ Cmpult(result, zero_reg, kScratchReg); ++ } ++ return; ++ } else if (instr->arch_opcode() == kSw64Addl || ++ instr->arch_opcode() == kSw64Subl) { ++ cc = FlagsConditionToConditionOvf(condition); ++ // Check for overflow creates 1 or 0 for result. ++ __ srll(i.OutputRegister(), 63, kScratchReg); ++ __ Srlw(kScratchReg2, i.OutputRegister(), 31); ++ __ xor_(kScratchReg, kScratchReg2, result); ++ if (cc == eq) // Toggle result for not overflow. ++ __ xor_(result, 1, result); ++ return; ++ } else if (instr->arch_opcode() == kSw64AddlOvf || ++ instr->arch_opcode() == kSw64SublOvf) { ++ // Overflow occurs if overflow register is negative ++ __ cmplt(kScratchReg, zero_reg, result); ++ } else if (instr->arch_opcode() == kSw64MulwOvf) { ++ // Overflow occurs if overflow register is not zero ++ __ Cmpugt(result, kScratchReg, zero_reg); ++ } else if (instr->arch_opcode() == kSw64Cmp) { ++ cc = FlagsConditionToConditionCmp(condition); ++ switch (cc) { ++ case eq: ++ case ne: { ++ Register left = i.InputRegister(0); ++ Operand right = i.InputOperand(1); ++ if (instr->InputAt(1)->IsImmediate()) { ++ if (is_int16(-right.immediate())) { ++ if (right.immediate() == 0) { ++ if (cc == eq) { ++ __ Cmpult(result, left, 1); ++ } else { ++ __ Cmpult(result, zero_reg, left); ++ } ++ } else { ++ __ Addl(result, left, Operand(-right.immediate())); ++ if (cc == eq) { ++ __ Cmpult(result, result, 1); ++ } else { ++ __ Cmpult(result, zero_reg, result); ++ } ++ } ++ } else { ++ if (is_uint16(right.immediate())) { ++ __ Xor(result, left, right); ++ } else { ++ __ li(kScratchReg, right); ++ __ Xor(result, left, kScratchReg); ++ } ++ if (cc == eq) { ++ __ Cmpult(result, result, 1); ++ } else { ++ __ Cmpult(result, zero_reg, result); ++ } ++ } ++ } else { ++ __ Xor(result, left, right); ++ if (cc == eq) { ++ __ Cmpult(result, result, 1); ++ } else { ++ __ Cmpult(result, zero_reg, result); ++ } ++ } ++ } break; ++ case lt: ++ case ge: { ++ Register left = i.InputRegister(0); ++ Operand right = i.InputOperand(1); ++ __ Cmplt(result, left, right); ++ if (cc == ge) { ++ __ xor_(result, 1, result); ++ } ++ } break; ++ case gt: ++ case le: { ++ Register left = i.InputRegister(1); ++ Operand right = i.InputOperand(0); ++ __ Cmplt(result, left, right); ++ if (cc == le) { ++ __ xor_(result, 1, result); ++ } ++ } break; ++ case lo: ++ case hs: { ++ Register left = i.InputRegister(0); ++ Operand right = i.InputOperand(1); ++ __ Cmpult(result, left, right); ++ if (cc == hs) { ++ __ xor_(result, 1, result); ++ } ++ } break; ++ case hi: ++ case ls: { ++ Register left = i.InputRegister(1); ++ Operand right = i.InputOperand(0); ++ __ Cmpult(result, left, right); ++ if (cc == ls) { ++ __ xor_(result, 1, result); ++ } ++ } break; ++ default: ++ UNREACHABLE(); ++ } ++ return; ++ } else if (instr->arch_opcode() == kSw64Fcmpd || ++ instr->arch_opcode() == kSw64Fcmps) { ++ bool predicate; ++ FlagsConditionToConditionCmpFPU(&predicate, condition); ++ __ li(result, Operand(1)); ++ if (predicate) { ++ __ LoadZeroIfNotFPUCondition(result); ++ } else { ++ __ LoadZeroIfFPUCondition(result); ++ } ++ return; ++ } else if (instr->arch_opcode() == kArchStackPointerGreaterThan) { ++ cc = FlagsConditionToConditionCmp(condition); ++ DCHECK((cc == ls) || (cc == hi)); ++ if (cc == ls) { ++ __ xor_(i.OutputRegister(), 1, i.TempRegister(0)); ++ } ++ return; ++ } else { ++ PrintF("AssembleArchBranch Unimplemented arch_opcode is : %d\n", ++ instr->arch_opcode()); ++ TRACE_UNIMPL(); ++ UNIMPLEMENTED(); ++ } ++} ++ ++void CodeGenerator::AssembleArchBinarySearchSwitch(Instruction* instr) { ++ Sw64OperandConverter i(this, instr); ++ Register input = i.InputRegister(0); ++ std::vector> cases; ++ for (size_t index = 2; index < instr->InputCount(); index += 2) { ++ cases.push_back({i.InputInt32(index + 0), GetLabel(i.InputRpo(index + 1))}); ++ } ++ AssembleArchBinarySearchSwitchRange(input, i.InputRpo(1), cases.data(), ++ cases.data() + cases.size()); ++} ++ ++void CodeGenerator::AssembleArchTableSwitch(Instruction* instr) { ++ Sw64OperandConverter i(this, instr); ++ Register input = i.InputRegister(0); ++ size_t const case_count = instr->InputCount() - 2; ++ ++ __ Branch(GetLabel(i.InputRpo(1)), hs, input, Operand(case_count)); ++ __ GenerateSwitchTable(input, case_count, [&i, this](size_t index) { ++ return GetLabel(i.InputRpo(index + 2)); ++ }); ++} ++ ++void CodeGenerator::AssembleArchSelect(Instruction* instr, ++ FlagsCondition condition) { ++ UNIMPLEMENTED(); ++} ++ ++void CodeGenerator::FinishFrame(Frame* frame) { ++ auto call_descriptor = linkage()->GetIncomingDescriptor(); ++ ++ const DoubleRegList saves_fpu = call_descriptor->CalleeSavedFPRegisters(); ++ if (!saves_fpu.is_empty()) { ++ int count = saves_fpu.Count(); ++ DCHECK_EQ(kNumCalleeSavedFPU, count); ++ frame->AllocateSavedCalleeRegisterSlots(count * ++ (kDoubleSize / kSystemPointerSize)); ++ } ++ ++ const RegList saves = call_descriptor->CalleeSavedRegisters(); ++ if (!saves.is_empty()) { ++ int count = saves.Count(); ++ frame->AllocateSavedCalleeRegisterSlots(count); ++ } ++} ++ ++void CodeGenerator::AssembleConstructFrame() { ++ auto call_descriptor = linkage()->GetIncomingDescriptor(); ++ ++ if (frame_access_state()->has_frame()) { ++ if (call_descriptor->IsCFunctionCall()) { ++#if V8_ENABLE_WEBASSEMBLY ++ if (info()->GetOutputStackFrameType() == StackFrame::C_WASM_ENTRY) { ++ __ StubPrologue(StackFrame::C_WASM_ENTRY); ++ // Reserve stack space for saving the c_entry_fp later. ++ __ Subl(sp, sp, Operand(kSystemPointerSize)); ++#else ++ // For balance. ++ if (false) { ++#endif // V8_ENABLE_WEBASSEMBLY ++ } else { ++ __ Push(ra, fp); ++ __ mov(fp, sp); ++ } ++ } else if (call_descriptor->IsJSFunctionCall()) { ++ __ Prologue(); ++ } else { ++ __ StubPrologue(info()->GetOutputStackFrameType()); ++#if V8_ENABLE_WEBASSEMBLY ++ if (call_descriptor->IsWasmFunctionCall() || ++ call_descriptor->IsWasmImportWrapper() || ++ call_descriptor->IsWasmCapiFunction()) { ++ __ Push(kWasmInstanceRegister); ++ } ++ if (call_descriptor->IsWasmCapiFunction()) { ++ // Reserve space for saving the PC later. ++ __ Subl(sp, sp, Operand(kSystemPointerSize)); ++ } ++#endif // V8_ENABLE_WEBASSEMBLY ++ } ++ } ++ ++ int required_slots = ++ frame()->GetTotalFrameSlotCount() - frame()->GetFixedSlotCount(); ++ ++ if (info()->is_osr()) { ++ // TurboFan OSR-compiled functions cannot be entered directly. ++ __ Abort(AbortReason::kShouldNotDirectlyEnterOsrFunction); ++ ++ // Unoptimized code jumps directly to this entrypoint while the unoptimized ++ // frame is still on the stack. Optimized code uses OSR values directly from ++ // the unoptimized frame. Thus, all that needs to be done is to allocate the ++ // remaining stack slots. ++ __ RecordComment("-- OSR entrypoint --"); ++ osr_pc_offset_ = __ pc_offset(); ++ required_slots -= osr_helper()->UnoptimizedFrameSlots(); ++ } ++ ++ const RegList saves = call_descriptor->CalleeSavedRegisters(); ++ const DoubleRegList saves_fpu = call_descriptor->CalleeSavedFPRegisters(); ++ ++ if (required_slots > 0) { ++ DCHECK(frame_access_state()->has_frame()); ++#if V8_ENABLE_WEBASSEMBLY ++ if (info()->IsWasm() && required_slots * kSystemPointerSize > 4 * KB) { ++ // For WebAssembly functions with big frames we have to do the stack ++ // overflow check before we construct the frame. Otherwise we may not ++ // have enough space on the stack to call the runtime for the stack ++ // overflow. ++ Label done; ++ ++ // If the frame is bigger than the stack, we throw the stack overflow ++ // exception unconditionally. Thereby we can avoid the integer overflow ++ // check in the condition code. ++ if (required_slots * kSystemPointerSize < FLAG_stack_size * KB) { ++ __ Ldl( ++ kScratchReg, ++ FieldMemOperand(kWasmInstanceRegister, ++ WasmInstanceObject::kRealStackLimitAddressOffset)); ++ __ Ldl(kScratchReg, MemOperand(kScratchReg)); ++ __ Addl(kScratchReg, kScratchReg, ++ Operand(required_slots * kSystemPointerSize)); ++ __ Branch(&done, uge, sp, Operand(kScratchReg)); ++ } ++ ++ __ Call(wasm::WasmCode::kWasmStackOverflow, RelocInfo::WASM_STUB_CALL); ++ // The call does not return, hence we can ignore any references and just ++ // define an empty safepoint. ++ ReferenceMap* reference_map = zone()->New(zone()); ++ RecordSafepoint(reference_map); ++ if (FLAG_debug_code) { ++ __ halt(); ++ } ++ ++ __ bind(&done); ++ } ++#endif // V8_ENABLE_WEBASSEMBLY ++ } ++ ++ const int returns = frame()->GetReturnSlotCount(); ++ ++ // Skip callee-saved and return slots, which are pushed below. ++ required_slots -= saves.Count(); ++ required_slots -= saves_fpu.Count(); ++ required_slots -= returns; ++ if (required_slots > 0) { ++ __ Subl(sp, sp, Operand(required_slots * kSystemPointerSize)); ++ } ++ ++ if (!saves_fpu.is_empty()) { ++ // Save callee-saved FPU registers. ++ __ MultiPushFPU(saves_fpu); ++ DCHECK_EQ(kNumCalleeSavedFPU, saves_fpu.Count()); ++ } ++ ++ if (!saves.is_empty()) { ++ // Save callee-saved registers. ++ __ MultiPush(saves); ++ } ++ ++ if (returns != 0) { ++ // Create space for returns. ++ __ Subl(sp, sp, Operand(returns * kSystemPointerSize)); ++ } ++} ++ ++void CodeGenerator::AssembleReturn(InstructionOperand* additional_pop_count) { ++ auto call_descriptor = linkage()->GetIncomingDescriptor(); ++ ++ const int returns = frame()->GetReturnSlotCount(); ++ if (returns != 0) { ++ __ Addl(sp, sp, Operand(returns * kSystemPointerSize)); ++ } ++ ++ // Restore GP registers. ++ const RegList saves = call_descriptor->CalleeSavedRegisters(); ++ if (!saves.is_empty()) { ++ __ MultiPop(saves); ++ } ++ ++ // Restore FPU registers. ++ const DoubleRegList saves_fpu = call_descriptor->CalleeSavedFPRegisters(); ++ if (!saves_fpu.is_empty()) { ++ __ MultiPopFPU(saves_fpu); ++ } ++ ++ Sw64OperandConverter g(this, nullptr); ++ ++ const int parameter_slots = ++ static_cast(call_descriptor->ParameterSlotCount()); ++ ++ // {aditional_pop_count} is only greater than zero if {parameter_slots = 0}. ++ // Check RawMachineAssembler::PopAndReturn. ++ if (parameter_slots != 0) { ++ if (additional_pop_count->IsImmediate()) { ++ DCHECK_EQ(g.ToConstant(additional_pop_count).ToInt32(), 0); ++ } else if (FLAG_debug_code) { ++ __ Assert(eq, AbortReason::kUnexpectedAdditionalPopValue, ++ g.ToRegister(additional_pop_count), ++ Operand(static_cast(0))); ++ } ++ } ++ ++ // Functions with JS linkage have at least one parameter (the receiver). ++ // If {parameter_slots} == 0, it means it is a builtin with ++ // kDontAdaptArgumentsSentinel, which takes care of JS arguments popping ++ // itself. ++ const bool drop_jsargs = frame_access_state()->has_frame() && ++ call_descriptor->IsJSFunctionCall() && ++ parameter_slots != 0; ++ ++ if (call_descriptor->IsCFunctionCall()) { ++ AssembleDeconstructFrame(); ++ } else if (frame_access_state()->has_frame()) { ++ // Canonicalize JSFunction return sites for now unless they have an variable ++ // number of stack slot pops. ++ if (additional_pop_count->IsImmediate() && ++ g.ToConstant(additional_pop_count).ToInt32() == 0) { ++ if (return_label_.is_bound()) { ++ __ Branch(&return_label_); ++ return; ++ } else { ++ __ bind(&return_label_); ++ } ++ } ++ if (drop_jsargs) { ++ // Get the actual argument count ++ __ Ldl(t0, MemOperand(fp, StandardFrameConstants::kArgCOffset)); ++ } ++ AssembleDeconstructFrame(); ++ } ++ if (drop_jsargs) { ++ // We must pop all arguments from the stack (including the receiver). This ++ // number of arguments is given by max(1 + argc_reg, parameter_slots). ++ if (parameter_slots > 1) { ++ __ li(kScratchReg, parameter_slots); ++ __ cmplt(t0, kScratchReg, kScratchReg2); ++ __ Selne(t0, kScratchReg, kScratchReg2); ++ } ++ __ Lsal(sp, sp, t0, kSystemPointerSizeLog2); ++ } else if (additional_pop_count->IsImmediate()) { ++ int additional_count = g.ToConstant(additional_pop_count).ToInt32(); ++ __ Drop(parameter_slots + additional_count); ++ } else { ++ Register pop_reg = g.ToRegister(additional_pop_count); ++ __ Drop(parameter_slots); ++ __ Lsal(sp, sp, pop_reg, kSystemPointerSizeLog2); ++ } ++ __ Ret(); ++} ++ ++void CodeGenerator::FinishCode() {} ++ ++void CodeGenerator::PrepareForDeoptimizationExits( ++ ZoneDeque* exits) {} ++ ++void CodeGenerator::AssembleMove(InstructionOperand* source, ++ InstructionOperand* destination) { ++ Sw64OperandConverter g(this, nullptr); ++ // Dispatch on the source and destination operand kinds. Not all ++ // combinations are possible. ++ if (source->IsRegister()) { ++ DCHECK(destination->IsRegister() || destination->IsStackSlot()); ++ Register src = g.ToRegister(source); ++ if (destination->IsRegister()) { ++ __ mov(g.ToRegister(destination), src); ++ } else { ++ __ Stl(src, g.ToMemOperand(destination)); ++ } ++ } else if (source->IsStackSlot()) { ++ DCHECK(destination->IsRegister() || destination->IsStackSlot()); ++ MemOperand src = g.ToMemOperand(source); ++ if (destination->IsRegister()) { ++ __ Ldl(g.ToRegister(destination), src); ++ } else { ++ Register temp = kScratchReg; ++ __ Ldl(temp, src); ++ __ Stl(temp, g.ToMemOperand(destination)); ++ } ++ } else if (source->IsConstant()) { ++ Constant src = g.ToConstant(source); ++ if (destination->IsRegister() || destination->IsStackSlot()) { ++ Register dst = ++ destination->IsRegister() ? g.ToRegister(destination) : kScratchReg; ++ switch (src.type()) { ++ case Constant::kInt32: ++ __ li(dst, Operand(src.ToInt32())); ++ break; ++ case Constant::kFloat32: ++ __ li(dst, Operand::EmbeddedNumber(src.ToFloat32())); ++ break; ++ case Constant::kInt64: ++#if V8_ENABLE_WEBASSEMBLY ++ if (RelocInfo::IsWasmReference(src.rmode())) ++ __ li(dst, Operand(src.ToInt64(), src.rmode())); ++ else ++#endif // V8_ENABLE_WEBASSEMBLY ++ __ li(dst, Operand(src.ToInt64())); ++ break; ++ case Constant::kFloat64: ++ __ li(dst, Operand::EmbeddedNumber(src.ToFloat64().value())); ++ break; ++ case Constant::kExternalReference: ++ __ li(dst, src.ToExternalReference()); ++ break; ++ case Constant::kDelayedStringConstant: ++ __ li(dst, src.ToDelayedStringConstant()); ++ break; ++ case Constant::kHeapObject: { ++ Handle src_object = src.ToHeapObject(); ++ RootIndex index; ++ if (IsMaterializableFromRoot(src_object, &index)) { ++ __ LoadRoot(dst, index); ++ } else { ++ __ li(dst, src_object); ++ } ++ break; ++ } ++ case Constant::kCompressedHeapObject: ++ UNREACHABLE(); ++ case Constant::kRpoNumber: ++ UNREACHABLE(); // TODO(titzer): loading RPO numbers on sw64. ++ } ++ if (destination->IsStackSlot()) __ Stl(dst, g.ToMemOperand(destination)); ++ } else if (src.type() == Constant::kFloat32) { ++ if (destination->IsFPStackSlot()) { ++ MemOperand dst = g.ToMemOperand(destination); ++ if (bit_cast(src.ToFloat32()) == 0) { ++ __ Stl(zero_reg, dst); ++ } else { ++ __ li(kScratchReg, Operand(bit_cast(src.ToFloat32()))); ++ __ Stl(kScratchReg, dst); ++ } ++ } else { ++ DCHECK(destination->IsFPRegister()); ++ FloatRegister dst = g.ToSingleRegister(destination); ++ __ Move(dst, src.ToFloat32()); ++ } ++ } else { ++ DCHECK_EQ(Constant::kFloat64, src.type()); ++ DoubleRegister dst = destination->IsFPRegister() ++ ? g.ToDoubleRegister(destination) ++ : kScratchDoubleReg; ++ __ Move(dst, src.ToFloat64().value()); ++ if (destination->IsFPStackSlot()) { ++ __ Fstd(dst, g.ToMemOperand(destination)); ++ } ++ } ++ } else if (source->IsFPRegister()) { ++ MachineRepresentation rep = LocationOperand::cast(source)->representation(); ++ if (rep == MachineRepresentation::kSimd128) { ++ UNREACHABLE(); ++ } else { ++ FPURegister src = g.ToDoubleRegister(source); ++ if (destination->IsFPRegister()) { ++ FPURegister dst = g.ToDoubleRegister(destination); ++ __ Move(dst, src); ++ } else { ++ DCHECK(destination->IsFPStackSlot()); ++ destination->IsDoubleStackSlot() ++ ? __ Fstd(src, g.ToMemOperand(destination)) ++ : __ Fsts(src, g.ToMemOperand(destination)); ++ } ++ } ++ } else if (source->IsFPStackSlot()) { ++ DCHECK(destination->IsFPRegister() || destination->IsFPStackSlot()); ++ MemOperand src = g.ToMemOperand(source); ++ MachineRepresentation rep = LocationOperand::cast(source)->representation(); ++ if (rep == MachineRepresentation::kSimd128) { ++ UNREACHABLE(); ++ } else { ++ if (destination->IsFPRegister()) { ++ source->IsDoubleStackSlot() ++ ? __ Fldd(g.ToDoubleRegister(destination), src) ++ : __ Flds(g.ToFloatRegister(destination), src); ++ } else { ++ DCHECK(destination->IsFPStackSlot()); ++ FPURegister temp = kScratchDoubleReg; ++ source->IsDoubleStackSlot() ? __ Fldd(temp, src) : __ Flds(temp, src); ++ destination->IsDoubleStackSlot() ++ ? __ Fstd(temp, g.ToMemOperand(destination)) ++ : __ Fsts(temp, g.ToMemOperand(destination)); ++ } ++ } ++ } else { ++ UNREACHABLE(); ++ } ++} ++ ++void CodeGenerator::AssembleSwap(InstructionOperand* source, ++ InstructionOperand* destination) { ++ Sw64OperandConverter g(this, nullptr); ++ // Dispatch on the source and destination operand kinds. Not all ++ // combinations are possible. ++ if (source->IsRegister()) { ++ // Register-register. ++ Register temp = kScratchReg; ++ Register src = g.ToRegister(source); ++ if (destination->IsRegister()) { ++ Register dst = g.ToRegister(destination); ++ __ Move(temp, src); ++ __ Move(src, dst); ++ __ Move(dst, temp); ++ } else { ++ DCHECK(destination->IsStackSlot()); ++ MemOperand dst = g.ToMemOperand(destination); ++ __ mov(temp, src); ++ __ Ldl(src, dst); ++ __ Stl(temp, dst); ++ } ++ } else if (source->IsStackSlot()) { ++ DCHECK(destination->IsStackSlot()); ++ Register temp_0 = kScratchReg; ++ Register temp_1 = kScratchReg2; ++ MemOperand src = g.ToMemOperand(source); ++ MemOperand dst = g.ToMemOperand(destination); ++ __ Ldl(temp_0, src); ++ __ Ldl(temp_1, dst); ++ __ Stl(temp_0, dst); ++ __ Stl(temp_1, src); ++ } else if (source->IsFPRegister()) { ++ MachineRepresentation rep = LocationOperand::cast(source)->representation(); ++ if (rep == MachineRepresentation::kSimd128) { ++ UNREACHABLE(); ++ } else { ++ FPURegister temp = kScratchDoubleReg; ++ FPURegister src = g.ToDoubleRegister(source); ++ if (destination->IsFPRegister()) { ++ FPURegister dst = g.ToDoubleRegister(destination); ++ __ Move(temp, src); ++ __ Move(src, dst); ++ __ Move(dst, temp); ++ } else { ++ DCHECK(destination->IsFPStackSlot()); ++ MemOperand dst = g.ToMemOperand(destination); ++ __ Move(temp, src); ++ destination->IsDoubleStackSlot() ? __ Fldd(src, dst) ++ : __ Flds(src, dst); ++ source->IsDoubleRegister() ? __ Fstd(temp, dst) : __ Fsts(temp, dst); ++ } ++ } ++ } else if (source->IsFPStackSlot()) { ++ DCHECK(destination->IsFPStackSlot()); ++ Register temp_0 = kScratchReg; ++ MemOperand src0 = g.ToMemOperand(source); ++ MemOperand src1(src0.rm(), src0.offset() + kIntSize); ++ MemOperand dst0 = g.ToMemOperand(destination); ++ MemOperand dst1(dst0.rm(), dst0.offset() + kIntSize); ++ MachineRepresentation rep = LocationOperand::cast(source)->representation(); ++ if (rep == MachineRepresentation::kSimd128) { ++ UNREACHABLE(); ++ } else { ++ FPURegister temp_1 = kScratchDoubleReg; ++ __ Fldd(temp_1, dst0); // Save destination in temp_1. ++ __ Ldl(temp_0, src0); // Then use temp_0 to copy source to destination. ++ __ Fstd(temp_1, src0); ++ __ Stl(temp_0, dst0); ++ } ++ } else { ++ // No other combinations are possible. ++ UNREACHABLE(); ++ } ++} ++ ++void CodeGenerator::AssembleJumpTable(Label** targets, size_t target_count) { ++ // On 64-bit SW64 we emit the jump tables inline. ++ UNREACHABLE(); ++} ++ ++#undef ASSEMBLE_ATOMIC_LOAD_INTEGER ++#undef ASSEMBLE_ATOMIC_STORE_INTEGER ++#undef ASSEMBLE_ATOMIC_BINOP ++#undef ASSEMBLE_ATOMIC_BINOP_EXT ++#undef ASSEMBLE_ATOMIC_EXCHANGE_INTEGER ++#undef ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT ++#undef ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER ++#undef ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT ++#undef ASSEMBLE_IEEE754_BINOP ++#undef ASSEMBLE_IEEE754_UNOP ++#undef ASSEMBLE_F64X2_ARITHMETIC_BINOP ++ ++#undef TRACE_MSG ++#undef TRACE_UNIMPL ++#undef __ ++ ++} // namespace compiler ++} // namespace internal ++} // namespace v8 +diff --git a/deps/v8/src/compiler/backend/sw64/instruction-codes-sw64.h b/deps/v8/src/compiler/backend/sw64/instruction-codes-sw64.h +new file mode 100644 +index 00000000..11ee042c +--- /dev/null ++++ b/deps/v8/src/compiler/backend/sw64/instruction-codes-sw64.h +@@ -0,0 +1,420 @@ ++// Copyright 2014 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef V8_COMPILER_BACKEND_SW64_INSTRUCTION_CODES_SW64_H_ ++#define V8_COMPILER_BACKEND_SW64_INSTRUCTION_CODES_SW64_H_ ++ ++namespace v8 { ++namespace internal { ++namespace compiler { ++ ++// SW64-specific opcodes that specify which assembly sequence to emit. ++// Most opcodes specify a single instruction. ++ ++#define TARGET_ARCH_OPCODE_LIST(V) \ ++ V(Sw64Addw) \ ++ V(Sw64Addl) \ ++ V(Sw64AddlOvf) \ ++ V(Sw64Subw) \ ++ V(Sw64Subl) \ ++ V(Sw64SublOvf) \ ++ V(Sw64Mulw) \ ++ V(Sw64MulwOvf) \ ++ V(Sw64MullOvf) \ ++ V(Sw64MulwHigh) \ ++ V(Sw64MullHigh) \ ++ V(Sw64MulwHighU) \ ++ V(Sw64MullHighU) \ ++ V(Sw64Mull) \ ++ V(Sw64Divw) \ ++ V(Sw64Divl) \ ++ V(Sw64Divwu) \ ++ V(Sw64Divlu) \ ++ V(Sw64Modw) \ ++ V(Sw64Modl) \ ++ V(Sw64Modwu) \ ++ V(Sw64Modlu) \ ++ V(Sw64Andl) \ ++ V(Sw64Andw) \ ++ V(Sw64Orl) \ ++ V(Sw64Orw) \ ++ V(Sw64Norl) \ ++ V(Sw64Norw) \ ++ V(Sw64Xorl) \ ++ V(Sw64Xorw) \ ++ V(Sw64Ctlzw) \ ++ V(Sw64Lsaw) \ ++ V(Sw64Lsal) \ ++ V(Sw64Sllw) \ ++ V(Sw64Srlw) \ ++ V(Sw64Sraw) \ ++ V(Sw64Extw) \ ++ V(Sw64InsZerow) \ ++ V(Sw64Extl) \ ++ V(Sw64InsZerod) \ ++ V(Sw64Ctlzl) \ ++ V(Sw64Cttzw) \ ++ V(Sw64Cttzl) \ ++ V(Sw64Ctpopw) \ ++ V(Sw64Ctpopl) \ ++ V(Sw64Slll) \ ++ V(Sw64Srll) \ ++ V(Sw64Sral) \ ++ V(Sw64Rolw) \ ++ V(Sw64Roll) \ ++ V(Sw64Mov) \ ++ V(Sw64Tst) \ ++ V(Sw64Cmp) \ ++ V(Sw64Fcmps) \ ++ V(Sw64Fadds) \ ++ V(Sw64Fsubs) \ ++ V(Sw64Fmuls) \ ++ V(Sw64Fdivs) \ ++ V(Sw64Frecs) \ ++ V(Sw64Frecd) \ ++ V(Sw64Fmods) \ ++ V(Sw64Fabss) \ ++ V(Sw64Fnegs) \ ++ V(Sw64Fsqrts) \ ++ V(Sw64Fcmpd) \ ++ V(Sw64Faddd) \ ++ V(Sw64Fsubd) \ ++ V(Sw64Fmuld) \ ++ V(Sw64Fdivd) \ ++ V(Sw64Fmodd) \ ++ V(Sw64Fabsd) \ ++ V(Sw64Fnegd) \ ++ V(Sw64Fsqrtd) \ ++ V(Sw64Float64RoundDown) \ ++ V(Sw64Float64RoundTruncate) \ ++ V(Sw64Float64RoundUp) \ ++ V(Sw64Float64RoundTiesEven) \ ++ V(Sw64Float32RoundDown) \ ++ V(Sw64Float32RoundTruncate) \ ++ V(Sw64Float32RoundUp) \ ++ V(Sw64Float32RoundTiesEven) \ ++ V(Sw64F64ToF32) \ ++ V(Sw64F32ToF64) \ ++ V(Sw64F64ToI32) \ ++ V(Sw64F64ToI32_g) \ ++ V(Sw64F64ToI32_n) \ ++ V(Sw64F64ToI32_p) \ ++ V(Sw64F32ToI32) \ ++ V(Sw64F32ToI32_g) \ ++ V(Sw64F32ToI32_n) \ ++ V(Sw64F32ToI32_p) \ ++ V(Sw64F64ToUI32) \ ++ V(Sw64F32ToUI32) \ ++ V(Sw64F32ToI64) \ ++ V(Sw64F64ToI64) \ ++ V(Sw64F32ToUI64) \ ++ V(Sw64F64ToUI64) \ ++ V(Sw64I32ToF32) \ ++ V(Sw64UI32ToF32) \ ++ V(Sw64I64ToF32) \ ++ V(Sw64UI64ToF32) \ ++ V(Sw64I32ToF64) \ ++ V(Sw64UI32ToF64) \ ++ V(Sw64I64ToF64) \ ++ V(Sw64UI64ToF64) \ ++ V(Sw64Ldb) \ ++ V(Sw64Ldbu) \ ++ V(Sw64Stb) \ ++ V(Sw64Ldh) \ ++ V(Sw64Ldh_u) \ ++ V(Sw64Ldhu) \ ++ V(Sw64Ldhu_u) \ ++ V(Sw64Sth) \ ++ V(Sw64Sth_u) \ ++ V(Sw64Ldl) \ ++ V(Sw64Ldl_u) \ ++ V(Sw64Ldw) \ ++ V(Sw64Ldw_u) \ ++ V(Sw64Ldwu) \ ++ V(Sw64Ldwu_u) \ ++ V(Sw64Stw) \ ++ V(Sw64Stw_u) \ ++ V(Sw64Stl) \ ++ V(Sw64Stl_u) \ ++ V(Sw64Flds) \ ++ V(Sw64Flds_u) \ ++ V(Sw64Fsts) \ ++ V(Sw64Fsts_u) \ ++ V(Sw64Fldd) \ ++ V(Sw64Fldd_u) \ ++ V(Sw64Fstd) \ ++ V(Sw64Fstd_u) \ ++ V(Sw64BitcastDL) \ ++ V(Sw64BitcastLD) \ ++ V(Sw64BitcastSW) \ ++ V(Sw64BitcastWS) \ ++ V(Sw64Float64ExtractLowWord32) \ ++ V(Sw64Float64ExtractHighWord32) \ ++ V(Sw64Float64InsertLowWord32) \ ++ V(Sw64Float64InsertHighWord32) \ ++ V(Sw64Float32Max) \ ++ V(Sw64Float64Max) \ ++ V(Sw64Float32Min) \ ++ V(Sw64Float64Min) \ ++ V(Sw64Float64SilenceNaN) \ ++ V(Sw64Push) \ ++ V(Sw64Peek) \ ++ V(Sw64Poke) \ ++ V(Sw64ByteSwap64) \ ++ V(Sw64ByteSwap32) \ ++ V(Sw64StackClaim) \ ++ V(Sw64Sextb) \ ++ V(Sw64Sexth) \ ++ V(Sw64Memb) \ ++ V(Sw64AssertEqual) \ ++ V(Sw64S128Const) \ ++ V(Sw64S128Zero) \ ++ V(Sw64S128AllOnes) \ ++ V(Sw64I32x4Splat) \ ++ V(Sw64I32x4ExtractLane) \ ++ V(Sw64I32x4ReplaceLane) \ ++ V(Sw64I32x4Add) \ ++ V(Sw64I32x4Sub) \ ++ V(Sw64F64x2Abs) \ ++ V(Sw64F64x2Neg) \ ++ V(Sw64F32x4Splat) \ ++ V(Sw64F32x4ExtractLane) \ ++ V(Sw64F32x4ReplaceLane) \ ++ V(Sw64F32x4SConvertI32x4) \ ++ V(Sw64F32x4UConvertI32x4) \ ++ V(Sw64I32x4Mul) \ ++ V(Sw64I32x4MaxS) \ ++ V(Sw64I32x4MinS) \ ++ V(Sw64I32x4Eq) \ ++ V(Sw64I32x4Ne) \ ++ V(Sw64I32x4Shl) \ ++ V(Sw64I32x4ShrS) \ ++ V(Sw64I32x4ShrU) \ ++ V(Sw64I32x4MaxU) \ ++ V(Sw64I32x4MinU) \ ++ V(Sw64F64x2Sqrt) \ ++ V(Sw64F64x2Add) \ ++ V(Sw64F64x2Sub) \ ++ V(Sw64F64x2Mul) \ ++ V(Sw64F64x2Div) \ ++ V(Sw64F64x2Min) \ ++ V(Sw64F64x2Max) \ ++ V(Sw64F64x2Eq) \ ++ V(Sw64F64x2Ne) \ ++ V(Sw64F64x2Lt) \ ++ V(Sw64F64x2Le) \ ++ V(Sw64F64x2Splat) \ ++ V(Sw64F64x2ExtractLane) \ ++ V(Sw64F64x2ReplaceLane) \ ++ V(Sw64F64x2Pmin) \ ++ V(Sw64F64x2Pmax) \ ++ V(Sw64F64x2Ceil) \ ++ V(Sw64F64x2Floor) \ ++ V(Sw64F64x2Trunc) \ ++ V(Sw64F64x2NearestInt) \ ++ V(Sw64F64x2ConvertLowI32x4S) \ ++ V(Sw64F64x2ConvertLowI32x4U) \ ++ V(Sw64F64x2PromoteLowF32x4) \ ++ V(Sw64I64x2Splat) \ ++ V(Sw64I64x2ExtractLane) \ ++ V(Sw64I64x2ReplaceLane) \ ++ V(Sw64F32x4RecipApprox) \ ++ V(Sw64F32x4RecipSqrtApprox) \ ++ V(Sw64I64x2Add) \ ++ V(Sw64I64x2Sub) \ ++ V(Sw64I64x2Mul) \ ++ V(Sw64I64x2Neg) \ ++ V(Sw64I64x2Shl) \ ++ V(Sw64I64x2ShrS) \ ++ V(Sw64I64x2ShrU) \ ++ V(Sw64I64x2BitMask) \ ++ V(Sw64I64x2Eq) \ ++ V(Sw64I64x2Ne) \ ++ V(Sw64I64x2GtS) \ ++ V(Sw64I64x2GeS) \ ++ V(Sw64I64x2Abs) \ ++ V(Sw64I64x2SConvertI32x4Low) \ ++ V(Sw64I64x2SConvertI32x4High) \ ++ V(Sw64I64x2UConvertI32x4Low) \ ++ V(Sw64I64x2UConvertI32x4High) \ ++ V(Sw64ExtMulLow) \ ++ V(Sw64ExtMulHigh) \ ++ V(Sw64ExtAddPairwise) \ ++ V(Sw64F32x4Abs) \ ++ V(Sw64F32x4Neg) \ ++ V(Sw64F32x4Sqrt) \ ++ V(Sw64F32x4Add) \ ++ V(Sw64F32x4Sub) \ ++ V(Sw64F32x4Mul) \ ++ V(Sw64F32x4Div) \ ++ V(Sw64F32x4Max) \ ++ V(Sw64F32x4Min) \ ++ V(Sw64F32x4Eq) \ ++ V(Sw64F32x4Ne) \ ++ V(Sw64F32x4Lt) \ ++ V(Sw64F32x4Le) \ ++ V(Sw64F32x4Pmin) \ ++ V(Sw64F32x4Pmax) \ ++ V(Sw64F32x4Ceil) \ ++ V(Sw64F32x4Floor) \ ++ V(Sw64F32x4Trunc) \ ++ V(Sw64F32x4NearestInt) \ ++ V(Sw64F32x4DemoteF64x2Zero) \ ++ V(Sw64I32x4SConvertF32x4) \ ++ V(Sw64I32x4UConvertF32x4) \ ++ V(Sw64I32x4Neg) \ ++ V(Sw64I32x4GtS) \ ++ V(Sw64I32x4GeS) \ ++ V(Sw64I32x4GtU) \ ++ V(Sw64I32x4GeU) \ ++ V(Sw64I32x4Abs) \ ++ V(Sw64I32x4BitMask) \ ++ V(Sw64I32x4DotI16x8S) \ ++ V(Sw64I32x4TruncSatF64x2SZero) \ ++ V(Sw64I32x4TruncSatF64x2UZero) \ ++ V(Sw64I16x8Splat) \ ++ V(Sw64I16x8ExtractLaneU) \ ++ V(Sw64I16x8ExtractLaneS) \ ++ V(Sw64I16x8ReplaceLane) \ ++ V(Sw64I16x8Neg) \ ++ V(Sw64I16x8Shl) \ ++ V(Sw64I16x8ShrS) \ ++ V(Sw64I16x8ShrU) \ ++ V(Sw64I16x8Add) \ ++ V(Sw64I16x8AddSatS) \ ++ V(Sw64I16x8Sub) \ ++ V(Sw64I16x8SubSatS) \ ++ V(Sw64I16x8Mul) \ ++ V(Sw64I16x8MaxS) \ ++ V(Sw64I16x8MinS) \ ++ V(Sw64I16x8Eq) \ ++ V(Sw64I16x8Ne) \ ++ V(Sw64I16x8GtS) \ ++ V(Sw64I16x8GeS) \ ++ V(Sw64I16x8AddSatU) \ ++ V(Sw64I16x8SubSatU) \ ++ V(Sw64I16x8MaxU) \ ++ V(Sw64I16x8MinU) \ ++ V(Sw64I16x8GtU) \ ++ V(Sw64I16x8GeU) \ ++ V(Sw64I16x8RoundingAverageU) \ ++ V(Sw64I16x8Abs) \ ++ V(Sw64I16x8BitMask) \ ++ V(Sw64I16x8Q15MulRSatS) \ ++ V(Sw64I8x16Splat) \ ++ V(Sw64I8x16ExtractLaneU) \ ++ V(Sw64I8x16ExtractLaneS) \ ++ V(Sw64I8x16ReplaceLane) \ ++ V(Sw64I8x16Neg) \ ++ V(Sw64I8x16Shl) \ ++ V(Sw64I8x16ShrS) \ ++ V(Sw64I8x16Add) \ ++ V(Sw64I8x16AddSatS) \ ++ V(Sw64I8x16Sub) \ ++ V(Sw64I8x16SubSatS) \ ++ V(Sw64I8x16MaxS) \ ++ V(Sw64I8x16MinS) \ ++ V(Sw64I8x16Eq) \ ++ V(Sw64I8x16Ne) \ ++ V(Sw64I8x16GtS) \ ++ V(Sw64I8x16GeS) \ ++ V(Sw64I8x16ShrU) \ ++ V(Sw64I8x16AddSatU) \ ++ V(Sw64I8x16SubSatU) \ ++ V(Sw64I8x16MaxU) \ ++ V(Sw64I8x16MinU) \ ++ V(Sw64I8x16GtU) \ ++ V(Sw64I8x16GeU) \ ++ V(Sw64I8x16RoundingAverageU) \ ++ V(Sw64I8x16Abs) \ ++ V(Sw64I8x16Popcnt) \ ++ V(Sw64I8x16BitMask) \ ++ V(Sw64S128And) \ ++ V(Sw64S128Or) \ ++ V(Sw64S128Xor) \ ++ V(Sw64S128Not) \ ++ V(Sw64S128Select) \ ++ V(Sw64S128AndNot) \ ++ V(Sw64I64x2AllTrue) \ ++ V(Sw64I32x4AllTrue) \ ++ V(Sw64I16x8AllTrue) \ ++ V(Sw64I8x16AllTrue) \ ++ V(Sw64V128AnyTrue) \ ++ V(Sw64S32x4InterleaveRight) \ ++ V(Sw64S32x4InterleaveLeft) \ ++ V(Sw64S32x4PackEven) \ ++ V(Sw64S32x4PackOdd) \ ++ V(Sw64S32x4InterleaveEven) \ ++ V(Sw64S32x4InterleaveOdd) \ ++ V(Sw64S32x4Shuffle) \ ++ V(Sw64S16x8InterleaveRight) \ ++ V(Sw64S16x8InterleaveLeft) \ ++ V(Sw64S16x8PackEven) \ ++ V(Sw64S16x8PackOdd) \ ++ V(Sw64S16x8InterleaveEven) \ ++ V(Sw64S16x8InterleaveOdd) \ ++ V(Sw64S16x4Reverse) \ ++ V(Sw64S16x2Reverse) \ ++ V(Sw64S8x16InterleaveRight) \ ++ V(Sw64S8x16InterleaveLeft) \ ++ V(Sw64S8x16PackEven) \ ++ V(Sw64S8x16PackOdd) \ ++ V(Sw64S8x16InterleaveEven) \ ++ V(Sw64S8x16InterleaveOdd) \ ++ V(Sw64I8x16Shuffle) \ ++ V(Sw64I8x16Swizzle) \ ++ V(Sw64S8x16Concat) \ ++ V(Sw64S8x8Reverse) \ ++ V(Sw64S8x4Reverse) \ ++ V(Sw64S8x2Reverse) \ ++ V(Sw64S128LoadSplat) \ ++ V(Sw64SwsaLd) \ ++ V(Sw64SwsaSt) \ ++ V(Sw64I32x4SConvertI16x8Low) \ ++ V(Sw64I32x4SConvertI16x8High) \ ++ V(Sw64I32x4UConvertI16x8Low) \ ++ V(Sw64I32x4UConvertI16x8High) \ ++ V(Sw64I16x8SConvertI8x16Low) \ ++ V(Sw64I16x8SConvertI8x16High) \ ++ V(Sw64I16x8SConvertI32x4) \ ++ V(Sw64I16x8UConvertI32x4) \ ++ V(Sw64I16x8UConvertI8x16Low) \ ++ V(Sw64I16x8UConvertI8x16High) \ ++ V(Sw64I8x16SConvertI16x8) \ ++ V(Sw64I8x16UConvertI16x8) \ ++ V(Sw64StoreCompressTagged) \ ++ V(Sw64Word64AtomicLoadUint64) \ ++ V(Sw64Word64AtomicStoreWord64) \ ++ V(Sw64Word64AtomicAddUint64) \ ++ V(Sw64Word64AtomicSubUint64) \ ++ V(Sw64Word64AtomicAndUint64) \ ++ V(Sw64Word64AtomicOrUint64) \ ++ V(Sw64Word64AtomicXorUint64) \ ++ V(Sw64Word64AtomicExchangeUint64) \ ++ V(Sw64Word64AtomicCompareExchangeUint64) ++ ++// Addressing modes represent the "shape" of inputs to an instruction. ++// Many instructions support multiple addressing modes. Addressing modes ++// are encoded into the InstructionCode of the instruction and tell the ++// code generator after register allocation which assembler method to call. ++// ++// We use the following local notation for addressing modes: ++// ++// R = register ++// O = register or stack slot ++// D = double register ++// I = immediate (handle, external, int32) ++// MRI = [register + immediate] ++// MRR = [register + register] ++// TODO(plind): Add the new r6 address modes. ++#define TARGET_ADDRESSING_MODE_LIST(V) \ ++ V(MRI) /* [%r0 + K] */ \ ++ V(MRR) /* [%r0 + %r1] */ \ ++ V(Root) /* [%rr + K] */ ++ ++} // namespace compiler ++} // namespace internal ++} // namespace v8 ++ ++#endif // V8_COMPILER_SW64_INSTRUCTION_CODES_SW64_H_ +diff --git a/deps/v8/src/compiler/backend/sw64/instruction-scheduler-sw64.cc b/deps/v8/src/compiler/backend/sw64/instruction-scheduler-sw64.cc +new file mode 100644 +index 00000000..e9892127 +--- /dev/null ++++ b/deps/v8/src/compiler/backend/sw64/instruction-scheduler-sw64.cc +@@ -0,0 +1,1269 @@ ++// Copyright 2015 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "src/codegen/macro-assembler.h" ++#include "src/compiler/backend/instruction-scheduler.h" ++ ++namespace v8 { ++namespace internal { ++namespace compiler { ++ ++bool InstructionScheduler::SchedulerSupported() { return true; } ++ ++int InstructionScheduler::GetTargetInstructionFlags( ++ const Instruction* instr) const { ++ switch (instr->arch_opcode()) { ++ case kSw64Addw: ++ case kSw64Addl: ++ case kSw64AddlOvf: ++ case kSw64Subw: ++ case kSw64Subl: ++ case kSw64SublOvf: ++ case kSw64Mulw: ++ case kSw64MulwOvf: ++ case kSw64MulwHigh: ++ case kSw64MullHigh: ++ case kSw64MulwHighU: ++ case kSw64Mull: ++ case kSw64Divw: ++ case kSw64Divl: ++ case kSw64Divwu: ++ case kSw64Divlu: ++ case kSw64Modw: ++ case kSw64Modl: ++ case kSw64Modwu: ++ case kSw64Modlu: ++ case kSw64Andl: ++ case kSw64Andw: ++ case kSw64Orl: ++ case kSw64Orw: ++ case kSw64Norl: ++ case kSw64Norw: ++ case kSw64Xorl: ++ case kSw64Xorw: ++ case kSw64Ctlzw: ++ case kSw64Lsaw: ++ case kSw64Lsal: ++ case kSw64Sllw: ++ case kSw64Srlw: ++ case kSw64Sraw: ++ case kSw64Extw: ++ case kSw64InsZerow: ++ case kSw64Extl: ++ case kSw64InsZerod: ++ case kSw64Ctlzl: ++ case kSw64Cttzw: ++ case kSw64Cttzl: ++ ++ case kSw64Ctpopw: ++ case kSw64Ctpopl: ++ case kSw64Slll: ++ case kSw64Srll: ++ case kSw64Sral: ++ ++ case kSw64Rolw: ++ case kSw64Roll: ++ case kSw64Mov: ++ case kSw64Tst: ++ case kSw64Cmp: ++ case kSw64Fcmps: ++ case kSw64Fadds: ++ case kSw64Fsubs: ++ case kSw64Fmuls: ++ case kSw64Fdivs: ++ case kSw64Frecs: ++ case kSw64Frecd: ++ case kSw64Fabss: ++ case kSw64Fnegs: ++ case kSw64Fsqrts: ++ case kSw64Fcmpd: ++ case kSw64Faddd: ++ case kSw64Fsubd: ++ case kSw64Fmuld: ++ case kSw64Fdivd: ++ case kSw64Fabsd: ++ case kSw64Fnegd: ++ case kSw64Fsqrtd: ++ ++ case kSw64Float64RoundDown: ++ case kSw64Float64RoundTruncate: ++ case kSw64Float64RoundUp: ++ case kSw64Float64RoundTiesEven: ++ case kSw64Float32RoundDown: ++ case kSw64Float32RoundTruncate: ++ case kSw64Float32RoundUp: ++ case kSw64Float32RoundTiesEven: ++ ++ case kSw64F64ToF32: ++ case kSw64F32ToF64: ++ case kSw64F64ToI32: ++ case kSw64F64ToI32_g: ++ case kSw64F64ToI32_n: ++ case kSw64F64ToI32_p: ++ case kSw64F32ToI32: ++ case kSw64F32ToI32_g: ++ case kSw64F32ToI32_n: ++ case kSw64F32ToI32_p: ++ ++ case kSw64F32ToI64: ++ case kSw64F64ToI64: ++ case kSw64F64ToUI32: ++ case kSw64F32ToUI32: ++ case kSw64F32ToUI64: ++ case kSw64F64ToUI64: ++ ++ case kSw64I32ToF64: ++ case kSw64I64ToF32: ++ case kSw64I32ToF32: ++ case kSw64UI32ToF32: ++ case kSw64UI64ToF32: ++ case kSw64I64ToF64: ++ case kSw64UI32ToF64: ++ case kSw64UI64ToF64: ++ ++ case kSw64BitcastDL: ++ case kSw64BitcastLD: ++ case kSw64BitcastSW: ++ case kSw64BitcastWS: ++ case kSw64Float64ExtractLowWord32: ++ case kSw64Float64ExtractHighWord32: ++ case kSw64Float64InsertLowWord32: ++ case kSw64Float64InsertHighWord32: ++ case kSw64Float32Max: ++ case kSw64Float64Max: ++ case kSw64Float32Min: ++ case kSw64Float64Min: ++ case kSw64Float64SilenceNaN: ++ ++ case kSw64ByteSwap64: ++ case kSw64ByteSwap32: ++ ++ case kSw64Sextb: ++ case kSw64Sexth: ++ case kSw64AssertEqual: ++ return kNoOpcodeFlags; ++ case kSw64Ldb: ++ case kSw64Ldbu: ++ case kSw64Ldh: ++ case kSw64Ldh_u: ++ case kSw64Ldhu: ++ case kSw64Ldhu_u: ++ case kSw64Ldl: ++ case kSw64Ldl_u: ++ case kSw64Ldw: ++ case kSw64Ldw_u: ++ case kSw64Ldwu: ++ case kSw64Ldwu_u: ++ case kSw64Flds: ++ case kSw64Flds_u: ++ case kSw64Fldd: ++ case kSw64Fldd_u: ++ case kSw64Peek: ++ case kSw64SwsaLd: ++ case kSw64Word64AtomicLoadUint64: ++ return kIsLoadOperation; ++ case kSw64Fmods: ++ case kSw64Fmodd: ++ case kSw64Stb: ++ case kSw64Sth: ++ case kSw64Sth_u: ++ case kSw64Stw: ++ case kSw64Stw_u: ++ case kSw64Stl: ++ case kSw64Stl_u: ++ case kSw64Fsts: ++ case kSw64Fsts_u: ++ case kSw64Fstd: ++ case kSw64Fstd_u: ++ case kSw64Push: ++ case kSw64Word64AtomicStoreWord64: ++ case kSw64Word64AtomicAddUint64: ++ case kSw64Word64AtomicSubUint64: ++ case kSw64Word64AtomicAndUint64: ++ case kSw64Word64AtomicOrUint64: ++ case kSw64Word64AtomicXorUint64: ++ case kSw64Word64AtomicExchangeUint64: ++ case kSw64Word64AtomicCompareExchangeUint64: ++ case kSw64Poke: ++ case kSw64StackClaim: ++ return kHasSideEffect; ++ default: ++ return kHasSideEffect; ++ } ++} ++ ++enum Latency { ++ FSELEQ = 2, ++ FSELNE = 2, ++ FSELLT = 2, ++ FSELLE = 2, ++ FSELGT = 2, ++ FSELGE = 2, ++ ++ FCPYS = 2, ++ FCPYSE = 2, ++ FCPYSN = 2, ++ ++ SLLOW = 3, // 256 sll ++ SRLOW = 3, // 256 srl ++ SRAOW = 3, // core4 ++ ++ BRANCH = 4, // sw ?? core4? ++ RINT_S = 4, // Estimated. core4? ++ RINT_D = 4, // Estimated. core4? ++ ++ LDBU = 4, ++ LDHU = 4, ++ LDW = 4, ++ LDL = 4, ++ LDL_U = 4, ++ STB = 4, ++ STH = 4, ++ STW = 4, ++ STL = 4, ++ STL_U = 4, ++ ++ IFMOVS = 4, ++ IFMOVD = 4, ++ FIMOVS = 4, ++ FIMOVD = 4, ++ ++ LDWE = 4, ++ LDSE = 4, ++ LDDE = 4, ++ VLDS = 4, ++ FLDS = 4, ++ FLDD = 4, ++ PRI_LD = 4, ++ PRI_ST = 4, ++ FSTS = 4, ++ FSTD = 4, ++ ++ MULW = 3, ++ MULL = 3, ++ UMULH = 3, ++ ++ FCVTSD = 4, ++ FCVTDS = 4, ++ FCVTDL = 4, ++ FCVTWL = 4, ++ FCVTLW = 4, ++ FCVTLS = 4, ++ FCVTLD = 4, ++ FCVTDL_Z = 4, ++ FCVTDL_P = 4, ++ FCVTDL_G = 4, ++ FCVTDL_N = 4, ++ ++ FMAS = 6, ++ FMAD = 6, ++ FMSS = 6, ++ FMSD = 6, ++ FNMAS = 6, ++ FNMAD = 6, ++ FNMSS = 6, ++ FNMSD = 6, ++ ++ FADDS = 6, ++ FADDD = 6, ++ FSUBS = 6, ++ FSUBD = 6, ++ FMULS = 6, ++ FMULD = 6, ++ ++ FCMPDEQ = 6, ++ FCMPDLE = 6, ++ FCMPDLT = 6, ++ FCMPDUN = 6, ++ ++ FDIVS = 19, ++ FDIVD = 19, ++ FSQRTS = 19, ++ FSQRTD = 19, ++ ++ FRECS = 3, ++ FRECD = 3, ++ ++ CMOVDL = 4, ++ CMOVDL_G = 4, ++ CMOVDL_P = 4, ++ CMOVDL_Z = 4, ++ CMOVDL_N = 4, ++ CMOVDLU = 4, ++ CMOVDLU_G = 4, ++ CMOVDLU_P = 4, ++ CMOVDLU_Z = 4, ++ CMOVDLU_N = 4, ++ CMOVDWU = 4, ++ CMOVDWU_G = 4, ++ CMOVDWU_P = 4, ++ CMOVDWU_Z = 4, ++ CMOVDWU_N = 4, ++ CMOVDW = 4, ++ CMOVDW_G = 4, ++ CMOVDW_P = 4, ++ CMOVDW_Z = 4, ++ CMOVDW_N = 4, ++ ++ CMOVLS = 4, ++ CMOVWS = 4, ++ CMOVLD = 4, ++ CMOVWD = 4, ++ CMOVULS = 4, ++ CMOVUWS = 4, ++ CMOVULD = 4, ++ CMOVUWD = 4, ++ ++ FCVTSH = 2, ++ FCVTHS = 2, ++ ++ FBEQ = 1, ++ FBGE = 1, ++ FBGT = 1, ++ FBLE = 1, ++ FBLT = 1, ++ FBNE = 1, ++ ++ RFPCR = 1, ++ WFPCR = 1, ++ SETFPEC0 = 1, ++ SETFPEC1 = 1, ++ SETFPEC2 = 1, ++ SETFPEC3 = 1, ++ ++}; ++ ++int DadduLatency(bool is_operand_register = true) { ++ if (is_operand_register) { ++ return 1; ++ } else { ++ return 2; // Estimated max. ++ } ++} ++ ++int SrlwLatency(bool is_operand_register = true) { ++ if (is_operand_register) { ++ return 4; ++ } else { ++ return 3; // Estimated max. ++ } ++} ++ ++int DsubuLatency(bool is_operand_register = true) { ++ return DadduLatency(is_operand_register); ++} ++ ++int AndLatency(bool is_operand_register = true) { ++ return DadduLatency(is_operand_register); ++} ++ ++int OrLatency(bool is_operand_register = true) { ++ return DadduLatency(is_operand_register); ++} ++ ++int NorLatency(bool is_operand_register = true) { ++ if (is_operand_register) { ++ return 2; ++ } else { ++ return 3; // Estimated max. ++ } ++} ++ ++int XorLatency(bool is_operand_register = true) { ++ return DadduLatency(is_operand_register); ++} ++ ++int MulLatency(bool is_operand_register = true) { ++ if (is_operand_register) { ++ return Latency::MULW; ++ } else { ++ return Latency::MULW + 1; ++ } ++} ++ ++int DmulLatency(bool is_operand_register = true) { ++ int latency = 0; ++ latency = Latency::MULL; ++ if (!is_operand_register) { ++ latency += 1; ++ } ++ return latency; ++} ++ ++// Mulwh =(li +)mull + sral ++int MulhLatency(bool is_operand_register = true) { ++ int latency = 0; ++ latency = Latency::MULL + 1; ++ if (!is_operand_register) { ++ latency += 1; ++ } ++ return latency; ++} ++ ++// Mulwhu = zapnot + zapnot + mull ++int MulwhuLatency(bool is_operand_register = true) { ++ int latency = 0; ++ latency = Latency::MULL + 2; ++ if (!is_operand_register) { ++ latency += 1; ++ } ++ return latency; ++} ++ ++// Mullh = (li +)umulh + srll + mull + subl + srll +mull +subl ++int DMulhLatency(bool is_operand_register = true) { ++ int latency = 0; ++ latency = Latency::MULL + Latency::MULL + 5; ++ if (!is_operand_register) { ++ latency += 1; ++ } ++ return latency; ++} ++ ++// Divw = ifmovd*2 + fcvtld*2 + fdivd + fcvtdl_z + fimovd ++int DivLatency(bool is_operand_register = true) { ++ if (is_operand_register) { ++ return Latency::IFMOVD * 2 + Latency::FCVTLD * 2 + Latency::FDIVD + ++ Latency::FCVTDL_Z + Latency::FIMOVD; ++ } else { ++ return Latency::IFMOVD * 2 + Latency::FCVTLD * 2 + Latency::FDIVD + ++ Latency::FCVTDL_Z + Latency::FIMOVD + 1; ++ } ++} ++ ++// Divwu = Divw + zapnot * 2 ++int DivuLatency(bool is_operand_register = true) { ++ if (is_operand_register) { ++ return Latency::IFMOVD * 2 + Latency::FCVTLD * 2 + Latency::FDIVD + ++ Latency::FCVTDL_Z + Latency::FIMOVD + 2; ++ } else { ++ return Latency::IFMOVD * 2 + Latency::FCVTLD * 2 + Latency::FDIVD + ++ Latency::FCVTDL_Z + Latency::FIMOVD + 2 + 1; ++ } ++} ++ ++// Divl = ifmovd * 2 + fcvtld * 2 + fdivd + fcvtdl_z + fimovd ++int DdivLatency(bool is_operand_register = true) { ++ int latency = 0; ++ latency = Latency::IFMOVD * 2 + Latency::FCVTLD * 2 + Latency::FDIVD + ++ Latency::FCVTDL_Z + Latency::FIMOVD; ++ if (!is_operand_register) { ++ latency += 1; ++ } ++ return latency; ++} ++ ++// Divlu = Divl ++int DdivuLatency(bool is_operand_register = true) { ++ int latency = 0; ++ latency = Latency::IFMOVD * 2 + Latency::FCVTLD * 2 + Latency::FDIVD + ++ Latency::FCVTDL_Z + Latency::FIMOVD; ++ if (!is_operand_register) { ++ latency += 1; ++ } ++ return latency; ++} ++ ++// Modw = Divw + mulw + subw ++int ModLatency(bool is_operand_register = true) { ++ int latency = 0; ++ latency = Latency::IFMOVD * 2 + Latency::FCVTLD * 2 + Latency::FDIVD + ++ Latency::FCVTDL_Z + Latency::FIMOVD + Latency::MULW + ++ DadduLatency(is_operand_register); ++ if (!is_operand_register) { ++ latency += 1; ++ } ++ return latency; ++} ++ ++// Modwu = Modw + zapnot * 2 ++int ModuLatency(bool is_operand_register = true) { ++ int latency = 0; ++ latency = Latency::IFMOVD * 2 + Latency::FCVTLD * 2 + Latency::FDIVD + ++ Latency::FCVTDL_Z + Latency::FIMOVD + Latency::MULW + ++ DadduLatency(is_operand_register); ++ ; ++ if (!is_operand_register) { ++ latency += 1; ++ } ++ return latency; ++} ++ ++// Modl = Modw ++int DmodLatency(bool is_operand_register = true) { ++ int latency = 0; ++ latency = Latency::IFMOVD * 2 + Latency::FCVTLD * 2 + Latency::FDIVD + ++ Latency::FCVTDL_Z + Latency::FIMOVD + Latency::MULW + ++ DadduLatency(is_operand_register); ++ if (!is_operand_register) { ++ latency += 1; ++ } ++ return latency; ++} ++ ++// Modlu = Modl ++int DmoduLatency(bool is_operand_register = true) { ++ int latency = 0; ++ latency = Latency::IFMOVD * 2 + Latency::FCVTLD * 2 + Latency::FDIVD + ++ Latency::FCVTDL_Z + Latency::FIMOVD + Latency::MULW + ++ DadduLatency(is_operand_register); ++ if (!is_operand_register) { ++ latency += 1; ++ } ++ return latency; ++} ++ ++int LsalLatency() { ++ // Estimated max. ++ return DadduLatency() + 1; ++} ++ ++int CallLatency() { ++ // Estimated. ++ return DadduLatency(false) + Latency::BRANCH + 5; ++} ++ ++int JumpLatency() { ++ // Estimated max. ++ return 1 + DadduLatency() + Latency::BRANCH + 2; ++} ++ ++int SmiUntagLatency() { return 1; } ++ ++int PrepareForTailCallLatency() { ++ // Estimated max. ++ return 2 * (LsalLatency() + DadduLatency(false)) + 2 + Latency::BRANCH + ++ Latency::BRANCH + 2 * DsubuLatency(false) + 2 + Latency::BRANCH + 1; ++} ++ ++int AssertLatency() { return 1; } ++ ++int PrepareCallCFunctionLatency() { ++ int frame_alignment = TurboAssembler::ActivationFrameAlignment(); ++ if (frame_alignment > kSystemPointerSize) { ++ return 1 + DsubuLatency(false) + AndLatency(false) + 1; ++ } else { ++ return DsubuLatency(false); ++ } ++} ++ ++int AdjustBaseAndOffsetLatency() { ++ return 3; // Estimated max. ++} ++ ++int AlignedMemoryLatency() { return AdjustBaseAndOffsetLatency() + 1; } ++ ++int MultiPushLatency() { ++ int latency = DsubuLatency(false); ++ for (int16_t i = kNumRegisters - 1; i >= 0; i--) { ++ latency++; ++ } ++ return latency; ++} ++ ++int MultiPushFPULatency() { ++ int latency = DsubuLatency(false); ++ for (int16_t i = kNumRegisters - 1; i >= 0; i--) { ++ latency += 4; ++ } ++ return latency; ++} ++ ++int PushCallerSavedLatency(SaveFPRegsMode fp_mode) { ++ int latency = MultiPushLatency(); ++ if (fp_mode == SaveFPRegsMode::kSave) { ++ latency += MultiPushFPULatency(); ++ } ++ return latency; ++} ++ ++int MultiPopLatency() { ++ int latency = DadduLatency(false); ++ for (int16_t i = 0; i < kNumRegisters; i++) { ++ latency++; ++ } ++ return latency; ++} ++ ++int MultiPopFPULatency() { ++ int latency = DadduLatency(false); ++ for (int16_t i = 0; i < kNumRegisters; i++) { ++ latency += 4; ++ } ++ return latency; ++} ++ ++int PopCallerSavedLatency(SaveFPRegsMode fp_mode) { ++ int latency = MultiPopLatency(); ++ if (fp_mode == SaveFPRegsMode::kSave) { ++ latency += MultiPopFPULatency(); ++ } ++ return latency; ++} ++ ++int CallCFunctionHelperLatency() { ++ // Estimated. ++ int latency = AndLatency(false) + Latency::BRANCH + 2 + CallLatency(); ++ if (base::OS::ActivationFrameAlignment() > kSystemPointerSize) { ++ latency++; ++ } else { ++ latency += DadduLatency(false); ++ } ++ return latency; ++} ++ ++int CallCFunctionLatency() { return 1 + CallCFunctionHelperLatency(); } ++ ++int AssembleArchJumpLatency() { ++ // Estimated max. ++ return Latency::BRANCH; ++} ++ ++int AssembleArchLookupSwitchLatency(const Instruction* instr) { ++ int latency = 0; ++ for (size_t index = 2; index < instr->InputCount(); index += 2) { ++ latency += 1 + Latency::BRANCH; ++ } ++ return latency + AssembleArchJumpLatency(); ++} ++ ++int GenerateSwitchTableLatency() { ++ int latency = 0; ++ latency = LsalLatency() + 2; ++ ++ latency += 2; ++ return latency; ++} ++ ++int AssembleArchTableSwitchLatency() { ++ return Latency::BRANCH + GenerateSwitchTableLatency(); ++} ++ ++int DropAndRetLatency() { ++ // Estimated max. ++ return DadduLatency(false) + JumpLatency(); ++} ++ ++int AssemblerReturnLatency() { ++ // Estimated max. ++ return DadduLatency(false) + MultiPopLatency() + MultiPopFPULatency() + ++ Latency::BRANCH + DadduLatency() + 1 + DropAndRetLatency(); ++} ++ ++int TryInlineTruncateDoubleToILatency() { ++ return 2 + Latency::FCVTDL_Z + Latency::FCVTLW + Latency::FIMOVS + 1 + 2 + ++ AndLatency(false) + Latency::BRANCH; ++} ++ ++int CallStubDelayedLatency() { return 1 + CallLatency(); } ++ ++int TruncateDoubleToIDelayedLatency() { ++ // TODO(sw64): This no longer reflects how TruncateDoubleToI is called. ++ return TryInlineTruncateDoubleToILatency() + 1 + DsubuLatency(false) + 4 + ++ CallStubDelayedLatency() + DadduLatency(false) + 1; ++} ++ ++int CheckPageFlagLatency() { ++ return AndLatency(false) + AlignedMemoryLatency() + AndLatency(false) + ++ Latency::BRANCH; ++} ++ ++int SltuLatency(bool is_operand_register = true) { ++ if (is_operand_register) { ++ return 1; ++ } else { ++ return 2; // Estimated max. ++ } ++} ++ ++int BranchShortHelperR6Latency() { ++ return 2; // Estimated max. ++} ++ ++int BranchShortHelperLatency() { ++ return SltuLatency() + 2; // Estimated max. ++} ++ ++int BranchShortLatency() { return BranchShortHelperR6Latency(); } ++ ++int MoveLatency() { return 1; } ++ ++int MovToFloatParametersLatency() { return 2 * MoveLatency(); } ++ ++int MovFromFloatResultLatency() { return MoveLatency(); } ++ ++int DaddOverflowLatency() { ++ // Estimated max. ++ return 6; ++} ++ ++int SublOverflowLatency() { ++ // Estimated max. ++ return 6; ++} ++ ++int MulwOverflowLatency() { ++ // Estimated max. ++ return MulLatency() + MulhLatency() + 2; ++} ++ ++// Ctlzw = addw + ctlz + subl + sellt ++int CtlzwLatency() { return 4; } ++ ++// Ctlzl = ctlz ++int CtlzlLatency() { return 1; } ++ ++// Cttzw = cttz + subl + selge ++int CttzwLatency() { return 3; } ++ ++// Cttzl = cttz ++int CttzlLatency() { return 1; } ++ ++// popcnt = zapnot + ctpop ++int CtpopwLatency() { return 2; } ++ ++// Ctpopl = ctpop ++int CtpoplLatency() { return 1; } ++ ++// Ext TODO ++int ExtLatency() { return 1; } ++ ++// Ins = li + and_ + slll * 2 + bic + bis + addw ++int InsLatency() { return 7; } ++ ++// Dins = Ins - addw ++int DinsLatency() { return 6; } ++ ++// Rolw = and_ + ldi + subw + and_ + zapnot + srll + addw + and_ + slll + addw + ++// bis + addw ++int RolwLatency(bool is_operand_register = true) { ++ if (is_operand_register) { ++ return 11; ++ } else { ++ return 7; ++ } ++} ++ ++int RollLatency(bool is_operand_register = true) { ++ if (is_operand_register) { ++ return 5; ++ } else { ++ return 3; ++ } ++} ++ ++int CompareFLatency() { return 1; } ++ ++int CompareIsNanFLatency() { return CompareFLatency(); } ++ ++int Float64RoundLatency() { return 32; } ++ ++int Float32RoundLatency() { return 36; } ++ ++int PushLatency() { return DadduLatency() + AlignedMemoryLatency(); } ++ ++int ByteSwapSignedLatency() { return 2; } ++ ++int LlLatency(int offset) { ++ bool is_one_instruction = is_int9(offset); ++ if (is_one_instruction) { ++ return 1; ++ } else { ++ return 3; ++ } ++} ++ ++int ExtractBitsLatency(bool sign_extend, int size) { ++ int latency = 2; ++ if (sign_extend) { ++ switch (size) { ++ case 8: ++ case 16: ++ case 32: ++ latency += 1; ++ break; ++ default: ++ UNREACHABLE(); ++ } ++ } ++ return latency; ++} ++ ++int InsertBitsLatency() { return 2 + DsubuLatency(false) + 2; } ++ ++int ScLatency(int offset) { ++ bool is_one_instruction = is_int9(offset); ++ if (is_one_instruction) { ++ return 1; ++ } else { ++ return 3; ++ } ++} ++ ++int Word32AtomicExchangeLatency(bool sign_extend, int size) { ++ return DadduLatency(false) + 1 + DsubuLatency() + 2 + LlLatency(0) + ++ ExtractBitsLatency(sign_extend, size) + InsertBitsLatency() + ++ ScLatency(0) + BranchShortLatency() + 1; ++} ++ ++int Word32AtomicCompareExchangeLatency(bool sign_extend, int size) { ++ return 2 + DsubuLatency() + 2 + LlLatency(0) + ++ ExtractBitsLatency(sign_extend, size) + InsertBitsLatency() + ++ ScLatency(0) + BranchShortLatency() + 1; ++} ++ ++int InstructionScheduler::GetInstructionLatency(const Instruction* instr) { ++ switch (instr->arch_opcode()) { ++ case kArchCallCodeObject: ++#if V8_ENABLE_WEBASSEMBLY ++ case kArchCallWasmFunction: ++#endif // V8_ENABLE_WEBASSEMBLY ++ return CallLatency(); ++ case kArchTailCallCodeObject: ++#if V8_ENABLE_WEBASSEMBLY ++ case kArchTailCallWasm: ++#endif // V8_ENABLE_WEBASSEMBLY ++ case kArchTailCallAddress: ++ return JumpLatency(); ++ case kArchCallJSFunction: { ++ int latency = 0; ++ if (FLAG_debug_code) { ++ latency = 1 + AssertLatency(); ++ } ++ return latency + 1 + DadduLatency(false) + CallLatency(); ++ } ++ case kArchPrepareCallCFunction: ++ return PrepareCallCFunctionLatency(); ++ case kArchSaveCallerRegisters: { ++ auto fp_mode = ++ static_cast(MiscField::decode(instr->opcode())); ++ return PushCallerSavedLatency(fp_mode); ++ } ++ case kArchRestoreCallerRegisters: { ++ auto fp_mode = ++ static_cast(MiscField::decode(instr->opcode())); ++ return PopCallerSavedLatency(fp_mode); ++ } ++ case kArchPrepareTailCall: ++ return 2; ++ case kArchCallCFunction: ++ return CallCFunctionLatency(); ++ case kArchJmp: ++ return AssembleArchJumpLatency(); ++ case kArchTableSwitch: ++ return AssembleArchTableSwitchLatency(); ++ case kArchAbortCSADcheck: ++ return CallLatency() + 1; ++ case kArchDebugBreak: ++ return 1; ++ case kArchComment: ++ case kArchNop: ++ case kArchThrowTerminator: ++ case kArchDeoptimize: ++ return 0; ++ case kArchRet: ++ return AssemblerReturnLatency(); ++ case kArchFramePointer: ++ return 1; ++ case kArchParentFramePointer: ++ return AlignedMemoryLatency(); ++ case kArchTruncateDoubleToI: ++ return TruncateDoubleToIDelayedLatency(); ++ case kArchStoreWithWriteBarrier: ++ return DadduLatency() + 1 + CheckPageFlagLatency(); ++ case kArchStackSlot: ++ return DadduLatency(false) + AndLatency(false) + AssertLatency() + ++ DadduLatency(false) + AndLatency(false) + BranchShortLatency() + ++ 1 + DsubuLatency() + DadduLatency(); ++ case kIeee754Float64Acos: ++ case kIeee754Float64Acosh: ++ case kIeee754Float64Asin: ++ case kIeee754Float64Asinh: ++ case kIeee754Float64Atan: ++ case kIeee754Float64Atanh: ++ case kIeee754Float64Atan2: ++ case kIeee754Float64Cos: ++ case kIeee754Float64Cosh: ++ case kIeee754Float64Cbrt: ++ case kIeee754Float64Exp: ++ case kIeee754Float64Expm1: ++ case kIeee754Float64Log: ++ case kIeee754Float64Log1p: ++ case kIeee754Float64Log2: ++ case kIeee754Float64Log10: ++ case kIeee754Float64Pow: ++ case kIeee754Float64Sin: ++ case kIeee754Float64Sinh: ++ case kIeee754Float64Tan: ++ case kIeee754Float64Tanh: ++ return PrepareCallCFunctionLatency() + MovToFloatParametersLatency() + ++ CallCFunctionLatency() + MovFromFloatResultLatency(); ++ case kSw64Addw: ++ case kSw64Addl: ++ return DadduLatency(instr->InputAt(1)->IsRegister()); ++ case kSw64AddlOvf: ++ return DaddOverflowLatency(); ++ case kSw64Subw: ++ case kSw64Subl: ++ return DsubuLatency(instr->InputAt(1)->IsRegister()); ++ case kSw64SublOvf: ++ return SublOverflowLatency(); ++ case kSw64Mulw: ++ return MulLatency(); ++ case kSw64MulwOvf: ++ return MulwOverflowLatency(); ++ case kSw64MulwHigh: ++ return MulhLatency(); ++ case kSw64MulwHighU: ++ return MulwhuLatency(); ++ case kSw64MullHigh: ++ return DMulhLatency(); ++ case kSw64Divw: ++ return DivLatency(instr->InputAt(1)->IsRegister()); ++ case kSw64Divwu: ++ return DivuLatency(instr->InputAt(1)->IsRegister()); ++ case kSw64Modw: ++ return ModLatency(instr->InputAt(1)->IsRegister()); ++ case kSw64Modwu: ++ return ModuLatency(instr->InputAt(1)->IsRegister()); ++ case kSw64Mull: ++ return DmulLatency(instr->InputAt(1)->IsRegister()); ++ case kSw64Divl: ++ return ModLatency(instr->InputAt(1)->IsRegister()); ++ case kSw64Divlu: ++ return DdivLatency(instr->InputAt(1)->IsRegister()); ++ case kSw64Modl: ++ return DmodLatency(instr->InputAt(1)->IsRegister()); ++ case kSw64Modlu: ++ return DmodLatency(instr->InputAt(1)->IsRegister()); ++ case kSw64Lsal: ++ case kSw64Lsaw: ++ return LsalLatency(); ++ case kSw64Andl: ++ return AndLatency(instr->InputAt(1)->IsRegister()); ++ case kSw64Andw: { ++ bool is_operand_register = instr->InputAt(1)->IsRegister(); ++ int latency = AndLatency(is_operand_register); ++ if (is_operand_register) { ++ return latency + 2; ++ } else { ++ return latency + 1; ++ } ++ } ++ case kSw64Orl: ++ return OrLatency(instr->InputAt(1)->IsRegister()); ++ case kSw64Orw: { ++ bool is_operand_register = instr->InputAt(1)->IsRegister(); ++ int latency = OrLatency(is_operand_register); ++ if (is_operand_register) { ++ return latency + 2; ++ } else { ++ return latency + 1; ++ } ++ } ++ case kSw64Norl: ++ return NorLatency(instr->InputAt(1)->IsRegister()); ++ case kSw64Norw: { ++ bool is_operand_register = instr->InputAt(1)->IsRegister(); ++ int latency = NorLatency(is_operand_register); ++ if (is_operand_register) { ++ return latency + 2; ++ } else { ++ return latency + 1; ++ } ++ } ++ case kSw64Xorl: ++ return XorLatency(instr->InputAt(1)->IsRegister()); ++ case kSw64Xorw: { ++ bool is_operand_register = instr->InputAt(1)->IsRegister(); ++ int latency = XorLatency(is_operand_register); ++ if (is_operand_register) { ++ return latency + 2; ++ } else { ++ return latency + 1; ++ } ++ } ++ case kSw64Ctlzw: ++ return CtlzwLatency(); ++ case kSw64Ctlzl: ++ return CtlzlLatency(); ++ case kSw64Cttzw: ++ return CttzwLatency(); ++ case kSw64Cttzl: ++ return CttzlLatency(); ++ case kSw64Ctpopw: ++ return CtpopwLatency(); ++ case kSw64Ctpopl: ++ return CtpoplLatency(); ++ case kSw64Sllw: ++ return 1; ++ case kSw64Srlw: ++ case kSw64Sraw: ++ return 2; ++ case kSw64Extw: ++ case kSw64Extl: ++ return ExtLatency(); ++ case kSw64InsZerow: ++ return InsLatency(); ++ case kSw64InsZerod: ++ return DinsLatency(); ++ case kSw64Slll: ++ case kSw64Srll: ++ case kSw64Sral: ++ return 1; ++ case kSw64Rolw: ++ return RolwLatency(instr->InputAt(1)->IsRegister()); ++ case kSw64Roll: ++ return RollLatency(instr->InputAt(1)->IsRegister()); ++ case kSw64Tst: ++ return AndLatency(instr->InputAt(1)->IsRegister()); ++ case kSw64Mov: ++ return 1; ++ case kSw64Fcmps: ++ return CompareFLatency(); ++ case kSw64Fadds: ++ case kSw64Fsubs: ++ case kSw64Fmuls: ++ case kSw64Fdivs: ++ case kSw64Frecs: ++ case kSw64Frecd: ++ return 1; ++ case kSw64Fmods: ++ return PrepareCallCFunctionLatency() + MovToFloatParametersLatency() + ++ CallCFunctionLatency() + MovFromFloatResultLatency(); ++ case kSw64Fabss: ++ return Latency::FCPYS; ++ case kSw64Fnegs: ++ return 1; ++ case kSw64Fsqrts: ++ return Latency::FSQRTS; ++ case kSw64Fcmpd: ++ return CompareFLatency(); ++ case kSw64Faddd: ++ case kSw64Fsubd: ++ case kSw64Fmuld: ++ case kSw64Fdivd: ++ return 1; ++ case kSw64Fmodd: ++ return PrepareCallCFunctionLatency() + MovToFloatParametersLatency() + ++ CallCFunctionLatency() + MovFromFloatResultLatency(); ++ case kSw64Fabsd: ++ return Latency::FCPYS; ++ case kSw64Fnegd: ++ return 1; ++ case kSw64Fsqrtd: ++ return Latency::FSQRTD; ++ case kSw64Float64RoundDown: ++ case kSw64Float64RoundTruncate: ++ case kSw64Float64RoundUp: ++ case kSw64Float64RoundTiesEven: ++ return Float64RoundLatency(); // TODO ++ case kSw64Float32RoundDown: ++ case kSw64Float32RoundTruncate: ++ case kSw64Float32RoundUp: ++ case kSw64Float32RoundTiesEven: ++ return Float32RoundLatency(); // TODO ++ /* case kSw64Float32Max: ++ return Float32MaxLatency(); ++ case kSw64Float64Max: ++ return Float64MaxLatency(); ++ case kSw64Float32Min: ++ return Float32MinLatency(); ++ case kSw64Float64Min: ++ return Float64MinLatency();*/ ++ case kSw64Float64SilenceNaN: ++ return Latency::FSUBD; ++ case kSw64F64ToF32: ++ return Latency::FCVTDS; ++ case kSw64F32ToF64: ++ return Latency::FCVTSD; ++ case kSw64I32ToF64: ++ return Latency::IFMOVD + Latency::FCVTLD; ++ case kSw64I32ToF32: ++ return Latency::IFMOVS + Latency::FCVTWL + Latency::FCVTLS; ++ case kSw64UI32ToF32: ++ return 1 + Latency::FIMOVS; ++ case kSw64I64ToF32: ++ return Latency::IFMOVD + Latency::FCVTLS; ++ case kSw64I64ToF64: ++ return Latency::IFMOVD + Latency::FCVTLD; ++ case kSw64UI32ToF64: ++ return 1 + Latency::FIMOVS; ++ case kSw64UI64ToF64: ++ return 1 + Latency::FIMOVD; ++ case kSw64UI64ToF32: ++ return 1 + Latency::FIMOVD; ++ case kSw64F64ToI32_n: ++ return Latency::FCVTDL_N + Latency::FCVTLW + Latency::FIMOVS; ++ case kSw64F64ToI32_p: ++ return Latency::FCVTDL_P + Latency::FCVTLW + Latency::FIMOVS; ++ case kSw64F64ToI32_g: ++ return Latency::FCVTDL_G + Latency::FCVTLW + Latency::FIMOVS; ++ case kSw64F64ToI32: ++ return Latency::FCVTDL_Z + Latency::FCVTLW + Latency::FIMOVS; ++ case kSw64F32ToI32_n: ++ return Latency::FCVTSD + Latency::FCVTDL_N + Latency::FCVTLW + ++ Latency::FIMOVS; ++ case kSw64F32ToI32_p: ++ return Latency::FCVTSD + Latency::FCVTDL_P + Latency::FCVTLW + ++ Latency::FIMOVS; ++ case kSw64F32ToI32_g: ++ return Latency::FCVTSD + Latency::FCVTDL_G + Latency::FCVTLW + ++ Latency::FIMOVS; ++ case kSw64F32ToI32: ++ return Latency::FCVTSD + Latency::FCVTDL_Z + Latency::FCVTLW + ++ Latency::FIMOVS + 3; // cmplt selne ? ++ // case kSw64F32ToI64: ++ // return 5; //TODO ++ // case kSw64F64ToI64: ++ // return 5; ++ // case kSw64F64ToUI32: ++ // return CompareFLatency() + 2 * Latency::BRANCH + ++ // 2 * Latency::TRUNC_W_D + Latency::SUB_D + OrLatency() + ++ // Latency::MTC1 + Latency::MFC1 + Latency::MTHC1 + 1; ++ // case kSw64F32ToUI32: ++ // return CompareFLatency() + 2 * Latency::BRANCH + ++ // 2 * Latency::TRUNC_W_S + Latency::SUB_S + OrLatency() + ++ // Latency::MTC1 + 2 * Latency::MFC1 + 2 + MovzLatency(); ++ // case kSw64F32ToUI64: ++ // return TruncUlSLatency(); ++ // case kSw64F64ToUI64: ++ // return TruncUlDLatency(); ++ // TODO ++ case kSw64BitcastDL: ++ return 4; ++ case kSw64BitcastLD: ++ return 4; ++ case kSw64BitcastSW: ++ case kSw64BitcastWS: ++ return 4; ++ case kSw64Float64ExtractLowWord32: ++ case kSw64Float64InsertLowWord32: ++ return 12; ++ case kSw64Float64ExtractHighWord32: ++ case kSw64Float64InsertHighWord32: ++ return 5; ++ case kSw64Sextb: ++ case kSw64Sexth: ++ return 1; ++ case kSw64Ldbu: ++ case kSw64Ldhu: ++ case kSw64Ldl: ++ case kSw64Ldl_u: ++ case kSw64Ldw: ++ case kSw64Ldw_u: ++ case kSw64Flds: ++ case kSw64Flds_u: ++ case kSw64Fldd: ++ case kSw64Fldd_u: ++ case kSw64Stb: ++ case kSw64Sth: ++ case kSw64Stw: ++ case kSw64Stw_u: ++ case kSw64Stl: ++ case kSw64Stl_u: ++ case kSw64Fsts: ++ case kSw64Fsts_u: ++ case kSw64Fstd: ++ case kSw64Fstd_u: ++ return 4; ++ ++ case kSw64Ldb: ++ case kSw64Ldh: ++ case kSw64Ldwu: ++ case kSw64Ldwu_u: ++ return 5; ++ ++ case kSw64Ldhu_u: ++ return 10; ++ case kSw64Ldh_u: ++ return 11; ++ case kSw64Sth_u: ++ return 8 + SrlwLatency(instr->InputAt(1)->IsRegister()); ++ case kSw64Push: { ++ int latency = 0; ++ if (instr->InputAt(0)->IsFPRegister()) { ++ latency = 4 + DsubuLatency(false); ++ } else { ++ latency = PushLatency(); ++ } ++ return latency; ++ } ++ case kSw64Peek: { ++ int latency = 0; ++ if (instr->OutputAt(0)->IsFPRegister()) { ++ latency = Latency::FLDD; ++ } else { ++ latency = AlignedMemoryLatency(); ++ } ++ return latency; ++ } ++ case kSw64StackClaim: ++ return DsubuLatency(false); ++ case kSw64Poke: { ++ int latency = 0; ++ if (instr->InputAt(0)->IsFPRegister()) { ++ if (instr->InputAt(0)->IsSimd128Register()) { ++ latency = 1; // Estimated value. ++ } else { ++ latency = 4; ++ } ++ } else { ++ latency = AlignedMemoryLatency(); ++ } ++ return latency; ++ } ++ // case kSw64ByteSwap64: ++ // case kSw64ByteSwap32: ++ case kAtomicLoadInt8: ++ case kAtomicLoadUint8: ++ case kAtomicLoadInt16: ++ case kAtomicLoadUint16: ++ case kAtomicLoadWord32: ++ return 2; ++ case kAtomicStoreWord8: ++ case kAtomicStoreWord16: ++ case kAtomicStoreWord32: ++ return 3; ++ case kAtomicExchangeInt8: ++ return Word32AtomicExchangeLatency(true, 8); ++ case kAtomicExchangeUint8: ++ return Word32AtomicExchangeLatency(false, 8); ++ case kAtomicExchangeInt16: ++ return Word32AtomicExchangeLatency(true, 16); ++ case kAtomicExchangeUint16: ++ return Word32AtomicExchangeLatency(false, 16); ++ case kAtomicExchangeWord32: ++ return 2 + LlLatency(0) + 1 + ScLatency(0) + BranchShortLatency() + 1; ++ case kAtomicCompareExchangeInt8: ++ return Word32AtomicCompareExchangeLatency(true, 8); ++ case kAtomicCompareExchangeUint8: ++ return Word32AtomicCompareExchangeLatency(false, 8); ++ case kAtomicCompareExchangeInt16: ++ return Word32AtomicCompareExchangeLatency(true, 16); ++ case kAtomicCompareExchangeUint16: ++ return Word32AtomicCompareExchangeLatency(false, 16); ++ case kAtomicCompareExchangeWord32: ++ return 3 + LlLatency(0) + BranchShortLatency() + 1 + ScLatency(0) + ++ BranchShortLatency() + 1; ++ /* case kSw64Word64AtomicCompareExchangeUint64:*/ ++ //case kWord32Atomic##op##Int8: \ ++ //case kWord32Atomic##op##Uint8: \ ++ //case kWord32Atomic##op##Int16: \ ++ //case kWord32Atomic##op##Uint16: \ ++ //case kWord32Atomic##op##Word32: \ ++ //case kSw64Word64Atomic##op##Uint8: \ ++ //case kSw64Word64Atomic##op##Uint16: \ ++ //case kSw64Word64Atomic##op##Uint32: \ ++ //case kSw64Word64Atomic##op##Uint64: ++ case kSw64AssertEqual: ++ return AssertLatency(); ++ default: ++ return 1; ++ } ++} ++ ++} // namespace compiler ++} // namespace internal ++} // namespace v8 +diff --git a/deps/v8/src/compiler/backend/sw64/instruction-selector-sw64.cc b/deps/v8/src/compiler/backend/sw64/instruction-selector-sw64.cc +new file mode 100755 +index 00000000..4f9a42c2 +--- /dev/null ++++ b/deps/v8/src/compiler/backend/sw64/instruction-selector-sw64.cc +@@ -0,0 +1,3241 @@ ++// Copyright 2014 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "src/base/bits.h" ++#include "src/base/platform/wrappers.h" ++#include "src/codegen/machine-type.h" ++#include "src/compiler/backend/instruction-selector-impl.h" ++#include "src/compiler/node-matchers.h" ++#include "src/compiler/node-properties.h" ++ ++namespace v8 { ++namespace internal { ++namespace compiler { ++ ++#define TRACE_UNIMPL() \ ++ PrintF("UNIMPLEMENTED instr_sel: %s at line %d\n", __FUNCTION__, __LINE__) ++ ++#define TRACE() PrintF("instr_sel: %s at line %d\n", __FUNCTION__, __LINE__) ++ ++// Adds Sw64-specific methods for generating InstructionOperands. ++class Sw64OperandGenerator final : public OperandGenerator { ++ public: ++ explicit Sw64OperandGenerator(InstructionSelector* selector) ++ : OperandGenerator(selector) {} ++ ++ InstructionOperand UseOperand(Node* node, InstructionCode opcode) { ++ if (CanBeImmediate(node, opcode)) { ++ return UseImmediate(node); ++ } ++ return UseRegister(node); ++ } ++ ++ // Use the zero register if the node has the immediate value zero, otherwise ++ // assign a register. ++ InstructionOperand UseRegisterOrImmediateZero(Node* node) { ++ if ((IsIntegerConstant(node) && (GetIntegerConstantValue(node) == 0)) || ++ (IsFloatConstant(node) && ++ (bit_cast(GetFloatConstantValue(node)) == 0))) { ++ return UseImmediate(node); ++ } ++ return UseRegister(node); ++ } ++ ++ bool IsIntegerConstant(Node* node) { ++ return (node->opcode() == IrOpcode::kInt32Constant) || ++ (node->opcode() == IrOpcode::kInt64Constant); ++ } ++ ++ int64_t GetIntegerConstantValue(Node* node) { ++ if (node->opcode() == IrOpcode::kInt32Constant) { ++ return OpParameter(node->op()); ++ } ++ DCHECK_EQ(IrOpcode::kInt64Constant, node->opcode()); ++ return OpParameter(node->op()); ++ } ++ ++ bool IsFloatConstant(Node* node) { ++ return (node->opcode() == IrOpcode::kFloat32Constant) || ++ (node->opcode() == IrOpcode::kFloat64Constant); ++ } ++ ++ double GetFloatConstantValue(Node* node) { ++ if (node->opcode() == IrOpcode::kFloat32Constant) { ++ return OpParameter(node->op()); ++ } ++ DCHECK_EQ(IrOpcode::kFloat64Constant, node->opcode()); ++ return OpParameter(node->op()); ++ } ++ ++ bool CanBeImmediate(Node* node, InstructionCode mode) { ++ return IsIntegerConstant(node) && ++ CanBeImmediate(GetIntegerConstantValue(node), mode); ++ } ++ bool CanBeImmediate(int64_t value, InstructionCode opcode) { ++ switch (ArchOpcodeField::decode(opcode)) { ++ case kSw64Sllw: ++ case kSw64Sraw: ++ case kSw64Srlw: ++ return is_uint5(value); ++ case kSw64Slll: ++ case kSw64Sral: ++ case kSw64Srll: ++ return is_uint6(value); ++ case kSw64Addw: ++ case kSw64Andw: ++ case kSw64Andl: ++ case kSw64Addl: ++ case kSw64Orw: ++ case kSw64Orl: ++ case kSw64Tst: ++ case kSw64Xorl: ++ return is_uint16(value); ++ case kSw64Ldb: ++ case kSw64Ldbu: ++ case kSw64Stb: ++ case kSw64Ldh: ++ case kSw64Ldhu: ++ case kSw64Sth: ++ case kSw64Ldw: ++ case kSw64Stw: ++ case kSw64Ldl: ++ case kSw64Stl: ++ case kSw64Flds: ++ case kSw64Fsts: ++ case kSw64Fldd: ++ case kSw64Fstd: ++ return is_int32(value); ++ default: ++ return is_int16(value); ++ } ++ } ++ ++ private: ++ bool ImmediateFitsAddrMode1Instruction(int32_t imm) const { ++ TRACE_UNIMPL(); ++ return false; ++ } ++}; ++ ++static void VisitRR(InstructionSelector* selector, ArchOpcode opcode, ++ Node* node) { ++ Sw64OperandGenerator g(selector); ++ selector->Emit(opcode, g.DefineAsRegister(node), ++ g.UseRegister(node->InputAt(0))); ++} ++ ++static void VisitRRI(InstructionSelector* selector, ArchOpcode opcode, ++ Node* node) { ++ Sw64OperandGenerator g(selector); ++ int32_t imm = OpParameter(node->op()); ++ selector->Emit(opcode, g.DefineAsRegister(node), ++ g.UseRegister(node->InputAt(0)), g.UseImmediate(imm)); ++} ++ ++static void VisitSimdShift(InstructionSelector* selector, ArchOpcode opcode, ++ Node* node) { ++ Sw64OperandGenerator g(selector); ++ if (g.IsIntegerConstant(node->InputAt(1))) { ++ selector->Emit(opcode, g.DefineAsRegister(node), ++ g.UseRegister(node->InputAt(0)), ++ g.UseImmediate(node->InputAt(1))); ++ } else { ++ selector->Emit(opcode, g.DefineAsRegister(node), ++ g.UseRegister(node->InputAt(0)), ++ g.UseRegister(node->InputAt(1))); ++ } ++} ++ ++static void VisitRRIR(InstructionSelector* selector, ArchOpcode opcode, ++ Node* node) { ++ Sw64OperandGenerator g(selector); ++ int32_t imm = OpParameter(node->op()); ++ selector->Emit(opcode, g.DefineAsRegister(node), ++ g.UseRegister(node->InputAt(0)), g.UseImmediate(imm), ++ g.UseRegister(node->InputAt(1))); ++} ++ ++static void VisitRRR(InstructionSelector* selector, ArchOpcode opcode, ++ Node* node) { ++ Sw64OperandGenerator g(selector); ++ selector->Emit(opcode, g.DefineAsRegister(node), ++ g.UseRegister(node->InputAt(0)), ++ g.UseRegister(node->InputAt(1))); ++} ++ ++// static void VisitUniqueRRR(InstructionSelector* selector, ArchOpcode opcode, ++// Node* node) { ++// Sw64OperandGenerator g(selector); ++// selector->Emit(opcode, g.DefineAsRegister(node), ++// g.UseUniqueRegister(node->InputAt(0)), ++// g.UseUniqueRegister(node->InputAt(1))); ++// } ++ ++void VisitRRRR(InstructionSelector* selector, ArchOpcode opcode, Node* node) { ++ Sw64OperandGenerator g(selector); ++ selector->Emit( ++ opcode, g.DefineSameAsFirst(node), g.UseRegister(node->InputAt(0)), ++ g.UseRegister(node->InputAt(1)), g.UseRegister(node->InputAt(2))); ++} ++ ++static void VisitRRO(InstructionSelector* selector, ArchOpcode opcode, ++ Node* node) { ++ Sw64OperandGenerator g(selector); ++ selector->Emit(opcode, g.DefineAsRegister(node), ++ g.UseRegister(node->InputAt(0)), ++ g.UseOperand(node->InputAt(1), opcode)); ++} ++ ++struct ExtendingLoadMatcher { ++ ExtendingLoadMatcher(Node* node, InstructionSelector* selector) ++ : matches_(false), selector_(selector), base_(nullptr), immediate_(0) { ++ Initialize(node); ++ } ++ ++ bool Matches() const { return matches_; } ++ ++ Node* base() const { ++ DCHECK(Matches()); ++ return base_; ++ } ++ int64_t immediate() const { ++ DCHECK(Matches()); ++ return immediate_; ++ } ++ ArchOpcode opcode() const { ++ DCHECK(Matches()); ++ return opcode_; ++ } ++ ++ private: ++ bool matches_; ++ InstructionSelector* selector_; ++ Node* base_; ++ int64_t immediate_; ++ ArchOpcode opcode_; ++ ++ void Initialize(Node* node) { ++ Int64BinopMatcher m(node); ++ // When loading a 64-bit value and shifting by 32, we should ++ // just load and sign-extend the interesting 4 bytes instead. ++ // This happens, for example, when we're loading and untagging SMIs. ++ DCHECK(m.IsWord64Sar()); ++ if (m.left().IsLoad() && m.right().Is(32) && ++ selector_->CanCover(m.node(), m.left().node())) { ++ DCHECK_EQ(selector_->GetEffectLevel(node), ++ selector_->GetEffectLevel(m.left().node())); ++ MachineRepresentation rep = ++ LoadRepresentationOf(m.left().node()->op()).representation(); ++ DCHECK_EQ(3, ElementSizeLog2Of(rep)); ++ if (rep != MachineRepresentation::kTaggedSigned && ++ rep != MachineRepresentation::kTaggedPointer && ++ rep != MachineRepresentation::kTagged && ++ rep != MachineRepresentation::kWord64) { ++ return; ++ } ++ ++ Sw64OperandGenerator g(selector_); ++ Node* load = m.left().node(); ++ Node* offset = load->InputAt(1); ++ base_ = load->InputAt(0); ++ opcode_ = kSw64Ldw; ++ if (g.CanBeImmediate(offset, opcode_)) { ++ immediate_ = g.GetIntegerConstantValue(offset) + 4; ++ matches_ = g.CanBeImmediate(immediate_, kSw64Ldw); ++ } ++ } ++ } ++}; ++ ++bool TryEmitExtendingLoad(InstructionSelector* selector, Node* node, ++ Node* output_node) { ++ ExtendingLoadMatcher m(node, selector); ++ Sw64OperandGenerator g(selector); ++ if (m.Matches()) { ++ InstructionOperand inputs[2]; ++ inputs[0] = g.UseRegister(m.base()); ++ InstructionCode opcode = ++ m.opcode() | AddressingModeField::encode(kMode_MRI); ++ DCHECK(is_int32(m.immediate())); ++ inputs[1] = g.TempImmediate(static_cast(m.immediate())); ++ InstructionOperand outputs[] = {g.DefineAsRegister(output_node)}; ++ selector->Emit(opcode, arraysize(outputs), outputs, arraysize(inputs), ++ inputs); ++ return true; ++ } ++ return false; ++} ++ ++bool TryMatchImmediate(InstructionSelector* selector, ++ InstructionCode* opcode_return, Node* node, ++ size_t* input_count_return, InstructionOperand* inputs) { ++ Sw64OperandGenerator g(selector); ++ if (g.CanBeImmediate(node, *opcode_return)) { ++ *opcode_return |= AddressingModeField::encode(kMode_MRI); ++ inputs[0] = g.UseImmediate(node); ++ *input_count_return = 1; ++ return true; ++ } ++ return false; ++} ++ ++static void VisitBinop(InstructionSelector* selector, Node* node, ++ InstructionCode opcode, bool has_reverse_opcode, ++ InstructionCode reverse_opcode, ++ FlagsContinuation* cont) { ++ Sw64OperandGenerator g(selector); ++ Int32BinopMatcher m(node); ++ InstructionOperand inputs[2]; ++ size_t input_count = 0; ++ InstructionOperand outputs[1]; ++ size_t output_count = 0; ++ ++ if (TryMatchImmediate(selector, &opcode, m.right().node(), &input_count, ++ &inputs[1])) { ++ inputs[0] = g.UseRegister(m.left().node()); ++ input_count++; ++ } else if (has_reverse_opcode && ++ TryMatchImmediate(selector, &reverse_opcode, m.left().node(), ++ &input_count, &inputs[1])) { ++ inputs[0] = g.UseRegister(m.right().node()); ++ opcode = reverse_opcode; ++ input_count++; ++ } else { ++ inputs[input_count++] = g.UseRegister(m.left().node()); ++ inputs[input_count++] = g.UseOperand(m.right().node(), opcode); ++ } ++ ++ outputs[output_count++] = g.DefineAsRegister(node); ++ ++ DCHECK_NE(0u, input_count); ++ DCHECK_EQ(1u, output_count); ++ DCHECK_GE(arraysize(inputs), input_count); ++ DCHECK_GE(arraysize(outputs), output_count); ++ ++ selector->EmitWithContinuation(opcode, output_count, outputs, input_count, ++ inputs, cont); ++} ++ ++static void VisitBinop(InstructionSelector* selector, Node* node, ++ InstructionCode opcode, bool has_reverse_opcode, ++ InstructionCode reverse_opcode) { ++ FlagsContinuation cont; ++ VisitBinop(selector, node, opcode, has_reverse_opcode, reverse_opcode, &cont); ++} ++ ++static void VisitBinop(InstructionSelector* selector, Node* node, ++ InstructionCode opcode, FlagsContinuation* cont) { ++ VisitBinop(selector, node, opcode, false, kArchNop, cont); ++} ++ ++static void VisitBinop(InstructionSelector* selector, Node* node, ++ InstructionCode opcode) { ++ VisitBinop(selector, node, opcode, false, kArchNop); ++} ++ ++void InstructionSelector::VisitStackSlot(Node* node) { ++ StackSlotRepresentation rep = StackSlotRepresentationOf(node->op()); ++ int alignment = rep.alignment(); ++ int slot = frame_->AllocateSpillSlot(rep.size(), alignment); ++ OperandGenerator g(this); ++ ++ Emit(kArchStackSlot, g.DefineAsRegister(node), ++ sequence()->AddImmediate(Constant(slot)), 0, nullptr); ++} ++ ++void InstructionSelector::VisitAbortCSADcheck(Node* node) { ++ Sw64OperandGenerator g(this); ++ Emit(kArchAbortCSADcheck, g.NoOutput(), g.UseFixed(node->InputAt(0), a0)); ++} ++ ++void EmitLoad(InstructionSelector* selector, Node* node, InstructionCode opcode, ++ Node* output = nullptr) { ++ Sw64OperandGenerator g(selector); ++ Node* base = node->InputAt(0); ++ Node* index = node->InputAt(1); ++ ++ if (g.CanBeImmediate(index, opcode)) { ++ selector->Emit(opcode | AddressingModeField::encode(kMode_MRI), ++ g.DefineAsRegister(output == nullptr ? node : output), ++ g.UseRegister(base), g.UseImmediate(index)); ++ } else { ++ InstructionOperand addr_reg = g.TempRegister(); ++ selector->Emit(kSw64Addl | AddressingModeField::encode(kMode_None), ++ addr_reg, g.UseRegister(base), g.UseRegister(index)); ++ // Emit desired load opcode, using temp addr_reg. ++ selector->Emit(opcode | AddressingModeField::encode(kMode_MRI), ++ g.DefineAsRegister(output == nullptr ? node : output), ++ addr_reg, g.TempImmediate(0)); ++ } ++} ++ ++// TODO ++void InstructionSelector::VisitLoadLane(Node* node) { UNIMPLEMENTED(); } ++ ++void InstructionSelector::VisitStoreLane(Node* node) { UNIMPLEMENTED(); } ++ ++#if 0 ++namespace { ++InstructionOperand EmitAddBeforeS128LoadStore(InstructionSelector* selector, ++ Node* node, ++ InstructionCode* opcode) { ++ Sw64OperandGenerator g(selector); ++ Node* base = node->InputAt(0); ++ Node* index = node->InputAt(1); ++ InstructionOperand addr_reg = g.TempRegister(); ++ selector->Emit(kSw64Addl | AddressingModeField::encode(kMode_None), ++ addr_reg, g.UseRegister(base), g.UseRegister(index)); ++ *opcode |= AddressingModeField::encode(kMode_MRI); ++ return addr_reg; ++} ++ ++} // namespace ++ ++void InstructionSelector::VisitStoreLane(Node* node) { ++ StoreLaneParameters params = StoreLaneParametersOf(node->op()); ++ LoadStoreLaneParams f(params.rep, params.laneidx); ++ InstructionCode opcode = kSw64S128StoreLane; ++ opcode |= MiscField::encode(f.sz); ++ ++ Sw64OperandGenerator g(this); ++ InstructionOperand addr = EmitAddBeforeS128LoadStore(this, node, &opcode); ++ InstructionOperand inputs[4] = { ++ g.UseRegister(node->InputAt(2)), ++ g.UseImmediate(f.laneidx), ++ addr, ++ g.TempImmediate(0), ++ }; ++ Emit(opcode, 0, nullptr, 4, inputs); ++} ++ ++void InstructionSelector::VisitLoadLane(Node* node) { ++ LoadLaneParameters params = LoadLaneParametersOf(node->op()); ++ LoadStoreLaneParams f(params.rep.representation(), params.laneidx); ++ InstructionCode opcode = kSw64S128LoadLane; ++ opcode |= MiscField::encode(f.sz); ++ ++ Sw64OperandGenerator g(this); ++ InstructionOperand addr = EmitAddBeforeS128LoadStore(this, node, &opcode); ++ Emit(opcode, g.DefineSameAsFirst(node), g.UseRegister(node->InputAt(2)), ++ g.UseImmediate(f.laneidx), addr, g.TempImmediate(0)); ++} ++#endif ++ ++void InstructionSelector::VisitLoadTransform(Node* node) { ++ LoadTransformParameters params = LoadTransformParametersOf(node->op()); ++ ++ InstructionCode opcode = kArchNop; ++ switch (params.transformation) { ++ case LoadTransformation::kS128Load8Splat: ++ opcode = kSw64S128LoadSplat; ++ opcode |= MiscField::encode(SWSASize::SWSA_B); ++ break; ++ case LoadTransformation::kS128Load16Splat: ++ opcode = kSw64S128LoadSplat; ++ opcode |= MiscField::encode(SWSASize::SWSA_H); ++ break; ++ case LoadTransformation::kS128Load32Splat: ++ opcode = kSw64S128LoadSplat; ++ opcode |= MiscField::encode(SWSASize::SWSA_W); ++ break; ++ case LoadTransformation::kS128Load64Splat: ++ opcode = kSw64S128LoadSplat; ++ opcode |= MiscField::encode(SWSASize::SWSA_D); ++ break; ++#if 0 ++ case LoadTransformation::kS128Load8x8S: ++ opcode = kSw64S128Load8x8S; ++ break; ++ case LoadTransformation::kS128Load8x8U: ++ opcode = kSw64S128Load8x8U; ++ break; ++ case LoadTransformation::kS128Load16x4S: ++ opcode = kSw64S128Load16x4S; ++ break; ++ case LoadTransformation::kS128Load16x4U: ++ opcode = kSw64S128Load16x4U; ++ break; ++ case LoadTransformation::kS128Load32x2S: ++ opcode = kSw64S128Load32x2S; ++ break; ++ case LoadTransformation::kS128Load32x2U: ++ opcode = kSw64S128Load32x2U; ++ break; ++ case LoadTransformation::kS128Load32Zero: ++ opcode = kSw64S128Load32Zero; ++ break; ++ case LoadTransformation::kS128Load64Zero: ++ opcode = kSw64S128Load64Zero; ++ break; ++#endif ++ default: ++ UNIMPLEMENTED(); ++ } ++ ++ EmitLoad(this, node, opcode); ++} ++ ++void InstructionSelector::VisitLoad(Node* node) { ++ LoadRepresentation load_rep = LoadRepresentationOf(node->op()); ++ ++ InstructionCode opcode = kArchNop; ++ switch (load_rep.representation()) { ++ case MachineRepresentation::kFloat32: ++ opcode = kSw64Flds; ++ break; ++ case MachineRepresentation::kFloat64: ++ opcode = kSw64Fldd; ++ break; ++ case MachineRepresentation::kBit: // Fall through. ++ case MachineRepresentation::kWord8: ++ opcode = load_rep.IsUnsigned() ? kSw64Ldbu : kSw64Ldb; ++ break; ++ case MachineRepresentation::kWord16: ++ opcode = load_rep.IsUnsigned() ? kSw64Ldhu : kSw64Ldh; ++ break; ++ case MachineRepresentation::kWord32: ++ opcode = kSw64Ldw; ++ break; ++ case MachineRepresentation::kTaggedSigned: // Fall through. ++ case MachineRepresentation::kTaggedPointer: // Fall through. ++ case MachineRepresentation::kTagged: // Fall through. ++ case MachineRepresentation::kWord64: ++ opcode = kSw64Ldl; ++ break; ++ case MachineRepresentation::kSimd128: ++ opcode = kSw64SwsaLd; ++ break; ++ case MachineRepresentation::kCompressedPointer: // Fall through. ++ case MachineRepresentation::kSandboxedPointer: // Fall through. ++ case MachineRepresentation::kCompressed: // Fall through. ++ case MachineRepresentation::kMapWord: // Fall through. ++ case MachineRepresentation::kNone: ++ UNREACHABLE(); ++ } ++ ++ EmitLoad(this, node, opcode); ++} ++ ++void InstructionSelector::VisitProtectedLoad(Node* node) { ++ // TODO(eholk) ++ UNIMPLEMENTED(); ++} ++ ++void InstructionSelector::VisitStore(Node* node) { ++ Sw64OperandGenerator g(this); ++ Node* base = node->InputAt(0); ++ Node* index = node->InputAt(1); ++ Node* value = node->InputAt(2); ++ ++ StoreRepresentation store_rep = StoreRepresentationOf(node->op()); ++ WriteBarrierKind write_barrier_kind = store_rep.write_barrier_kind(); ++ MachineRepresentation rep = store_rep.representation(); ++ ++ if (FLAG_enable_unconditional_write_barriers && CanBeTaggedPointer(rep)) { ++ write_barrier_kind = kFullWriteBarrier; ++ } ++ ++ // TODO(sw64): I guess this could be done in a better way. ++ if (write_barrier_kind != kNoWriteBarrier && !FLAG_disable_write_barriers) { ++ DCHECK(CanBeTaggedPointer(rep)); ++ InstructionOperand inputs[3]; ++ size_t input_count = 0; ++ inputs[input_count++] = g.UseUniqueRegister(base); ++ inputs[input_count++] = g.UseUniqueRegister(index); ++ inputs[input_count++] = g.UseUniqueRegister(value); ++ RecordWriteMode record_write_mode = ++ WriteBarrierKindToRecordWriteMode(write_barrier_kind); ++ InstructionOperand temps[] = {g.TempRegister(), g.TempRegister()}; ++ size_t const temp_count = arraysize(temps); ++ InstructionCode code = kArchStoreWithWriteBarrier; ++ code |= MiscField::encode(static_cast(record_write_mode)); ++ Emit(code, 0, nullptr, input_count, inputs, temp_count, temps); ++ } else { ++ ArchOpcode opcode; ++ switch (rep) { ++ case MachineRepresentation::kFloat32: ++ opcode = kSw64Fsts; ++ break; ++ case MachineRepresentation::kFloat64: ++ opcode = kSw64Fstd; ++ break; ++ case MachineRepresentation::kBit: // Fall through. ++ case MachineRepresentation::kWord8: ++ opcode = kSw64Stb; ++ break; ++ case MachineRepresentation::kWord16: ++ opcode = kSw64Sth; ++ break; ++ case MachineRepresentation::kWord32: ++ opcode = kSw64Stw; ++ break; ++ case MachineRepresentation::kTaggedSigned: // Fall through. ++ case MachineRepresentation::kTaggedPointer: // Fall through. ++ case MachineRepresentation::kTagged: // Fall through. ++ case MachineRepresentation::kWord64: ++ opcode = kSw64Stl; ++ break; ++ case MachineRepresentation::kSimd128: ++ opcode = kSw64SwsaSt; ++ break; ++ case MachineRepresentation::kCompressedPointer: // Fall through. ++ case MachineRepresentation::kCompressed: // Fall through. ++ case MachineRepresentation::kSandboxedPointer: // Fall through. ++ case MachineRepresentation::kMapWord: // Fall through. ++ case MachineRepresentation::kNone: ++ UNREACHABLE(); ++ } ++ ++ if (g.CanBeImmediate(index, opcode)) { ++ Emit(opcode | AddressingModeField::encode(kMode_MRI), g.NoOutput(), ++ g.UseRegister(base), g.UseImmediate(index), ++ g.UseRegisterOrImmediateZero(value)); ++ } else { ++ InstructionOperand addr_reg = g.TempRegister(); ++ Emit(kSw64Addl | AddressingModeField::encode(kMode_None), addr_reg, ++ g.UseRegister(index), g.UseRegister(base)); ++ // Emit desired store opcode, using temp addr_reg. ++ Emit(opcode | AddressingModeField::encode(kMode_MRI), g.NoOutput(), ++ addr_reg, g.TempImmediate(0), g.UseRegisterOrImmediateZero(value)); ++ } ++ } ++} ++ ++void InstructionSelector::VisitProtectedStore(Node* node) { ++ // TODO(eholk) ++ UNIMPLEMENTED(); ++} ++ ++void InstructionSelector::VisitWord32And(Node* node) { ++ VisitBinop(this, node, kSw64Andw, true, kSw64Andw); ++} ++ ++void InstructionSelector::VisitWord64And(Node* node) { ++ VisitBinop(this, node, kSw64Andl, true, kSw64Andl); ++} ++ ++void InstructionSelector::VisitWord32Or(Node* node) { ++ VisitBinop(this, node, kSw64Orw, true, kSw64Orw); ++} ++ ++void InstructionSelector::VisitWord64Or(Node* node) { ++ VisitBinop(this, node, kSw64Orl, true, kSw64Orl); ++} ++ ++void InstructionSelector::VisitWord32Xor(Node* node) { ++ Int32BinopMatcher m(node); ++ if (m.left().IsWord32Or() && CanCover(node, m.left().node()) && ++ m.right().Is(-1)) { ++ Int32BinopMatcher mleft(m.left().node()); ++ if (!mleft.right().HasResolvedValue()) { ++ Sw64OperandGenerator g(this); ++ Emit(kSw64Norw, g.DefineAsRegister(node), ++ g.UseRegister(mleft.left().node()), ++ g.UseRegister(mleft.right().node())); ++ return; ++ } ++ } ++ if (m.right().Is(-1)) { ++ // Use Nor for bit negation and eliminate constant loading for xori. ++ Sw64OperandGenerator g(this); ++ Emit(kSw64Norw, g.DefineAsRegister(node), g.UseRegister(m.left().node()), ++ g.TempImmediate(0)); ++ return; ++ } ++ VisitBinop(this, node, kSw64Xorw, true, kSw64Xorw); ++} ++ ++void InstructionSelector::VisitWord64Xor(Node* node) { ++ Int64BinopMatcher m(node); ++ if (m.left().IsWord64Or() && CanCover(node, m.left().node()) && ++ m.right().Is(-1)) { ++ Int64BinopMatcher mleft(m.left().node()); ++ if (!mleft.right().HasResolvedValue()) { ++ Sw64OperandGenerator g(this); ++ Emit(kSw64Norl, g.DefineAsRegister(node), ++ g.UseRegister(mleft.left().node()), ++ g.UseRegister(mleft.right().node())); ++ return; ++ } ++ } ++ if (m.right().Is(-1)) { ++ // Use Nor for bit negation and eliminate constant loading for xori. ++ Sw64OperandGenerator g(this); ++ Emit(kSw64Norl, g.DefineAsRegister(node), g.UseRegister(m.left().node()), ++ g.TempImmediate(0)); ++ return; ++ } ++ VisitBinop(this, node, kSw64Xorl, true, kSw64Xorl); ++} ++ ++void InstructionSelector::VisitWord32Shl(Node* node) { ++ Int32BinopMatcher m(node); ++ if (m.left().IsWord32And() && CanCover(node, m.left().node()) && ++ m.right().IsInRange(1, 31)) { ++ Sw64OperandGenerator g(this); ++ Int32BinopMatcher mleft(m.left().node()); ++ // Match Word32Shl(Word32And(x, mask), imm) to Shl where the mask is ++ // contiguous, and the shift immediate non-zero. ++ if (mleft.right().HasResolvedValue()) { ++ uint32_t mask = mleft.right().ResolvedValue(); ++ uint32_t mask_width = base::bits::CountPopulation(mask); ++ uint32_t mask_msb = base::bits::CountLeadingZeros32(mask); ++ if ((mask_width != 0) && (mask_msb + mask_width == 32)) { ++ uint32_t shift = m.right().ResolvedValue(); ++ DCHECK_EQ(0u, base::bits::CountTrailingZeros32(mask)); ++ DCHECK_NE(0u, shift); ++ if ((shift + mask_width) >= 32) { ++ // If the mask is contiguous and reaches or extends beyond the top ++ // bit, only the shift is needed. ++ Emit(kSw64Sllw, g.DefineAsRegister(node), ++ g.UseRegister(mleft.left().node()), ++ g.UseImmediate(m.right().node())); ++ return; ++ } ++ } ++ } ++ } ++ VisitRRO(this, kSw64Sllw, node); ++} ++ ++void InstructionSelector::VisitWord32Shr(Node* node) { ++ Int32BinopMatcher m(node); ++ if (m.left().IsWord32And() && m.right().HasResolvedValue()) { ++ uint32_t lsb = m.right().ResolvedValue() & 0x1F; ++ Int32BinopMatcher mleft(m.left().node()); ++ if (mleft.right().HasResolvedValue() && ++ mleft.right().ResolvedValue() != 0) { ++ // Select Ext for Srlw(And(x, mask), imm) where the result of the mask is ++ // shifted into the least-significant bits. ++ uint32_t mask = (mleft.right().ResolvedValue() >> lsb) << lsb; ++ unsigned mask_width = base::bits::CountPopulation(mask); ++ unsigned mask_msb = base::bits::CountLeadingZeros32(mask); ++ if ((mask_msb + mask_width + lsb) == 32) { ++ Sw64OperandGenerator g(this); ++ DCHECK_EQ(lsb, base::bits::CountTrailingZeros32(mask)); ++ Emit(kSw64Extw, g.DefineAsRegister(node), ++ g.UseRegister(mleft.left().node()), g.TempImmediate(lsb), ++ g.TempImmediate(mask_width)); ++ return; ++ } ++ } ++ } ++ VisitRRO(this, kSw64Srlw, node); ++} ++ ++void InstructionSelector::VisitWord32Sar(Node* node) { ++ Int32BinopMatcher m(node); ++ if (m.left().IsWord32Shl() && CanCover(node, m.left().node())) { ++ Int32BinopMatcher mleft(m.left().node()); ++ if (m.right().HasResolvedValue() && mleft.right().HasResolvedValue()) { ++ Sw64OperandGenerator g(this); ++ uint32_t sraw = m.right().ResolvedValue(); ++ uint32_t sllw = mleft.right().ResolvedValue(); ++ if ((sraw == sllw) && (sraw == 16)) { ++ Emit(kSw64Sexth, g.DefineAsRegister(node), ++ g.UseRegister(mleft.left().node())); ++ return; ++ } else if ((sraw == sllw) && (sraw == 24)) { ++ Emit(kSw64Sextb, g.DefineAsRegister(node), ++ g.UseRegister(mleft.left().node())); ++ return; ++ } else if ((sraw == sllw) && (sraw == 32)) { ++ Emit(kSw64Sllw, g.DefineAsRegister(node), ++ g.UseRegister(mleft.left().node()), g.TempImmediate(0)); ++ return; ++ } ++ } ++ } ++ VisitRRO(this, kSw64Sraw, node); ++} ++ ++void InstructionSelector::VisitWord64Shl(Node* node) { ++ Sw64OperandGenerator g(this); ++ Int64BinopMatcher m(node); ++ if ((m.left().IsChangeInt32ToInt64() || m.left().IsChangeUint32ToUint64()) && ++ m.right().IsInRange(32, 63) && CanCover(node, m.left().node())) { ++ // There's no need to sign/zero-extend to 64-bit if we shift out the upper ++ // 32 bits anyway. ++ Emit(kSw64Slll, g.DefineAsRegister(node), ++ g.UseRegister(m.left().node()->InputAt(0)), ++ g.UseImmediate(m.right().node())); ++ return; ++ } ++ if (m.left().IsWord64And() && CanCover(node, m.left().node()) && ++ m.right().IsInRange(1, 63)) { ++ // Match Word64Shl(Word64And(x, mask), imm) to Slll where the mask is ++ // contiguous, and the shift immediate non-zero. ++ Int64BinopMatcher mleft(m.left().node()); ++ if (mleft.right().HasResolvedValue()) { ++ uint64_t mask = mleft.right().ResolvedValue(); ++ uint32_t mask_width = base::bits::CountPopulation(mask); ++ uint32_t mask_msb = base::bits::CountLeadingZeros64(mask); ++ if ((mask_width != 0) && (mask_msb + mask_width == 64)) { ++ uint64_t shift = m.right().ResolvedValue(); ++ DCHECK_EQ(0u, base::bits::CountTrailingZeros64(mask)); ++ DCHECK_NE(0u, shift); ++ ++ if ((shift + mask_width) >= 64) { ++ // If the mask is contiguous and reaches or extends beyond the top ++ // bit, only the shift is needed. ++ Emit(kSw64Slll, g.DefineAsRegister(node), ++ g.UseRegister(mleft.left().node()), ++ g.UseImmediate(m.right().node())); ++ return; ++ } ++ } ++ } ++ } ++ VisitRRO(this, kSw64Slll, node); ++} ++ ++void InstructionSelector::VisitWord64Shr(Node* node) { ++ Int64BinopMatcher m(node); ++ if (m.left().IsWord64And() && m.right().HasResolvedValue()) { ++ uint32_t lsb = m.right().ResolvedValue() & 0x3F; ++ Int64BinopMatcher mleft(m.left().node()); ++ if (mleft.right().HasResolvedValue() && ++ mleft.right().ResolvedValue() != 0) { ++ // Select Extl for Srlw(And(x, mask), imm) where the result of the mask is ++ // shifted into the least-significant bits. ++ uint64_t mask = (mleft.right().ResolvedValue() >> lsb) << lsb; ++ unsigned mask_width = base::bits::CountPopulation(mask); ++ unsigned mask_msb = base::bits::CountLeadingZeros64(mask); ++ if ((mask_msb + mask_width + lsb) == 64) { ++ Sw64OperandGenerator g(this); ++ DCHECK_EQ(lsb, base::bits::CountTrailingZeros64(mask)); ++ Emit(kSw64Extl, g.DefineAsRegister(node), ++ g.UseRegister(mleft.left().node()), g.TempImmediate(lsb), ++ g.TempImmediate(mask_width)); ++ return; ++ } ++ } ++ } ++ VisitRRO(this, kSw64Srll, node); ++} ++ ++void InstructionSelector::VisitWord64Sar(Node* node) { ++ if (TryEmitExtendingLoad(this, node, node)) return; ++ VisitRRO(this, kSw64Sral, node); ++} ++ ++void InstructionSelector::VisitWord32Rol(Node* node) { UNREACHABLE(); } ++ ++void InstructionSelector::VisitWord64Rol(Node* node) { UNREACHABLE(); } ++ ++void InstructionSelector::VisitWord32Ror(Node* node) { ++ VisitRRO(this, kSw64Rolw, node); ++} ++ ++void InstructionSelector::VisitWord32Clz(Node* node) { ++ VisitRR(this, kSw64Ctlzw, node); ++} ++ ++void InstructionSelector::VisitWord32ReverseBits(Node* node) { UNREACHABLE(); } ++ ++void InstructionSelector::VisitWord64ReverseBits(Node* node) { UNREACHABLE(); } ++ ++void InstructionSelector::VisitWord64ReverseBytes(Node* node) { ++ Sw64OperandGenerator g(this); ++ Emit(kSw64ByteSwap64, g.DefineAsRegister(node), ++ g.UseRegister(node->InputAt(0))); ++} ++ ++void InstructionSelector::VisitWord32ReverseBytes(Node* node) { ++ Sw64OperandGenerator g(this); ++ Emit(kSw64ByteSwap32, g.DefineAsRegister(node), ++ g.UseRegister(node->InputAt(0))); ++} ++ ++void InstructionSelector::VisitSimd128ReverseBytes(Node* node) { ++ UNREACHABLE(); ++} ++ ++void InstructionSelector::VisitWord32Ctz(Node* node) { ++ Sw64OperandGenerator g(this); ++ Emit(kSw64Cttzw, g.DefineAsRegister(node), g.UseRegister(node->InputAt(0))); ++} ++ ++void InstructionSelector::VisitWord64Ctz(Node* node) { ++ Sw64OperandGenerator g(this); ++ Emit(kSw64Cttzl, g.DefineAsRegister(node), g.UseRegister(node->InputAt(0))); ++} ++ ++void InstructionSelector::VisitWord32Popcnt(Node* node) { ++ Sw64OperandGenerator g(this); ++ Emit(kSw64Ctpopw, g.DefineAsRegister(node), g.UseRegister(node->InputAt(0))); ++} ++ ++void InstructionSelector::VisitWord64Popcnt(Node* node) { ++ Sw64OperandGenerator g(this); ++ Emit(kSw64Ctpopl, g.DefineAsRegister(node), g.UseRegister(node->InputAt(0))); ++} ++ ++void InstructionSelector::VisitWord64Ror(Node* node) { ++ VisitRRO(this, kSw64Roll, node); ++} ++ ++void InstructionSelector::VisitWord64Clz(Node* node) { ++ VisitRR(this, kSw64Ctlzl, node); ++} ++ ++void InstructionSelector::VisitInt32Add(Node* node) { ++ Sw64OperandGenerator g(this); ++ Int32BinopMatcher m(node); ++ ++ // Select Lsaw for (left + (left_of_right << imm)). ++ if (m.right().opcode() == IrOpcode::kWord32Shl && ++ CanCover(node, m.left().node()) && CanCover(node, m.right().node())) { ++ Int32BinopMatcher mright(m.right().node()); ++ if (mright.right().HasResolvedValue() && !m.left().HasResolvedValue()) { ++ int32_t shift_value = ++ static_cast(mright.right().ResolvedValue()); ++ Emit(kSw64Lsaw, g.DefineAsRegister(node), g.UseRegister(m.left().node()), ++ g.UseRegister(mright.left().node()), g.TempImmediate(shift_value)); ++ return; ++ } ++ } ++ ++ // Select Lsaw for ((left_of_left << imm) + right). ++ if (m.left().opcode() == IrOpcode::kWord32Shl && ++ CanCover(node, m.right().node()) && CanCover(node, m.left().node())) { ++ Int32BinopMatcher mleft(m.left().node()); ++ if (mleft.right().HasResolvedValue() && !m.right().HasResolvedValue()) { ++ int32_t shift_value = static_cast(mleft.right().ResolvedValue()); ++ Emit(kSw64Lsaw, g.DefineAsRegister(node), g.UseRegister(m.right().node()), ++ g.UseRegister(mleft.left().node()), g.TempImmediate(shift_value)); ++ return; ++ } ++ } ++ VisitBinop(this, node, kSw64Addw, true, kSw64Addw); ++} ++ ++void InstructionSelector::VisitInt64Add(Node* node) { ++ Sw64OperandGenerator g(this); ++ Int64BinopMatcher m(node); ++ ++ // Select Lsal for (left + (left_of_right << imm)). ++ if (m.right().opcode() == IrOpcode::kWord64Shl && ++ CanCover(node, m.left().node()) && CanCover(node, m.right().node())) { ++ Int64BinopMatcher mright(m.right().node()); ++ if (mright.right().HasResolvedValue() && !m.left().HasResolvedValue()) { ++ int32_t shift_value = ++ static_cast(mright.right().ResolvedValue()); ++ Emit(kSw64Lsal, g.DefineAsRegister(node), g.UseRegister(m.left().node()), ++ g.UseRegister(mright.left().node()), g.TempImmediate(shift_value)); ++ return; ++ } ++ } ++ ++ // Select Lsal for ((left_of_left << imm) + right). ++ if (m.left().opcode() == IrOpcode::kWord64Shl && ++ CanCover(node, m.right().node()) && CanCover(node, m.left().node())) { ++ Int64BinopMatcher mleft(m.left().node()); ++ if (mleft.right().HasResolvedValue() && !m.right().HasResolvedValue()) { ++ int32_t shift_value = static_cast(mleft.right().ResolvedValue()); ++ Emit(kSw64Lsal, g.DefineAsRegister(node), g.UseRegister(m.right().node()), ++ g.UseRegister(mleft.left().node()), g.TempImmediate(shift_value)); ++ return; ++ } ++ } ++ ++ VisitBinop(this, node, kSw64Addl, true, kSw64Addl); ++} ++ ++void InstructionSelector::VisitInt32Sub(Node* node) { ++ VisitBinop(this, node, kSw64Subw); ++} ++ ++void InstructionSelector::VisitInt64Sub(Node* node) { ++ VisitBinop(this, node, kSw64Subl); ++} ++ ++void InstructionSelector::VisitInt32Mul(Node* node) { ++ Sw64OperandGenerator g(this); ++ Int32BinopMatcher m(node); ++ if (m.right().HasResolvedValue() && m.right().ResolvedValue() > 0) { ++ uint32_t value = static_cast(m.right().ResolvedValue()); ++ if (base::bits::IsPowerOfTwo(value)) { ++ Emit(kSw64Sllw | AddressingModeField::encode(kMode_None), ++ g.DefineAsRegister(node), g.UseRegister(m.left().node()), ++ g.TempImmediate(base::bits::WhichPowerOfTwo(value))); ++ return; ++ } ++ if (base::bits::IsPowerOfTwo(value - 1)) { ++ Emit(kSw64Lsaw, g.DefineAsRegister(node), g.UseRegister(m.left().node()), ++ g.UseRegister(m.left().node()), ++ g.TempImmediate(base::bits::WhichPowerOfTwo(value - 1))); ++ return; ++ } ++ if (base::bits::IsPowerOfTwo(value + 1)) { ++ InstructionOperand temp = g.TempRegister(); ++ Emit(kSw64Sllw | AddressingModeField::encode(kMode_None), temp, ++ g.UseRegister(m.left().node()), ++ g.TempImmediate(base::bits::WhichPowerOfTwo(value + 1))); ++ Emit(kSw64Subw | AddressingModeField::encode(kMode_None), ++ g.DefineAsRegister(node), temp, g.UseRegister(m.left().node())); ++ return; ++ } ++ } ++ Node* left = node->InputAt(0); ++ Node* right = node->InputAt(1); ++ if (CanCover(node, left) && CanCover(node, right)) { ++ if (left->opcode() == IrOpcode::kWord64Sar && ++ right->opcode() == IrOpcode::kWord64Sar) { ++ Int64BinopMatcher leftInput(left), rightInput(right); ++ if (leftInput.right().Is(32) && rightInput.right().Is(32)) { ++ // Combine untagging shifts with Dmul high. ++ Emit(kSw64MullHigh, g.DefineSameAsFirst(node), ++ g.UseRegister(leftInput.left().node()), ++ g.UseRegister(rightInput.left().node())); ++ return; ++ } ++ } ++ } ++ VisitRRR(this, kSw64Mulw, node); ++} ++ ++void InstructionSelector::VisitInt32MulHigh(Node* node) { ++ VisitRRR(this, kSw64MulwHigh, node); ++} ++ ++void InstructionSelector::VisitUint32MulHigh(Node* node) { ++ VisitRRR(this, kSw64MulwHighU, node); ++} ++ ++void InstructionSelector::VisitInt64Mul(Node* node) { ++ Sw64OperandGenerator g(this); ++ Int64BinopMatcher m(node); ++ if (m.right().HasResolvedValue() && m.right().ResolvedValue() > 0) { ++ uint64_t value = static_cast(m.right().ResolvedValue()); ++ if (base::bits::IsPowerOfTwo(value)) { ++ Emit(kSw64Slll | AddressingModeField::encode(kMode_None), ++ g.DefineAsRegister(node), g.UseRegister(m.left().node()), ++ g.TempImmediate(base::bits::WhichPowerOfTwo(value))); ++ return; ++ } ++ if (base::bits::IsPowerOfTwo(value - 1) && value - 1 > 0) { ++ // Lsal macro will handle the shifting value out of bound cases. ++ Emit(kSw64Lsal, g.DefineAsRegister(node), g.UseRegister(m.left().node()), ++ g.UseRegister(m.left().node()), ++ g.TempImmediate(base::bits::WhichPowerOfTwo(value - 1))); ++ return; ++ } ++ if (base::bits::IsPowerOfTwo(value + 1)) { ++ InstructionOperand temp = g.TempRegister(); ++ Emit(kSw64Slll | AddressingModeField::encode(kMode_None), temp, ++ g.UseRegister(m.left().node()), ++ g.TempImmediate(base::bits::WhichPowerOfTwo(value + 1))); ++ Emit(kSw64Subl | AddressingModeField::encode(kMode_None), ++ g.DefineAsRegister(node), temp, g.UseRegister(m.left().node())); ++ return; ++ } ++ } ++ Emit(kSw64Mull, g.DefineAsRegister(node), g.UseRegister(m.left().node()), ++ g.UseRegister(m.right().node())); ++} ++ ++void InstructionSelector::VisitInt32Div(Node* node) { ++ Sw64OperandGenerator g(this); ++ Int32BinopMatcher m(node); ++ Node* left = node->InputAt(0); ++ Node* right = node->InputAt(1); ++ if (CanCover(node, left) && CanCover(node, right)) { ++ if (left->opcode() == IrOpcode::kWord64Sar && ++ right->opcode() == IrOpcode::kWord64Sar) { ++ Int64BinopMatcher rightInput(right), leftInput(left); ++ if (rightInput.right().Is(32) && leftInput.right().Is(32)) { ++ // Combine both shifted operands with Ddiv. ++ Emit(kSw64Divl, g.DefineSameAsFirst(node), ++ g.UseRegister(leftInput.left().node()), ++ g.UseRegister(rightInput.left().node())); ++ return; ++ } ++ } ++ } ++ Emit(kSw64Divw, g.DefineSameAsFirst(node), g.UseRegister(m.left().node()), ++ g.UseRegister(m.right().node())); ++} ++ ++void InstructionSelector::VisitUint32Div(Node* node) { ++ Sw64OperandGenerator g(this); ++ Int32BinopMatcher m(node); ++ Emit(kSw64Divwu, g.DefineSameAsFirst(node), g.UseRegister(m.left().node()), ++ g.UseRegister(m.right().node())); ++} ++ ++void InstructionSelector::VisitInt32Mod(Node* node) { ++ Sw64OperandGenerator g(this); ++ Int32BinopMatcher m(node); ++ Node* left = node->InputAt(0); ++ Node* right = node->InputAt(1); ++ if (CanCover(node, left) && CanCover(node, right)) { ++ if (left->opcode() == IrOpcode::kWord64Sar && ++ right->opcode() == IrOpcode::kWord64Sar) { ++ Int64BinopMatcher rightInput(right), leftInput(left); ++ if (rightInput.right().Is(32) && leftInput.right().Is(32)) { ++ // Combine both shifted operands with Dmod. ++ Emit(kSw64Modl, g.DefineSameAsFirst(node), ++ g.UseRegister(leftInput.left().node()), ++ g.UseRegister(rightInput.left().node())); ++ return; ++ } ++ } ++ } ++ Emit(kSw64Modw, g.DefineAsRegister(node), g.UseRegister(m.left().node()), ++ g.UseRegister(m.right().node())); ++} ++ ++void InstructionSelector::VisitUint32Mod(Node* node) { ++ Sw64OperandGenerator g(this); ++ Int32BinopMatcher m(node); ++ Emit(kSw64Modwu, g.DefineAsRegister(node), g.UseRegister(m.left().node()), ++ g.UseRegister(m.right().node())); ++} ++ ++void InstructionSelector::VisitInt64Div(Node* node) { ++ Sw64OperandGenerator g(this); ++ Int64BinopMatcher m(node); ++ Emit(kSw64Divl, g.DefineSameAsFirst(node), g.UseRegister(m.left().node()), ++ g.UseRegister(m.right().node())); ++} ++ ++void InstructionSelector::VisitUint64Div(Node* node) { ++ Sw64OperandGenerator g(this); ++ Int64BinopMatcher m(node); ++ Emit(kSw64Divlu, g.DefineSameAsFirst(node), g.UseRegister(m.left().node()), ++ g.UseRegister(m.right().node())); ++} ++ ++void InstructionSelector::VisitInt64Mod(Node* node) { ++ Sw64OperandGenerator g(this); ++ Int64BinopMatcher m(node); ++ Emit(kSw64Modl, g.DefineAsRegister(node), g.UseRegister(m.left().node()), ++ g.UseRegister(m.right().node())); ++} ++ ++void InstructionSelector::VisitUint64Mod(Node* node) { ++ Sw64OperandGenerator g(this); ++ Int64BinopMatcher m(node); ++ Emit(kSw64Modlu, g.DefineAsRegister(node), g.UseRegister(m.left().node()), ++ g.UseRegister(m.right().node())); ++} ++ ++void InstructionSelector::VisitChangeFloat32ToFloat64(Node* node) { ++ VisitRR(this, kSw64F32ToF64, node); ++} ++ ++void InstructionSelector::VisitRoundInt32ToFloat32(Node* node) { ++ VisitRR(this, kSw64I32ToF32, node); ++} ++ ++void InstructionSelector::VisitRoundUint32ToFloat32(Node* node) { ++ VisitRR(this, kSw64UI32ToF32, node); ++} ++ ++void InstructionSelector::VisitChangeInt32ToFloat64(Node* node) { ++ VisitRR(this, kSw64I32ToF64, node); ++} ++ ++void InstructionSelector::VisitChangeInt64ToFloat64(Node* node) { ++ VisitRR(this, kSw64I64ToF64, node); ++} ++ ++void InstructionSelector::VisitChangeUint32ToFloat64(Node* node) { ++ VisitRR(this, kSw64UI32ToF64, node); ++} ++ ++void InstructionSelector::VisitTruncateFloat32ToInt32(Node* node) { ++ Sw64OperandGenerator g(this); ++ InstructionCode opcode = kSw64F32ToI32; ++ TruncateKind kind = OpParameter(node->op()); ++ if (kind == TruncateKind::kSetOverflowToMin) { ++ opcode |= MiscField::encode(true); ++ } ++ Emit(opcode, g.DefineAsRegister(node), g.UseRegister(node->InputAt(0))); ++} ++ ++void InstructionSelector::VisitTruncateFloat32ToUint32(Node* node) { ++ Sw64OperandGenerator g(this); ++ InstructionCode opcode = kSw64F32ToUI32; ++ TruncateKind kind = OpParameter(node->op()); ++ if (kind == TruncateKind::kSetOverflowToMin) { ++ opcode |= MiscField::encode(true); ++ } ++ Emit(opcode, g.DefineAsRegister(node), g.UseRegister(node->InputAt(0))); ++} ++ ++void InstructionSelector::VisitChangeFloat64ToInt32(Node* node) { ++ Sw64OperandGenerator g(this); ++ Node* value = node->InputAt(0); ++ // Match ChangeFloat64ToInt32(Float64Round##OP) to corresponding instruction ++ // which does rounding and conversion to integer format. ++ if (CanCover(node, value)) { ++ switch (value->opcode()) { ++ case IrOpcode::kFloat64RoundDown: ++ Emit(kSw64F64ToI32_n, g.DefineAsRegister(node), ++ g.UseRegister(value->InputAt(0))); ++ return; ++ case IrOpcode::kFloat64RoundUp: ++ Emit(kSw64F64ToI32_p, g.DefineAsRegister(node), ++ g.UseRegister(value->InputAt(0))); ++ return; ++ case IrOpcode::kFloat64RoundTiesEven: ++ Emit(kSw64F64ToI32_g, g.DefineAsRegister(node), ++ g.UseRegister(value->InputAt(0))); ++ return; ++ case IrOpcode::kFloat64RoundTruncate: ++ Emit(kSw64F64ToI32, g.DefineAsRegister(node), ++ g.UseRegister(value->InputAt(0))); ++ return; ++ default: ++ break; ++ } ++ if (value->opcode() == IrOpcode::kChangeFloat32ToFloat64) { ++ Node* next = value->InputAt(0); ++ if (CanCover(value, next)) { ++ // Match ChangeFloat64ToInt32(ChangeFloat32ToFloat64(Float64Round##OP)) ++ switch (next->opcode()) { ++ case IrOpcode::kFloat32RoundDown: ++ Emit(kSw64F32ToI32_n, g.DefineAsRegister(node), ++ g.UseRegister(next->InputAt(0))); ++ return; ++ case IrOpcode::kFloat32RoundUp: ++ Emit(kSw64F32ToI32_p, g.DefineAsRegister(node), ++ g.UseRegister(next->InputAt(0))); ++ return; ++ case IrOpcode::kFloat32RoundTiesEven: ++ Emit(kSw64F32ToI32_g, g.DefineAsRegister(node), ++ g.UseRegister(next->InputAt(0))); ++ return; ++ case IrOpcode::kFloat32RoundTruncate: ++ Emit(kSw64F32ToI32, g.DefineAsRegister(node), ++ g.UseRegister(next->InputAt(0))); ++ return; ++ default: ++ Emit(kSw64F32ToI32, g.DefineAsRegister(node), ++ g.UseRegister(value->InputAt(0))); ++ return; ++ } ++ } else { ++ // Match float32 -> float64 -> int32 representation change path. ++ Emit(kSw64F32ToI32, g.DefineAsRegister(node), ++ g.UseRegister(value->InputAt(0))); ++ return; ++ } ++ } ++ } ++ VisitRR(this, kSw64F64ToI32, node); ++} ++ ++void InstructionSelector::VisitChangeFloat64ToInt64(Node* node) { ++ VisitRR(this, kSw64F64ToI64, node); ++} ++ ++void InstructionSelector::VisitChangeFloat64ToUint32(Node* node) { ++ VisitRR(this, kSw64F64ToUI32, node); ++} ++ ++void InstructionSelector::VisitChangeFloat64ToUint64(Node* node) { ++ VisitRR(this, kSw64F64ToUI64, node); ++} ++ ++void InstructionSelector::VisitTruncateFloat64ToUint32(Node* node) { ++ VisitRR(this, kSw64F64ToUI32, node); ++} ++ ++void InstructionSelector::VisitTruncateFloat64ToInt64(Node* node) { ++ Sw64OperandGenerator g(this); ++ InstructionCode opcode = kSw64F64ToI64; ++ TruncateKind kind = OpParameter(node->op()); ++ if (kind == TruncateKind::kSetOverflowToMin) { ++ opcode |= MiscField::encode(true); ++ } ++ Emit(opcode, g.DefineAsRegister(node), g.UseRegister(node->InputAt(0))); ++} ++ ++void InstructionSelector::VisitTryTruncateFloat32ToInt64(Node* node) { ++ Sw64OperandGenerator g(this); ++ InstructionOperand inputs[] = {g.UseRegister(node->InputAt(0))}; ++ InstructionOperand outputs[2]; ++ size_t output_count = 0; ++ outputs[output_count++] = g.DefineAsRegister(node); ++ ++ Node* success_output = NodeProperties::FindProjection(node, 1); ++ if (success_output) { ++ outputs[output_count++] = g.DefineAsRegister(success_output); ++ } ++ ++ this->Emit(kSw64F32ToI64, output_count, outputs, 1, inputs); ++} ++ ++void InstructionSelector::VisitTryTruncateFloat64ToInt64(Node* node) { ++ Sw64OperandGenerator g(this); ++ InstructionOperand inputs[] = {g.UseRegister(node->InputAt(0))}; ++ InstructionOperand outputs[2]; ++ size_t output_count = 0; ++ outputs[output_count++] = g.DefineAsRegister(node); ++ ++ Node* success_output = NodeProperties::FindProjection(node, 1); ++ if (success_output) { ++ outputs[output_count++] = g.DefineAsRegister(success_output); ++ } ++ ++ Emit(kSw64F64ToI64, output_count, outputs, 1, inputs); ++} ++ ++void InstructionSelector::VisitTryTruncateFloat32ToUint64(Node* node) { ++ Sw64OperandGenerator g(this); ++ InstructionOperand inputs[] = {g.UseRegister(node->InputAt(0))}; ++ InstructionOperand outputs[2]; ++ size_t output_count = 0; ++ outputs[output_count++] = g.DefineAsRegister(node); ++ ++ Node* success_output = NodeProperties::FindProjection(node, 1); ++ if (success_output) { ++ outputs[output_count++] = g.DefineAsRegister(success_output); ++ } ++ ++ Emit(kSw64F32ToUI64, output_count, outputs, 1, inputs); ++} ++ ++void InstructionSelector::VisitTryTruncateFloat64ToUint64(Node* node) { ++ Sw64OperandGenerator g(this); ++ ++ InstructionOperand inputs[] = {g.UseRegister(node->InputAt(0))}; ++ InstructionOperand outputs[2]; ++ size_t output_count = 0; ++ outputs[output_count++] = g.DefineAsRegister(node); ++ ++ Node* success_output = NodeProperties::FindProjection(node, 1); ++ if (success_output) { ++ outputs[output_count++] = g.DefineAsRegister(success_output); ++ } ++ ++ Emit(kSw64F64ToUI64, output_count, outputs, 1, inputs); ++} ++ ++void InstructionSelector::VisitBitcastWord32ToWord64(Node* node) { ++ UNIMPLEMENTED(); ++} ++ ++void InstructionSelector::VisitChangeInt32ToInt64(Node* node) { ++ // On SW64, int32 values should all be sign-extended to 64-bit, so ++ // no need to sign-extend them here. ++ // But when call to a host function in simulator, if the function return an ++ // int32 value, the simulator do not sign-extend to int64, because in ++ // simulator we do not know the function whether return an int32 or int64. ++#ifdef USE_SIMULATOR ++ Node* value = node->InputAt(0); ++ if (value->opcode() == IrOpcode::kCall) { ++ Sw64OperandGenerator g(this); ++ Emit(kSw64Shl, g.DefineAsRegister(node), g.UseRegister(value), ++ g.TempImmediate(0)); ++ return; ++ } ++#endif ++ EmitIdentity(node); ++} ++ ++bool InstructionSelector::ZeroExtendsWord32ToWord64NoPhis(Node* node) { ++ DCHECK_NE(node->opcode(), IrOpcode::kPhi); ++ switch (node->opcode()) { ++ // Comparisons only emit 0/1, so the upper 32 bits must be zero. ++ case IrOpcode::kWord32Equal: ++ case IrOpcode::kInt32LessThan: ++ case IrOpcode::kInt32LessThanOrEqual: ++ case IrOpcode::kUint32LessThan: ++ case IrOpcode::kUint32LessThanOrEqual: ++ return true; ++ case IrOpcode::kWord32And: { ++ Int32BinopMatcher m(node); ++ if (m.right().HasResolvedValue()) { ++ uint32_t mask = m.right().ResolvedValue(); ++ return is_uint31(mask); ++ } ++ return false; ++ } ++ case IrOpcode::kWord32Shr: { ++ Int32BinopMatcher m(node); ++ if (m.right().HasResolvedValue()) { ++ uint8_t sa = m.right().ResolvedValue() & 0x1f; ++ return sa > 0; ++ } ++ return false; ++ } ++ case IrOpcode::kLoad: ++ case IrOpcode::kLoadImmutable: { ++ LoadRepresentation load_rep = LoadRepresentationOf(node->op()); ++ if (load_rep.IsUnsigned()) { ++ switch (load_rep.representation()) { ++ case MachineRepresentation::kWord8: ++ case MachineRepresentation::kWord16: ++ return true; ++ default: ++ return false; ++ } ++ } ++ return false; ++ } ++ default: ++ return false; ++ } ++} ++ ++void InstructionSelector::VisitChangeUint32ToUint64(Node* node) { ++ Sw64OperandGenerator g(this); ++ Node* value = node->InputAt(0); ++ IrOpcode::Value opcode = value->opcode(); ++ ++ if (opcode == IrOpcode::kLoad || opcode == IrOpcode::kUnalignedLoad) { ++ LoadRepresentation load_rep = LoadRepresentationOf(value->op()); ++ ArchOpcode arch_opcode = ++ opcode == IrOpcode::kUnalignedLoad ? kSw64Ldwu_u : kSw64Ldwu; ++ if (load_rep.IsUnsigned() && ++ load_rep.representation() == MachineRepresentation::kWord32) { ++ EmitLoad(this, value, arch_opcode, node); ++ return; ++ } ++ } ++ ++ if (ZeroExtendsWord32ToWord64(value)) { ++ EmitIdentity(node); ++ return; ++ } ++ ++ Emit(kSw64Extl, g.DefineAsRegister(node), g.UseRegister(node->InputAt(0)), ++ g.TempImmediate(0), g.TempImmediate(32)); ++} ++ ++void InstructionSelector::VisitTruncateInt64ToInt32(Node* node) { ++ Sw64OperandGenerator g(this); ++ Node* value = node->InputAt(0); ++ if (CanCover(node, value)) { ++ switch (value->opcode()) { ++ case IrOpcode::kWord64Sar: { ++ if (CanCover(value, value->InputAt(0)) && ++ TryEmitExtendingLoad(this, value, node)) { ++ return; ++ } else { ++ Int64BinopMatcher m(value); ++ if (m.right().IsInRange(32, 63)) { ++ // After smi untagging no need for truncate. Combine sequence. ++ Emit(kSw64Sral, g.DefineAsRegister(node), ++ g.UseRegister(m.left().node()), ++ g.UseImmediate(m.right().node())); ++ return; ++ } ++ } ++ break; ++ } ++ default: ++ break; ++ } ++ } ++ Emit(kSw64Sllw, g.DefineAsRegister(node), g.UseRegister(node->InputAt(0)), ++ g.TempImmediate(0)); ++} ++ ++void InstructionSelector::VisitTruncateFloat64ToFloat32(Node* node) { ++ Sw64OperandGenerator g(this); ++ Node* value = node->InputAt(0); ++ // Match TruncateFloat64ToFloat32(ChangeInt32ToFloat64) to corresponding ++ // instruction. ++ if (CanCover(node, value) && ++ value->opcode() == IrOpcode::kChangeInt32ToFloat64) { ++ Emit(kSw64I32ToF32, g.DefineAsRegister(node), ++ g.UseRegister(value->InputAt(0))); ++ return; ++ } ++ VisitRR(this, kSw64F64ToF32, node); ++} ++ ++void InstructionSelector::VisitTruncateFloat64ToWord32(Node* node) { ++ VisitRR(this, kArchTruncateDoubleToI, node); ++} ++ ++void InstructionSelector::VisitRoundFloat64ToInt32(Node* node) { ++ VisitRR(this, kSw64F64ToI32, node); ++} ++ ++void InstructionSelector::VisitRoundInt64ToFloat32(Node* node) { ++ VisitRR(this, kSw64I64ToF32, node); ++} ++ ++void InstructionSelector::VisitRoundInt64ToFloat64(Node* node) { ++ VisitRR(this, kSw64I64ToF64, node); ++} ++ ++void InstructionSelector::VisitRoundUint64ToFloat32(Node* node) { ++ VisitRR(this, kSw64UI64ToF32, node); ++} ++ ++void InstructionSelector::VisitRoundUint64ToFloat64(Node* node) { ++ VisitRR(this, kSw64UI64ToF64, node); ++} ++ ++void InstructionSelector::VisitBitcastFloat32ToInt32(Node* node) { ++ VisitRR(this, kSw64BitcastSW, node); ++} ++ ++void InstructionSelector::VisitBitcastFloat64ToInt64(Node* node) { ++ VisitRR(this, kSw64BitcastDL, node); ++} ++ ++void InstructionSelector::VisitBitcastInt32ToFloat32(Node* node) { ++ VisitRR(this, kSw64BitcastWS, node); ++} ++ ++void InstructionSelector::VisitBitcastInt64ToFloat64(Node* node) { ++ VisitRR(this, kSw64BitcastLD, node); ++} ++ ++void InstructionSelector::VisitFloat32Add(Node* node) { ++ // Optimization with Madd.S(z, x, y) is intentionally removed. ++ // See explanation for madd_s in assembler-sw64.cc. ++ VisitRRR(this, kSw64Fadds, node); ++} ++ ++void InstructionSelector::VisitFloat64Add(Node* node) { ++ // Optimization with Madd.D(z, x, y) is intentionally removed. ++ // See explanation for madd_d in assembler-sw64.cc. ++ VisitRRR(this, kSw64Faddd, node); ++} ++ ++void InstructionSelector::VisitFloat32Sub(Node* node) { ++ // Optimization with Msub.S(z, x, y) is intentionally removed. ++ // See explanation for madd_s in assembler-sw64.cc. ++ VisitRRR(this, kSw64Fsubs, node); ++} ++ ++void InstructionSelector::VisitFloat64Sub(Node* node) { ++ // Optimization with Msub.D(z, x, y) is intentionally removed. ++ // See explanation for madd_d in assembler-sw64.cc. ++ VisitRRR(this, kSw64Fsubd, node); ++} ++ ++void InstructionSelector::VisitFloat32Mul(Node* node) { ++ VisitRRR(this, kSw64Fmuls, node); ++} ++ ++void InstructionSelector::VisitFloat64Mul(Node* node) { ++ VisitRRR(this, kSw64Fmuld, node); ++} ++ ++void InstructionSelector::VisitFloat32Div(Node* node) { ++ VisitRRR(this, kSw64Fdivs, node); ++} ++ ++void InstructionSelector::VisitFloat64Div(Node* node) { ++ VisitRRR(this, kSw64Fdivd, node); ++} ++ ++void InstructionSelector::VisitFloat64Mod(Node* node) { ++ Sw64OperandGenerator g(this); ++ Emit(kSw64Fmodd, g.DefineAsFixed(node, f0), g.UseFixed(node->InputAt(0), f16), ++ g.UseFixed(node->InputAt(1), f17)) ++ ->MarkAsCall(); ++} ++ ++void InstructionSelector::VisitFloat32Max(Node* node) { ++ Sw64OperandGenerator g(this); ++ Emit(kSw64Float32Max, g.DefineAsRegister(node), ++ g.UseRegister(node->InputAt(0)), g.UseRegister(node->InputAt(1))); ++} ++ ++void InstructionSelector::VisitFloat64Max(Node* node) { ++ Sw64OperandGenerator g(this); ++ Emit(kSw64Float64Max, g.DefineAsRegister(node), ++ g.UseRegister(node->InputAt(0)), g.UseRegister(node->InputAt(1))); ++} ++ ++void InstructionSelector::VisitFloat32Min(Node* node) { ++ Sw64OperandGenerator g(this); ++ Emit(kSw64Float32Min, g.DefineAsRegister(node), ++ g.UseRegister(node->InputAt(0)), g.UseRegister(node->InputAt(1))); ++} ++ ++void InstructionSelector::VisitFloat64Min(Node* node) { ++ Sw64OperandGenerator g(this); ++ Emit(kSw64Float64Min, g.DefineAsRegister(node), ++ g.UseRegister(node->InputAt(0)), g.UseRegister(node->InputAt(1))); ++} ++ ++void InstructionSelector::VisitFloat32Abs(Node* node) { ++ VisitRR(this, kSw64Fabss, node); ++} ++ ++void InstructionSelector::VisitFloat64Abs(Node* node) { ++ VisitRR(this, kSw64Fabsd, node); ++} ++ ++void InstructionSelector::VisitFloat32Sqrt(Node* node) { ++ VisitRR(this, kSw64Fsqrts, node); ++} ++ ++void InstructionSelector::VisitFloat64Sqrt(Node* node) { ++ VisitRR(this, kSw64Fsqrtd, node); ++} ++ ++void InstructionSelector::VisitFloat32RoundDown(Node* node) { ++ VisitRR(this, kSw64Float32RoundDown, node); ++} ++ ++void InstructionSelector::VisitFloat64RoundDown(Node* node) { ++ VisitRR(this, kSw64Float64RoundDown, node); ++} ++ ++void InstructionSelector::VisitFloat32RoundUp(Node* node) { ++ VisitRR(this, kSw64Float32RoundUp, node); ++} ++ ++void InstructionSelector::VisitFloat64RoundUp(Node* node) { ++ VisitRR(this, kSw64Float64RoundUp, node); ++} ++ ++void InstructionSelector::VisitFloat32RoundTruncate(Node* node) { ++ VisitRR(this, kSw64Float32RoundTruncate, node); ++} ++ ++void InstructionSelector::VisitFloat64RoundTruncate(Node* node) { ++ VisitRR(this, kSw64Float64RoundTruncate, node); ++} ++ ++void InstructionSelector::VisitFloat64RoundTiesAway(Node* node) { ++ UNREACHABLE(); ++} ++ ++void InstructionSelector::VisitFloat32RoundTiesEven(Node* node) { ++ VisitRR(this, kSw64Float32RoundTiesEven, node); ++} ++ ++void InstructionSelector::VisitFloat64RoundTiesEven(Node* node) { ++ VisitRR(this, kSw64Float64RoundTiesEven, node); ++} ++ ++void InstructionSelector::VisitFloat32Neg(Node* node) { ++ VisitRR(this, kSw64Fnegs, node); ++} ++ ++void InstructionSelector::VisitFloat64Neg(Node* node) { ++ VisitRR(this, kSw64Fnegd, node); ++} ++ ++void InstructionSelector::VisitFloat64Ieee754Binop(Node* node, ++ InstructionCode opcode) { ++ Sw64OperandGenerator g(this); ++ Emit(opcode, g.DefineAsFixed(node, f0), g.UseFixed(node->InputAt(0), f2), ++ g.UseFixed(node->InputAt(1), f4)) ++ ->MarkAsCall(); ++} ++ ++void InstructionSelector::VisitFloat64Ieee754Unop(Node* node, ++ InstructionCode opcode) { ++ Sw64OperandGenerator g(this); ++ Emit(opcode, g.DefineAsFixed(node, f0), g.UseFixed(node->InputAt(0), f16)) ++ ->MarkAsCall(); ++} ++ ++void InstructionSelector::EmitPrepareArguments( ++ ZoneVector* arguments, const CallDescriptor* call_descriptor, ++ Node* node) { ++ Sw64OperandGenerator g(this); ++ ++ // Prepare for C function call. ++ if (call_descriptor->IsCFunctionCall()) { ++ Emit(kArchPrepareCallCFunction | MiscField::encode(static_cast( ++ call_descriptor->ParameterCount())), ++ 0, nullptr, 0, nullptr); ++ ++ // Poke any stack arguments. ++ int slot = kCArgSlotCount; ++ for (PushParameter input : (*arguments)) { ++ Emit(kSw64Poke, g.NoOutput(), g.UseRegister(input.node), ++ g.TempImmediate(slot << kSystemPointerSizeLog2)); ++ ++slot; ++ } ++ } else { ++ int push_count = static_cast(call_descriptor->ParameterSlotCount()); ++ if (push_count > 0) { ++ // Calculate needed space ++ int stack_size = 0; ++ for (PushParameter input : (*arguments)) { ++ if (input.node) { ++ stack_size += input.location.GetSizeInPointers(); ++ } ++ } ++ Emit(kSw64StackClaim, g.NoOutput(), ++ g.TempImmediate(stack_size << kSystemPointerSizeLog2)); ++ } ++ for (size_t n = 0; n < arguments->size(); ++n) { ++ PushParameter input = (*arguments)[n]; ++ if (input.node) { ++ Emit(kSw64Poke, g.NoOutput(), g.UseRegister(input.node), ++ g.TempImmediate(static_cast(n << kSystemPointerSizeLog2))); ++ } ++ } ++ } ++} ++ ++void InstructionSelector::EmitPrepareResults( ++ ZoneVector* results, const CallDescriptor* call_descriptor, ++ Node* node) { ++ Sw64OperandGenerator g(this); ++ ++ for (PushParameter output : *results) { ++ if (!output.location.IsCallerFrameSlot()) continue; ++ // Skip any alignment holes in nodes. ++ if (output.node != nullptr) { ++ DCHECK(!call_descriptor->IsCFunctionCall()); ++ if (output.location.GetType() == MachineType::Float32()) { ++ MarkAsFloat32(output.node); ++ } else if (output.location.GetType() == MachineType::Float64()) { ++ MarkAsFloat64(output.node); ++ } ++ int offset = call_descriptor->GetOffsetToReturns(); ++ int reverse_slot = -output.location.GetLocation() - offset; ++ Emit(kSw64Peek, g.DefineAsRegister(output.node), ++ g.UseImmediate(reverse_slot)); ++ } ++ } ++} ++ ++bool InstructionSelector::IsTailCallAddressImmediate() { return false; } ++ ++void InstructionSelector::VisitUnalignedLoad(Node* node) { ++ LoadRepresentation load_rep = LoadRepresentationOf(node->op()); ++ Sw64OperandGenerator g(this); ++ Node* base = node->InputAt(0); ++ Node* index = node->InputAt(1); ++ ++ ArchOpcode opcode; ++ switch (load_rep.representation()) { ++ case MachineRepresentation::kFloat32: ++ opcode = kSw64Flds_u; ++ break; ++ case MachineRepresentation::kFloat64: ++ opcode = kSw64Fldd_u; ++ break; ++ case MachineRepresentation::kWord8: ++ opcode = load_rep.IsUnsigned() ? kSw64Ldbu : kSw64Ldb; ++ break; ++ case MachineRepresentation::kWord16: ++ opcode = load_rep.IsUnsigned() ? kSw64Ldhu_u : kSw64Ldh_u; ++ break; ++ case MachineRepresentation::kWord32: ++ opcode = kSw64Ldw_u; ++ break; ++ case MachineRepresentation::kTaggedSigned: // Fall through. ++ case MachineRepresentation::kTaggedPointer: // Fall through. ++ case MachineRepresentation::kTagged: // Fall through. ++ case MachineRepresentation::kWord64: ++ opcode = kSw64Ldl_u; ++ break; ++ case MachineRepresentation::kSimd128: ++ opcode = kSw64SwsaLd; ++ break; ++ case MachineRepresentation::kBit: // Fall through. ++ case MachineRepresentation::kCompressedPointer: // Fall through. ++ case MachineRepresentation::kCompressed: // Fall through. ++ case MachineRepresentation::kSandboxedPointer: // Fall through. ++ case MachineRepresentation::kMapWord: // Fall through. ++ case MachineRepresentation::kNone: ++ UNREACHABLE(); ++ } ++ ++ if (g.CanBeImmediate(index, opcode)) { ++ Emit(opcode | AddressingModeField::encode(kMode_MRI), ++ g.DefineAsRegister(node), g.UseRegister(base), g.UseImmediate(index)); ++ } else { ++ InstructionOperand addr_reg = g.TempRegister(); ++ Emit(kSw64Addl | AddressingModeField::encode(kMode_None), addr_reg, ++ g.UseRegister(index), g.UseRegister(base)); ++ // Emit desired load opcode, using temp addr_reg. ++ Emit(opcode | AddressingModeField::encode(kMode_MRI), ++ g.DefineAsRegister(node), addr_reg, g.TempImmediate(0)); ++ } ++} ++ ++void InstructionSelector::VisitUnalignedStore(Node* node) { ++ Sw64OperandGenerator g(this); ++ Node* base = node->InputAt(0); ++ Node* index = node->InputAt(1); ++ Node* value = node->InputAt(2); ++ ++ UnalignedStoreRepresentation rep = UnalignedStoreRepresentationOf(node->op()); ++ ArchOpcode opcode; ++ switch (rep) { ++ case MachineRepresentation::kFloat32: ++ opcode = kSw64Fsts_u; ++ break; ++ case MachineRepresentation::kFloat64: ++ opcode = kSw64Fstd_u; ++ break; ++ case MachineRepresentation::kWord8: ++ opcode = kSw64Stb; ++ break; ++ case MachineRepresentation::kWord16: ++ opcode = kSw64Sth_u; ++ break; ++ case MachineRepresentation::kWord32: ++ opcode = kSw64Stw_u; ++ break; ++ case MachineRepresentation::kTaggedSigned: // Fall through. ++ case MachineRepresentation::kTaggedPointer: // Fall through. ++ case MachineRepresentation::kTagged: // Fall through. ++ case MachineRepresentation::kWord64: ++ opcode = kSw64Stl_u; ++ break; ++ case MachineRepresentation::kSimd128: ++ opcode = kSw64SwsaSt; ++ break; ++ case MachineRepresentation::kBit: // Fall through. ++ case MachineRepresentation::kCompressedPointer: // Fall through. ++ case MachineRepresentation::kCompressed: // Fall through. ++ case MachineRepresentation::kSandboxedPointer: // Fall through. ++ case MachineRepresentation::kMapWord: // Fall through. ++ case MachineRepresentation::kNone: ++ UNREACHABLE(); ++ } ++ ++ if (g.CanBeImmediate(index, opcode)) { ++ Emit(opcode | AddressingModeField::encode(kMode_MRI), g.NoOutput(), ++ g.UseRegister(base), g.UseImmediate(index), ++ g.UseRegisterOrImmediateZero(value)); ++ } else { ++ InstructionOperand addr_reg = g.TempRegister(); ++ Emit(kSw64Addl | AddressingModeField::encode(kMode_None), addr_reg, ++ g.UseRegister(index), g.UseRegister(base)); ++ // Emit desired store opcode, using temp addr_reg. ++ Emit(opcode | AddressingModeField::encode(kMode_MRI), g.NoOutput(), ++ addr_reg, g.TempImmediate(0), g.UseRegisterOrImmediateZero(value)); ++ } ++} ++ ++namespace { ++ ++// Shared routine for multiple compare operations. ++static void VisitCompare(InstructionSelector* selector, InstructionCode opcode, ++ InstructionOperand left, InstructionOperand right, ++ FlagsContinuation* cont) { ++ selector->EmitWithContinuation(opcode, left, right, cont); ++} ++ ++// Shared routine for multiple float32 compare operations. ++void VisitFloat32Compare(InstructionSelector* selector, Node* node, ++ FlagsContinuation* cont) { ++ Sw64OperandGenerator g(selector); ++ Float32BinopMatcher m(node); ++ InstructionOperand lhs, rhs; ++ ++ lhs = m.left().IsZero() ? g.UseImmediate(m.left().node()) ++ : g.UseRegister(m.left().node()); ++ rhs = m.right().IsZero() ? g.UseImmediate(m.right().node()) ++ : g.UseRegister(m.right().node()); ++ VisitCompare(selector, kSw64Fcmps, lhs, rhs, cont); ++} ++ ++// Shared routine for multiple float64 compare operations. ++void VisitFloat64Compare(InstructionSelector* selector, Node* node, ++ FlagsContinuation* cont) { ++ Sw64OperandGenerator g(selector); ++ Float64BinopMatcher m(node); ++ InstructionOperand lhs, rhs; ++ ++ lhs = m.left().IsZero() ? g.UseImmediate(m.left().node()) ++ : g.UseRegister(m.left().node()); ++ rhs = m.right().IsZero() ? g.UseImmediate(m.right().node()) ++ : g.UseRegister(m.right().node()); ++ VisitCompare(selector, kSw64Fcmpd, lhs, rhs, cont); ++} ++ ++// Shared routine for multiple word compare operations. ++void VisitWordCompare(InstructionSelector* selector, Node* node, ++ InstructionCode opcode, FlagsContinuation* cont, ++ bool commutative) { ++ Sw64OperandGenerator g(selector); ++ Node* left = node->InputAt(0); ++ Node* right = node->InputAt(1); ++ ++ // Match immediates on left or right side of comparison. ++ if (g.CanBeImmediate(right, opcode)) { ++ if (opcode == kSw64Tst) { ++ VisitCompare(selector, opcode, g.UseRegister(left), g.UseImmediate(right), ++ cont); ++ } else { ++ switch (cont->condition()) { ++ case kEqual: ++ case kNotEqual: ++ if (cont->IsSet()) { ++ VisitCompare(selector, opcode, g.UseRegister(left), ++ g.UseImmediate(right), cont); ++ } else { ++ VisitCompare(selector, opcode, g.UseRegister(left), ++ g.UseRegister(right), cont); ++ } ++ break; ++ case kSignedLessThan: ++ case kSignedGreaterThanOrEqual: ++ case kUnsignedLessThan: ++ case kUnsignedGreaterThanOrEqual: ++ VisitCompare(selector, opcode, g.UseRegister(left), ++ g.UseImmediate(right), cont); ++ break; ++ default: ++ VisitCompare(selector, opcode, g.UseRegister(left), ++ g.UseRegister(right), cont); ++ } ++ } ++ } else if (g.CanBeImmediate(left, opcode)) { ++ if (!commutative) cont->Commute(); ++ if (opcode == kSw64Tst) { ++ VisitCompare(selector, opcode, g.UseRegister(right), g.UseImmediate(left), ++ cont); ++ } else { ++ switch (cont->condition()) { ++ case kEqual: ++ case kNotEqual: ++ if (cont->IsSet()) { ++ VisitCompare(selector, opcode, g.UseRegister(right), ++ g.UseImmediate(left), cont); ++ } else { ++ VisitCompare(selector, opcode, g.UseRegister(right), ++ g.UseRegister(left), cont); ++ } ++ break; ++ case kSignedLessThan: ++ case kSignedGreaterThanOrEqual: ++ case kUnsignedLessThan: ++ case kUnsignedGreaterThanOrEqual: ++ VisitCompare(selector, opcode, g.UseRegister(right), ++ g.UseImmediate(left), cont); ++ break; ++ default: ++ VisitCompare(selector, opcode, g.UseRegister(right), ++ g.UseRegister(left), cont); ++ } ++ } ++ } else { ++ VisitCompare(selector, opcode, g.UseRegister(left), g.UseRegister(right), ++ cont); ++ } ++} ++ ++bool IsNodeUnsigned(Node* n) { ++ NodeMatcher m(n); ++ ++ if (m.IsLoad() || m.IsUnalignedLoad() || m.IsProtectedLoad()) { ++ LoadRepresentation load_rep = LoadRepresentationOf(n->op()); ++ return load_rep.IsUnsigned(); ++ } else if (m.IsWord32AtomicLoad() || m.IsWord64AtomicLoad()) { ++ AtomicLoadParameters atomic_load_params = AtomicLoadParametersOf(n->op()); ++ LoadRepresentation load_rep = atomic_load_params.representation(); ++ return load_rep.IsUnsigned(); ++ } else { ++ return m.IsUint32Div() || m.IsUint32LessThan() || ++ m.IsUint32LessThanOrEqual() || m.IsUint32Mod() || ++ m.IsUint32MulHigh() || m.IsChangeFloat64ToUint32() || ++ m.IsTruncateFloat64ToUint32() || m.IsTruncateFloat32ToUint32(); ++ } ++} ++ ++// Shared routine for multiple word compare operations. ++void VisitFullWord32Compare(InstructionSelector* selector, Node* node, ++ InstructionCode opcode, FlagsContinuation* cont) { ++ Sw64OperandGenerator g(selector); ++ InstructionOperand leftOp = g.TempRegister(); ++ InstructionOperand rightOp = g.TempRegister(); ++ ++ selector->Emit(kSw64Slll, leftOp, g.UseRegister(node->InputAt(0)), ++ g.TempImmediate(32)); ++ selector->Emit(kSw64Slll, rightOp, g.UseRegister(node->InputAt(1)), ++ g.TempImmediate(32)); ++ ++ VisitCompare(selector, opcode, leftOp, rightOp, cont); ++} ++ ++void VisitOptimizedWord32Compare(InstructionSelector* selector, Node* node, ++ InstructionCode opcode, ++ FlagsContinuation* cont) { ++ if (FLAG_debug_code) { ++ Sw64OperandGenerator g(selector); ++ InstructionOperand leftOp = g.TempRegister(); ++ InstructionOperand rightOp = g.TempRegister(); ++ InstructionOperand optimizedResult = g.TempRegister(); ++ InstructionOperand fullResult = g.TempRegister(); ++ FlagsCondition condition = cont->condition(); ++ InstructionCode testOpcode = opcode | ++ FlagsConditionField::encode(condition) | ++ FlagsModeField::encode(kFlags_set); ++ ++ selector->Emit(testOpcode, optimizedResult, g.UseRegister(node->InputAt(0)), ++ g.UseRegister(node->InputAt(1))); ++ ++ selector->Emit(kSw64Slll, leftOp, g.UseRegister(node->InputAt(0)), ++ g.TempImmediate(32)); ++ selector->Emit(kSw64Slll, rightOp, g.UseRegister(node->InputAt(1)), ++ g.TempImmediate(32)); ++ selector->Emit(testOpcode, fullResult, leftOp, rightOp); ++ ++ selector->Emit(kSw64AssertEqual, g.NoOutput(), optimizedResult, fullResult, ++ g.TempImmediate(static_cast( ++ AbortReason::kUnsupportedNonPrimitiveCompare))); ++ } ++ ++ VisitWordCompare(selector, node, opcode, cont, false); ++} ++ ++void VisitWord32Compare(InstructionSelector* selector, Node* node, ++ FlagsContinuation* cont) { ++ // SW64 doesn't support Word32 compare instructions. Instead it relies ++ // that the values in registers are correctly sign-extended and uses ++ // Word64 comparison instead. This behavior is correct in most cases, ++ // but doesn't work when comparing signed with unsigned operands. ++ // We could simulate full Word32 compare in all cases but this would ++ // create an unnecessary overhead since unsigned integers are rarely ++ // used in JavaScript. ++ // The solution proposed here tries to match a comparison of signed ++ // with unsigned operand, and perform full Word32Compare only ++ // in those cases. Unfortunately, the solution is not complete because ++ // it might skip cases where Word32 full compare is needed, so ++ // basically it is a hack. ++ // When call to a host function in simulator, if the function return a ++ // int32 value, the simulator do not sign-extended to int64 because in ++ // simulator we do not know the function whether return a int32 or int64. ++ // so we need do a full word32 compare in this case. ++#ifndef USE_SIMULATOR ++ if (IsNodeUnsigned(node->InputAt(0)) != IsNodeUnsigned(node->InputAt(1))) { ++#else ++ if (IsNodeUnsigned(node->InputAt(0)) != IsNodeUnsigned(node->InputAt(1)) || ++ node->InputAt(0)->opcode() == IrOpcode::kCall || ++ node->InputAt(1)->opcode() == IrOpcode::kCall) { ++#endif ++ VisitFullWord32Compare(selector, node, kSw64Cmp, cont); ++ } else { ++ VisitOptimizedWord32Compare(selector, node, kSw64Cmp, cont); ++ } ++} ++ ++void VisitWord64Compare(InstructionSelector* selector, Node* node, ++ FlagsContinuation* cont) { ++ VisitWordCompare(selector, node, kSw64Cmp, cont, false); ++} ++ ++void EmitWordCompareZero(InstructionSelector* selector, Node* value, ++ FlagsContinuation* cont) { ++ Sw64OperandGenerator g(selector); ++ selector->EmitWithContinuation(kSw64Cmp, g.UseRegister(value), ++ g.TempImmediate(0), cont); ++} ++ ++void VisitAtomicLoad(InstructionSelector* selector, Node* node, ++ AtomicWidth width) { ++ Sw64OperandGenerator g(selector); ++ Node* base = node->InputAt(0); ++ Node* index = node->InputAt(1); ++ ++ // The memory order is ignored. ++ AtomicLoadParameters atomic_load_params = AtomicLoadParametersOf(node->op()); ++ LoadRepresentation load_rep = atomic_load_params.representation(); ++ InstructionCode code; ++ switch (load_rep.representation()) { ++ case MachineRepresentation::kWord8: ++ DCHECK_IMPLIES(load_rep.IsSigned(), width == AtomicWidth::kWord32); ++ code = load_rep.IsSigned() ? kAtomicLoadInt8 : kAtomicLoadUint8; ++ break; ++ case MachineRepresentation::kWord16: ++ DCHECK_IMPLIES(load_rep.IsSigned(), width == AtomicWidth::kWord32); ++ code = load_rep.IsSigned() ? kAtomicLoadInt16 : kAtomicLoadUint16; ++ break; ++ case MachineRepresentation::kWord32: ++ code = kAtomicLoadWord32; ++ break; ++ case MachineRepresentation::kWord64: ++ code = kSw64Word64AtomicLoadUint64; ++ break; ++ case MachineRepresentation::kTaggedSigned: // Fall through. ++ case MachineRepresentation::kTaggedPointer: // Fall through. ++ case MachineRepresentation::kTagged: ++ DCHECK_EQ(kTaggedSize, 8); ++ code = kSw64Word64AtomicLoadUint64; ++ break; ++ default: ++ UNREACHABLE(); ++ } ++ ++ if (g.CanBeImmediate(index, code)) { ++ selector->Emit(code | AddressingModeField::encode(kMode_MRI) | ++ AtomicWidthField::encode(width), ++ g.DefineAsRegister(node), g.UseRegister(base), ++ g.UseImmediate(index)); ++ } else { ++ InstructionOperand addr_reg = g.TempRegister(); ++ selector->Emit(kSw64Addl | AddressingModeField::encode(kMode_None), ++ addr_reg, g.UseRegister(index), g.UseRegister(base)); ++ // Emit desired load opcode, using temp addr_reg. ++ selector->Emit(code | AddressingModeField::encode(kMode_MRI) | ++ AtomicWidthField::encode(width), ++ g.DefineAsRegister(node), addr_reg, g.TempImmediate(0)); ++ } ++} ++ ++void VisitAtomicStore(InstructionSelector* selector, Node* node, ++ AtomicWidth width) { ++ Sw64OperandGenerator g(selector); ++ Node* base = node->InputAt(0); ++ Node* index = node->InputAt(1); ++ Node* value = node->InputAt(2); ++ ++ // The memory order is ignored. ++ AtomicStoreParameters store_params = AtomicStoreParametersOf(node->op()); ++ WriteBarrierKind write_barrier_kind = store_params.write_barrier_kind(); ++ MachineRepresentation rep = store_params.representation(); ++ ++ if (FLAG_enable_unconditional_write_barriers && ++ CanBeTaggedOrCompressedPointer(rep)) { ++ write_barrier_kind = kFullWriteBarrier; ++ } ++ ++ InstructionCode code; ++ ++ if (write_barrier_kind != kNoWriteBarrier && !FLAG_disable_write_barriers) { ++ DCHECK(CanBeTaggedPointer(rep)); ++ DCHECK_EQ(AtomicWidthSize(width), kTaggedSize); ++ ++ InstructionOperand inputs[3]; ++ size_t input_count = 0; ++ inputs[input_count++] = g.UseUniqueRegister(base); ++ inputs[input_count++] = g.UseUniqueRegister(index); ++ inputs[input_count++] = g.UseUniqueRegister(value); ++ RecordWriteMode record_write_mode = ++ WriteBarrierKindToRecordWriteMode(write_barrier_kind); ++ InstructionOperand temps[] = {g.TempRegister(), g.TempRegister()}; ++ size_t const temp_count = arraysize(temps); ++ code = kArchAtomicStoreWithWriteBarrier; ++ code |= MiscField::encode(static_cast(record_write_mode)); ++ selector->Emit(code, 0, nullptr, input_count, inputs, temp_count, temps); ++ } else { ++ switch (rep) { ++ case MachineRepresentation::kWord8: ++ code = kAtomicStoreWord8; ++ break; ++ case MachineRepresentation::kWord16: ++ code = kAtomicStoreWord16; ++ break; ++ case MachineRepresentation::kWord32: ++ code = kAtomicStoreWord32; ++ break; ++ case MachineRepresentation::kWord64: ++ DCHECK_EQ(width, AtomicWidth::kWord64); ++ code = kSw64Word64AtomicStoreWord64; ++ break; ++ case MachineRepresentation::kTaggedSigned: // Fall through. ++ case MachineRepresentation::kTaggedPointer: // Fall through. ++ case MachineRepresentation::kTagged: ++ DCHECK_EQ(AtomicWidthSize(width), kTaggedSize); ++ code = kSw64StoreCompressTagged; ++ break; ++ default: ++ UNREACHABLE(); ++ } ++ code |= AtomicWidthField::encode(width); ++ ++ if (g.CanBeImmediate(index, code)) { ++ selector->Emit(code | AddressingModeField::encode(kMode_MRI) | ++ AtomicWidthField::encode(width), ++ g.NoOutput(), g.UseRegister(base), g.UseImmediate(index), ++ g.UseRegisterOrImmediateZero(value)); ++ } else { ++ InstructionOperand addr_reg = g.TempRegister(); ++ selector->Emit(kSw64Addl | AddressingModeField::encode(kMode_None), ++ addr_reg, g.UseRegister(index), g.UseRegister(base)); ++ // Emit desired store opcode, using temp addr_reg. ++ selector->Emit(code | AddressingModeField::encode(kMode_MRI) | ++ AtomicWidthField::encode(width), ++ g.NoOutput(), addr_reg, g.TempImmediate(0), ++ g.UseRegisterOrImmediateZero(value)); ++ } ++ } ++} ++ ++void VisitAtomicExchange(InstructionSelector* selector, Node* node, ++ ArchOpcode opcode, AtomicWidth width) { ++ Sw64OperandGenerator g(selector); ++ Node* base = node->InputAt(0); ++ Node* index = node->InputAt(1); ++ Node* value = node->InputAt(2); ++ ++ AddressingMode addressing_mode = kMode_MRI; ++ InstructionOperand inputs[3]; ++ size_t input_count = 0; ++ inputs[input_count++] = g.UseUniqueRegister(base); ++ inputs[input_count++] = g.UseUniqueRegister(index); ++ inputs[input_count++] = g.UseUniqueRegister(value); ++ InstructionOperand outputs[1]; ++ outputs[0] = g.UseUniqueRegister(node); ++ InstructionOperand temp[3]; ++ temp[0] = g.TempRegister(); ++ temp[1] = g.TempRegister(); ++ temp[2] = g.TempRegister(); ++ InstructionCode code = opcode | AddressingModeField::encode(addressing_mode) | ++ AtomicWidthField::encode(width); ++ selector->Emit(code, 1, outputs, input_count, inputs, 3, temp); ++} ++ ++void VisitAtomicCompareExchange(InstructionSelector* selector, Node* node, ++ ArchOpcode opcode, AtomicWidth width) { ++ Sw64OperandGenerator g(selector); ++ Node* base = node->InputAt(0); ++ Node* index = node->InputAt(1); ++ Node* old_value = node->InputAt(2); ++ Node* new_value = node->InputAt(3); ++ ++ AddressingMode addressing_mode = kMode_MRI; ++ InstructionOperand inputs[4]; ++ size_t input_count = 0; ++ inputs[input_count++] = g.UseUniqueRegister(base); ++ inputs[input_count++] = g.UseUniqueRegister(index); ++ inputs[input_count++] = g.UseUniqueRegister(old_value); ++ inputs[input_count++] = g.UseUniqueRegister(new_value); ++ InstructionOperand outputs[1]; ++ outputs[0] = g.UseUniqueRegister(node); ++ InstructionOperand temp[3]; ++ temp[0] = g.TempRegister(); ++ temp[1] = g.TempRegister(); ++ temp[2] = g.TempRegister(); ++ InstructionCode code = opcode | AddressingModeField::encode(addressing_mode) | ++ AtomicWidthField::encode(width); ++ selector->Emit(code, 1, outputs, input_count, inputs, 3, temp); ++} ++ ++void VisitAtomicBinop(InstructionSelector* selector, Node* node, ++ ArchOpcode opcode, AtomicWidth width) { ++ Sw64OperandGenerator g(selector); ++ Node* base = node->InputAt(0); ++ Node* index = node->InputAt(1); ++ Node* value = node->InputAt(2); ++ ++ AddressingMode addressing_mode = kMode_MRI; ++ InstructionOperand inputs[3]; ++ size_t input_count = 0; ++ inputs[input_count++] = g.UseUniqueRegister(base); ++ inputs[input_count++] = g.UseUniqueRegister(index); ++ inputs[input_count++] = g.UseUniqueRegister(value); ++ InstructionOperand outputs[1]; ++ outputs[0] = g.UseUniqueRegister(node); ++ InstructionOperand temps[4]; ++ temps[0] = g.TempRegister(); ++ temps[1] = g.TempRegister(); ++ temps[2] = g.TempRegister(); ++ temps[3] = g.TempRegister(); ++ InstructionCode code = opcode | AddressingModeField::encode(addressing_mode) | ++ AtomicWidthField::encode(width); ++ selector->Emit(code, 1, outputs, input_count, inputs, 4, temps); ++} ++ ++} // namespace ++ ++void InstructionSelector::VisitStackPointerGreaterThan( ++ Node* node, FlagsContinuation* cont) { ++ StackCheckKind kind = StackCheckKindOf(node->op()); ++ InstructionCode opcode = ++ kArchStackPointerGreaterThan | MiscField::encode(static_cast(kind)); ++ ++ Sw64OperandGenerator g(this); ++ ++ // No outputs. ++ InstructionOperand* const outputs = nullptr; ++ const int output_count = 0; ++ ++ // TempRegister(0) is used to store the comparison result. ++ // Applying an offset to this stack check requires a temp register. Offsets ++ // are only applied to the first stack check. If applying an offset, we must ++ // ensure the input and temp registers do not alias, thus kUniqueRegister. ++ InstructionOperand temps[] = {g.TempRegister(), g.TempRegister()}; ++ const int temp_count = (kind == StackCheckKind::kJSFunctionEntry ? 2 : 1); ++ const auto register_mode = (kind == StackCheckKind::kJSFunctionEntry) ++ ? OperandGenerator::kUniqueRegister ++ : OperandGenerator::kRegister; ++ ++ Node* const value = node->InputAt(0); ++ InstructionOperand inputs[] = {g.UseRegisterWithMode(value, register_mode)}; ++ static constexpr int input_count = arraysize(inputs); ++ ++ EmitWithContinuation(opcode, output_count, outputs, input_count, inputs, ++ temp_count, temps, cont); ++} ++ ++// Shared routine for word comparisons against zero. ++void InstructionSelector::VisitWordCompareZero(Node* user, Node* value, ++ FlagsContinuation* cont) { ++ // Try to combine with comparisons against 0 by simply inverting the branch. ++ while (CanCover(user, value)) { ++ if (value->opcode() == IrOpcode::kWord32Equal) { ++ Int32BinopMatcher m(value); ++ if (!m.right().Is(0)) break; ++ user = value; ++ value = m.left().node(); ++ } else if (value->opcode() == IrOpcode::kWord64Equal) { ++ Int64BinopMatcher m(value); ++ if (!m.right().Is(0)) break; ++ user = value; ++ value = m.left().node(); ++ } else { ++ break; ++ } ++ ++ cont->Negate(); ++ } ++ ++ if (CanCover(user, value)) { ++ switch (value->opcode()) { ++ case IrOpcode::kWord32Equal: ++ cont->OverwriteAndNegateIfEqual(kEqual); ++ return VisitWord32Compare(this, value, cont); ++ case IrOpcode::kInt32LessThan: ++ cont->OverwriteAndNegateIfEqual(kSignedLessThan); ++ return VisitWord32Compare(this, value, cont); ++ case IrOpcode::kInt32LessThanOrEqual: ++ cont->OverwriteAndNegateIfEqual(kSignedLessThanOrEqual); ++ return VisitWord32Compare(this, value, cont); ++ case IrOpcode::kUint32LessThan: ++ cont->OverwriteAndNegateIfEqual(kUnsignedLessThan); ++ return VisitWord32Compare(this, value, cont); ++ case IrOpcode::kUint32LessThanOrEqual: ++ cont->OverwriteAndNegateIfEqual(kUnsignedLessThanOrEqual); ++ return VisitWord32Compare(this, value, cont); ++ case IrOpcode::kWord64Equal: ++ cont->OverwriteAndNegateIfEqual(kEqual); ++ return VisitWord64Compare(this, value, cont); ++ case IrOpcode::kInt64LessThan: ++ cont->OverwriteAndNegateIfEqual(kSignedLessThan); ++ return VisitWord64Compare(this, value, cont); ++ case IrOpcode::kInt64LessThanOrEqual: ++ cont->OverwriteAndNegateIfEqual(kSignedLessThanOrEqual); ++ return VisitWord64Compare(this, value, cont); ++ case IrOpcode::kUint64LessThan: ++ cont->OverwriteAndNegateIfEqual(kUnsignedLessThan); ++ return VisitWord64Compare(this, value, cont); ++ case IrOpcode::kUint64LessThanOrEqual: ++ cont->OverwriteAndNegateIfEqual(kUnsignedLessThanOrEqual); ++ return VisitWord64Compare(this, value, cont); ++ case IrOpcode::kFloat32Equal: ++ cont->OverwriteAndNegateIfEqual(kEqual); ++ return VisitFloat32Compare(this, value, cont); ++ case IrOpcode::kFloat32LessThan: ++ cont->OverwriteAndNegateIfEqual(kUnsignedLessThan); ++ return VisitFloat32Compare(this, value, cont); ++ case IrOpcode::kFloat32LessThanOrEqual: ++ cont->OverwriteAndNegateIfEqual(kUnsignedLessThanOrEqual); ++ return VisitFloat32Compare(this, value, cont); ++ case IrOpcode::kFloat64Equal: ++ cont->OverwriteAndNegateIfEqual(kEqual); ++ return VisitFloat64Compare(this, value, cont); ++ case IrOpcode::kFloat64LessThan: ++ cont->OverwriteAndNegateIfEqual(kUnsignedLessThan); ++ return VisitFloat64Compare(this, value, cont); ++ case IrOpcode::kFloat64LessThanOrEqual: ++ cont->OverwriteAndNegateIfEqual(kUnsignedLessThanOrEqual); ++ return VisitFloat64Compare(this, value, cont); ++ case IrOpcode::kProjection: ++ // Check if this is the overflow output projection of an ++ // WithOverflow node. ++ if (ProjectionIndexOf(value->op()) == 1u) { ++ // We cannot combine the WithOverflow with this branch ++ // unless the 0th projection (the use of the actual value of the ++ // is either nullptr, which means there's no use of the ++ // actual value, or was already defined, which means it is scheduled ++ // *AFTER* this branch). ++ Node* const node = value->InputAt(0); ++ Node* const result = NodeProperties::FindProjection(node, 0); ++ if (result == nullptr || IsDefined(result)) { ++ switch (node->opcode()) { ++ case IrOpcode::kInt32AddWithOverflow: ++ cont->OverwriteAndNegateIfEqual(kOverflow); ++ return VisitBinop(this, node, kSw64Addl, cont); ++ case IrOpcode::kInt32SubWithOverflow: ++ cont->OverwriteAndNegateIfEqual(kOverflow); ++ return VisitBinop(this, node, kSw64Subl, cont); ++ case IrOpcode::kInt32MulWithOverflow: ++ cont->OverwriteAndNegateIfEqual(kOverflow); ++ return VisitBinop(this, node, kSw64MulwOvf, cont); ++ case IrOpcode::kInt64AddWithOverflow: ++ cont->OverwriteAndNegateIfEqual(kOverflow); ++ return VisitBinop(this, node, kSw64AddlOvf, cont); ++ case IrOpcode::kInt64SubWithOverflow: ++ cont->OverwriteAndNegateIfEqual(kOverflow); ++ return VisitBinop(this, node, kSw64SublOvf, cont); ++ default: ++ break; ++ } ++ } ++ } ++ break; ++ case IrOpcode::kWord32And: ++ case IrOpcode::kWord64And: ++ return VisitWordCompare(this, value, kSw64Tst, cont, true); ++ case IrOpcode::kStackPointerGreaterThan: ++ cont->OverwriteAndNegateIfEqual(kStackPointerGreaterThanCondition); ++ return VisitStackPointerGreaterThan(value, cont); ++ default: ++ break; ++ } ++ } ++ ++ // Continuation could not be combined with a compare, emit compare against 0. ++ EmitWordCompareZero(this, value, cont); ++} ++ ++void InstructionSelector::VisitSwitch(Node* node, const SwitchInfo& sw) { ++ Sw64OperandGenerator g(this); ++ InstructionOperand value_operand = g.UseRegister(node->InputAt(0)); ++ ++ // Emit either ArchTableSwitch or ArchBinarySearchSwitch. ++ if (enable_switch_jump_table_ == kEnableSwitchJumpTable) { ++ static const size_t kMaxTableSwitchValueRange = 2 << 16; ++ size_t table_space_cost = 10 + 2 * sw.value_range(); ++ size_t table_time_cost = 3; ++ size_t lookup_space_cost = 2 + 2 * sw.case_count(); ++ size_t lookup_time_cost = sw.case_count(); ++ if (sw.case_count() > 0 && ++ table_space_cost + 3 * table_time_cost <= ++ lookup_space_cost + 3 * lookup_time_cost && ++ sw.min_value() > std::numeric_limits::min() && ++ sw.value_range() <= kMaxTableSwitchValueRange) { ++ InstructionOperand index_operand = value_operand; ++ if (sw.min_value()) { ++ index_operand = g.TempRegister(); ++ Emit(kSw64Subw, index_operand, value_operand, ++ g.TempImmediate(sw.min_value())); ++ } ++ // Generate a table lookup. ++ return EmitTableSwitch(sw, index_operand); ++ } ++ } ++ ++ // Generate a tree of conditional jumps. ++ return EmitBinarySearchSwitch(sw, value_operand); ++} ++ ++void InstructionSelector::VisitWord32Equal(Node* const node) { ++ FlagsContinuation cont = FlagsContinuation::ForSet(kEqual, node); ++ Int32BinopMatcher m(node); ++ if (m.right().Is(0)) { ++ return VisitWordCompareZero(m.node(), m.left().node(), &cont); ++ } ++ ++ VisitWord32Compare(this, node, &cont); ++} ++ ++void InstructionSelector::VisitInt32LessThan(Node* node) { ++ FlagsContinuation cont = FlagsContinuation::ForSet(kSignedLessThan, node); ++ VisitWord32Compare(this, node, &cont); ++} ++ ++void InstructionSelector::VisitInt32LessThanOrEqual(Node* node) { ++ FlagsContinuation cont = ++ FlagsContinuation::ForSet(kSignedLessThanOrEqual, node); ++ VisitWord32Compare(this, node, &cont); ++} ++ ++void InstructionSelector::VisitUint32LessThan(Node* node) { ++ FlagsContinuation cont = FlagsContinuation::ForSet(kUnsignedLessThan, node); ++ VisitWord32Compare(this, node, &cont); ++} ++ ++void InstructionSelector::VisitUint32LessThanOrEqual(Node* node) { ++ FlagsContinuation cont = ++ FlagsContinuation::ForSet(kUnsignedLessThanOrEqual, node); ++ VisitWord32Compare(this, node, &cont); ++} ++ ++void InstructionSelector::VisitInt32AddWithOverflow(Node* node) { ++ if (Node* ovf = NodeProperties::FindProjection(node, 1)) { ++ FlagsContinuation cont = FlagsContinuation::ForSet(kOverflow, ovf); ++ return VisitBinop(this, node, kSw64Addl, &cont); ++ } ++ FlagsContinuation cont; ++ VisitBinop(this, node, kSw64Addl, &cont); ++} ++ ++void InstructionSelector::VisitInt32SubWithOverflow(Node* node) { ++ if (Node* ovf = NodeProperties::FindProjection(node, 1)) { ++ FlagsContinuation cont = FlagsContinuation::ForSet(kOverflow, ovf); ++ return VisitBinop(this, node, kSw64Subl, &cont); ++ } ++ FlagsContinuation cont; ++ VisitBinop(this, node, kSw64Subl, &cont); ++} ++ ++void InstructionSelector::VisitInt32MulWithOverflow(Node* node) { ++ if (Node* ovf = NodeProperties::FindProjection(node, 1)) { ++ FlagsContinuation cont = FlagsContinuation::ForSet(kOverflow, ovf); ++ return VisitBinop(this, node, kSw64MulwOvf, &cont); ++ } ++ FlagsContinuation cont; ++ VisitBinop(this, node, kSw64MulwOvf, &cont); ++} ++ ++void InstructionSelector::VisitInt64AddWithOverflow(Node* node) { ++ if (Node* ovf = NodeProperties::FindProjection(node, 1)) { ++ FlagsContinuation cont = FlagsContinuation::ForSet(kOverflow, ovf); ++ return VisitBinop(this, node, kSw64AddlOvf, &cont); ++ } ++ FlagsContinuation cont; ++ VisitBinop(this, node, kSw64AddlOvf, &cont); ++} ++ ++void InstructionSelector::VisitInt64SubWithOverflow(Node* node) { ++ if (Node* ovf = NodeProperties::FindProjection(node, 1)) { ++ FlagsContinuation cont = FlagsContinuation::ForSet(kOverflow, ovf); ++ return VisitBinop(this, node, kSw64SublOvf, &cont); ++ } ++ FlagsContinuation cont; ++ VisitBinop(this, node, kSw64SublOvf, &cont); ++} ++ ++void InstructionSelector::VisitWord64Equal(Node* const node) { ++ FlagsContinuation cont = FlagsContinuation::ForSet(kEqual, node); ++ Int64BinopMatcher m(node); ++ if (m.right().Is(0)) { ++ return VisitWordCompareZero(m.node(), m.left().node(), &cont); ++ } ++ ++ VisitWord64Compare(this, node, &cont); ++} ++ ++void InstructionSelector::VisitInt64LessThan(Node* node) { ++ FlagsContinuation cont = FlagsContinuation::ForSet(kSignedLessThan, node); ++ VisitWord64Compare(this, node, &cont); ++} ++ ++void InstructionSelector::VisitInt64LessThanOrEqual(Node* node) { ++ FlagsContinuation cont = ++ FlagsContinuation::ForSet(kSignedLessThanOrEqual, node); ++ VisitWord64Compare(this, node, &cont); ++} ++ ++void InstructionSelector::VisitUint64LessThan(Node* node) { ++ FlagsContinuation cont = FlagsContinuation::ForSet(kUnsignedLessThan, node); ++ VisitWord64Compare(this, node, &cont); ++} ++ ++void InstructionSelector::VisitUint64LessThanOrEqual(Node* node) { ++ FlagsContinuation cont = ++ FlagsContinuation::ForSet(kUnsignedLessThanOrEqual, node); ++ VisitWord64Compare(this, node, &cont); ++} ++ ++void InstructionSelector::VisitFloat32Equal(Node* node) { ++ FlagsContinuation cont = FlagsContinuation::ForSet(kEqual, node); ++ VisitFloat32Compare(this, node, &cont); ++} ++ ++void InstructionSelector::VisitFloat32LessThan(Node* node) { ++ FlagsContinuation cont = FlagsContinuation::ForSet(kUnsignedLessThan, node); ++ VisitFloat32Compare(this, node, &cont); ++} ++ ++void InstructionSelector::VisitFloat32LessThanOrEqual(Node* node) { ++ FlagsContinuation cont = ++ FlagsContinuation::ForSet(kUnsignedLessThanOrEqual, node); ++ VisitFloat32Compare(this, node, &cont); ++} ++ ++void InstructionSelector::VisitFloat64Equal(Node* node) { ++ FlagsContinuation cont = FlagsContinuation::ForSet(kEqual, node); ++ VisitFloat64Compare(this, node, &cont); ++} ++ ++void InstructionSelector::VisitFloat64LessThan(Node* node) { ++ FlagsContinuation cont = FlagsContinuation::ForSet(kUnsignedLessThan, node); ++ VisitFloat64Compare(this, node, &cont); ++} ++ ++void InstructionSelector::VisitFloat64LessThanOrEqual(Node* node) { ++ FlagsContinuation cont = ++ FlagsContinuation::ForSet(kUnsignedLessThanOrEqual, node); ++ VisitFloat64Compare(this, node, &cont); ++} ++ ++void InstructionSelector::VisitFloat64ExtractLowWord32(Node* node) { ++ VisitRR(this, kSw64Float64ExtractLowWord32, node); ++} ++ ++void InstructionSelector::VisitFloat64ExtractHighWord32(Node* node) { ++ VisitRR(this, kSw64Float64ExtractHighWord32, node); ++} ++ ++void InstructionSelector::VisitFloat64SilenceNaN(Node* node) { ++ VisitRR(this, kSw64Float64SilenceNaN, node); ++} ++ ++void InstructionSelector::VisitFloat64InsertLowWord32(Node* node) { ++ Sw64OperandGenerator g(this); ++ Node* left = node->InputAt(0); ++ Node* right = node->InputAt(1); ++ Emit(kSw64Float64InsertLowWord32, g.DefineSameAsFirst(node), ++ g.UseRegister(left), g.UseRegister(right)); ++} ++ ++void InstructionSelector::VisitFloat64InsertHighWord32(Node* node) { ++ Sw64OperandGenerator g(this); ++ Node* left = node->InputAt(0); ++ Node* right = node->InputAt(1); ++ Emit(kSw64Float64InsertHighWord32, g.DefineSameAsFirst(node), ++ g.UseRegister(left), g.UseRegister(right)); ++} ++ ++void InstructionSelector::VisitMemoryBarrier(Node* node) { ++ Sw64OperandGenerator g(this); ++ Emit(kSw64Memb, g.NoOutput()); ++} ++ ++void InstructionSelector::VisitWord32AtomicLoad(Node* node) { ++ VisitAtomicLoad(this, node, AtomicWidth::kWord32); ++} ++ ++void InstructionSelector::VisitWord32AtomicStore(Node* node) { ++ VisitAtomicStore(this, node, AtomicWidth::kWord32); ++} ++ ++void InstructionSelector::VisitWord64AtomicLoad(Node* node) { ++ VisitAtomicLoad(this, node, AtomicWidth::kWord64); ++} ++ ++void InstructionSelector::VisitWord64AtomicStore(Node* node) { ++ VisitAtomicStore(this, node, AtomicWidth::kWord64); ++} ++ ++void InstructionSelector::VisitWord32AtomicExchange(Node* node) { ++ ArchOpcode opcode; ++ MachineType type = AtomicOpType(node->op()); ++ if (type == MachineType::Int8()) { ++ opcode = kAtomicExchangeInt8; ++ } else if (type == MachineType::Uint8()) { ++ opcode = kAtomicExchangeUint8; ++ } else if (type == MachineType::Int16()) { ++ opcode = kAtomicExchangeInt16; ++ } else if (type == MachineType::Uint16()) { ++ opcode = kAtomicExchangeUint16; ++ } else if (type == MachineType::Int32() || type == MachineType::Uint32()) { ++ opcode = kAtomicExchangeWord32; ++ } else { ++ UNREACHABLE(); ++ } ++ ++ VisitAtomicExchange(this, node, opcode, AtomicWidth::kWord32); ++} ++ ++void InstructionSelector::VisitWord64AtomicExchange(Node* node) { ++ ArchOpcode opcode; ++ MachineType type = AtomicOpType(node->op()); ++ if (type == MachineType::Uint8()) { ++ opcode = kAtomicExchangeUint8; ++ } else if (type == MachineType::Uint16()) { ++ opcode = kAtomicExchangeUint16; ++ } else if (type == MachineType::Uint32()) { ++ opcode = kAtomicExchangeWord32; ++ } else if (type == MachineType::Uint64()) { ++ opcode = kSw64Word64AtomicExchangeUint64; ++ } else { ++ UNREACHABLE(); ++ } ++ VisitAtomicExchange(this, node, opcode, AtomicWidth::kWord64); ++} ++ ++void InstructionSelector::VisitWord32AtomicCompareExchange(Node* node) { ++ ArchOpcode opcode; ++ MachineType type = AtomicOpType(node->op()); ++ if (type == MachineType::Int8()) { ++ opcode = kAtomicCompareExchangeInt8; ++ } else if (type == MachineType::Uint8()) { ++ opcode = kAtomicCompareExchangeUint8; ++ } else if (type == MachineType::Int16()) { ++ opcode = kAtomicCompareExchangeInt16; ++ } else if (type == MachineType::Uint16()) { ++ opcode = kAtomicCompareExchangeUint16; ++ } else if (type == MachineType::Int32() || type == MachineType::Uint32()) { ++ opcode = kAtomicCompareExchangeWord32; ++ } else { ++ UNREACHABLE(); ++ } ++ ++ VisitAtomicCompareExchange(this, node, opcode, AtomicWidth::kWord32); ++} ++ ++void InstructionSelector::VisitWord64AtomicCompareExchange(Node* node) { ++ ArchOpcode opcode; ++ MachineType type = AtomicOpType(node->op()); ++ if (type == MachineType::Uint8()) { ++ opcode = kAtomicCompareExchangeUint8; ++ } else if (type == MachineType::Uint16()) { ++ opcode = kAtomicCompareExchangeUint16; ++ } else if (type == MachineType::Uint32()) { ++ opcode = kAtomicCompareExchangeWord32; ++ } else if (type == MachineType::Uint64()) { ++ opcode = kSw64Word64AtomicCompareExchangeUint64; ++ } else { ++ UNREACHABLE(); ++ } ++ VisitAtomicCompareExchange(this, node, opcode, AtomicWidth::kWord64); ++} ++void InstructionSelector::VisitWord32AtomicBinaryOperation( ++ Node* node, ArchOpcode int8_op, ArchOpcode uint8_op, ArchOpcode int16_op, ++ ArchOpcode uint16_op, ArchOpcode word32_op) { ++ ArchOpcode opcode; ++ MachineType type = AtomicOpType(node->op()); ++ if (type == MachineType::Int8()) { ++ opcode = int8_op; ++ } else if (type == MachineType::Uint8()) { ++ opcode = uint8_op; ++ } else if (type == MachineType::Int16()) { ++ opcode = int16_op; ++ } else if (type == MachineType::Uint16()) { ++ opcode = uint16_op; ++ } else if (type == MachineType::Int32() || type == MachineType::Uint32()) { ++ opcode = word32_op; ++ } else { ++ UNREACHABLE(); ++ } ++ ++ VisitAtomicBinop(this, node, opcode, AtomicWidth::kWord32); ++} ++ ++#define VISIT_ATOMIC_BINOP(op) \ ++ void InstructionSelector::VisitWord32Atomic##op(Node* node) { \ ++ VisitWord32AtomicBinaryOperation( \ ++ node, kAtomic##op##Int8, kAtomic##op##Uint8, kAtomic##op##Int16, \ ++ kAtomic##op##Uint16, kAtomic##op##Word32); \ ++ } ++VISIT_ATOMIC_BINOP(Add) ++VISIT_ATOMIC_BINOP(Sub) ++VISIT_ATOMIC_BINOP(And) ++VISIT_ATOMIC_BINOP(Or) ++VISIT_ATOMIC_BINOP(Xor) ++#undef VISIT_ATOMIC_BINOP ++ ++void InstructionSelector::VisitWord64AtomicBinaryOperation( ++ Node* node, ArchOpcode uint8_op, ArchOpcode uint16_op, ArchOpcode uint32_op, ++ ArchOpcode uint64_op) { ++ ArchOpcode opcode; ++ MachineType type = AtomicOpType(node->op()); ++ if (type == MachineType::Uint8()) { ++ opcode = uint8_op; ++ } else if (type == MachineType::Uint16()) { ++ opcode = uint16_op; ++ } else if (type == MachineType::Uint32()) { ++ opcode = uint32_op; ++ } else if (type == MachineType::Uint64()) { ++ opcode = uint64_op; ++ } else { ++ UNREACHABLE(); ++ } ++ VisitAtomicBinop(this, node, opcode, AtomicWidth::kWord64); ++} ++ ++#define VISIT_ATOMIC_BINOP(op) \ ++ void InstructionSelector::VisitWord64Atomic##op(Node* node) { \ ++ VisitWord64AtomicBinaryOperation(node, kAtomic##op##Uint8, \ ++ kAtomic##op##Uint16, kAtomic##op##Word32, \ ++ kSw64Word64Atomic##op##Uint64); \ ++ } ++VISIT_ATOMIC_BINOP(Add) ++VISIT_ATOMIC_BINOP(Sub) ++VISIT_ATOMIC_BINOP(And) ++VISIT_ATOMIC_BINOP(Or) ++VISIT_ATOMIC_BINOP(Xor) ++#undef VISIT_ATOMIC_BINOP ++ ++void InstructionSelector::VisitInt32AbsWithOverflow(Node* node) { ++ UNREACHABLE(); ++} ++ ++void InstructionSelector::VisitInt64AbsWithOverflow(Node* node) { ++ UNREACHABLE(); ++} ++ ++#define SIMD_TYPE_LIST(V) \ ++ V(F64x2) \ ++ V(F32x4) \ ++ V(I64x2) \ ++ V(I32x4) \ ++ V(I16x8) \ ++ V(I8x16) ++ ++#define SIMD_UNOP_LIST(V) \ ++ V(F64x2Abs, kSw64F64x2Abs) \ ++ V(F64x2Neg, kSw64F64x2Neg) \ ++ V(F64x2Sqrt, kSw64F64x2Sqrt) \ ++ V(F64x2Ceil, kSw64F64x2Ceil) \ ++ V(F64x2Floor, kSw64F64x2Floor) \ ++ V(F64x2Trunc, kSw64F64x2Trunc) \ ++ V(F64x2NearestInt, kSw64F64x2NearestInt) \ ++ V(I64x2Neg, kSw64I64x2Neg) \ ++ V(I64x2BitMask, kSw64I64x2BitMask) \ ++ V(F64x2ConvertLowI32x4S, kSw64F64x2ConvertLowI32x4S) \ ++ V(F64x2ConvertLowI32x4U, kSw64F64x2ConvertLowI32x4U) \ ++ V(F64x2PromoteLowF32x4, kSw64F64x2PromoteLowF32x4) \ ++ V(F32x4SConvertI32x4, kSw64F32x4SConvertI32x4) \ ++ V(F32x4UConvertI32x4, kSw64F32x4UConvertI32x4) \ ++ V(F32x4Abs, kSw64F32x4Abs) \ ++ V(F32x4Neg, kSw64F32x4Neg) \ ++ V(F32x4Sqrt, kSw64F32x4Sqrt) \ ++ V(F32x4RecipApprox, kSw64F32x4RecipApprox) \ ++ V(F32x4RecipSqrtApprox, kSw64F32x4RecipSqrtApprox) \ ++ V(F32x4Ceil, kSw64F32x4Ceil) \ ++ V(F32x4Floor, kSw64F32x4Floor) \ ++ V(F32x4Trunc, kSw64F32x4Trunc) \ ++ V(F32x4NearestInt, kSw64F32x4NearestInt) \ ++ V(F32x4DemoteF64x2Zero, kSw64F32x4DemoteF64x2Zero) \ ++ V(I64x2Abs, kSw64I64x2Abs) \ ++ V(I64x2SConvertI32x4Low, kSw64I64x2SConvertI32x4Low) \ ++ V(I64x2SConvertI32x4High, kSw64I64x2SConvertI32x4High) \ ++ V(I64x2UConvertI32x4Low, kSw64I64x2UConvertI32x4Low) \ ++ V(I64x2UConvertI32x4High, kSw64I64x2UConvertI32x4High) \ ++ V(I32x4SConvertF32x4, kSw64I32x4SConvertF32x4) \ ++ V(I32x4UConvertF32x4, kSw64I32x4UConvertF32x4) \ ++ V(I32x4Neg, kSw64I32x4Neg) \ ++ V(I32x4SConvertI16x8Low, kSw64I32x4SConvertI16x8Low) \ ++ V(I32x4SConvertI16x8High, kSw64I32x4SConvertI16x8High) \ ++ V(I32x4UConvertI16x8Low, kSw64I32x4UConvertI16x8Low) \ ++ V(I32x4UConvertI16x8High, kSw64I32x4UConvertI16x8High) \ ++ V(I32x4Abs, kSw64I32x4Abs) \ ++ V(I32x4BitMask, kSw64I32x4BitMask) \ ++ V(I32x4TruncSatF64x2SZero, kSw64I32x4TruncSatF64x2SZero) \ ++ V(I32x4TruncSatF64x2UZero, kSw64I32x4TruncSatF64x2UZero) \ ++ V(I16x8Neg, kSw64I16x8Neg) \ ++ V(I16x8SConvertI8x16Low, kSw64I16x8SConvertI8x16Low) \ ++ V(I16x8SConvertI8x16High, kSw64I16x8SConvertI8x16High) \ ++ V(I16x8UConvertI8x16Low, kSw64I16x8UConvertI8x16Low) \ ++ V(I16x8UConvertI8x16High, kSw64I16x8UConvertI8x16High) \ ++ V(I16x8Abs, kSw64I16x8Abs) \ ++ V(I16x8BitMask, kSw64I16x8BitMask) \ ++ V(I8x16Neg, kSw64I8x16Neg) \ ++ V(I8x16Abs, kSw64I8x16Abs) \ ++ V(I8x16Popcnt, kSw64I8x16Popcnt) \ ++ V(I8x16BitMask, kSw64I8x16BitMask) \ ++ V(S128Not, kSw64S128Not) \ ++ V(I64x2AllTrue, kSw64I64x2AllTrue) \ ++ V(I32x4AllTrue, kSw64I32x4AllTrue) \ ++ V(I16x8AllTrue, kSw64I16x8AllTrue) \ ++ V(I8x16AllTrue, kSw64I8x16AllTrue) \ ++ V(V128AnyTrue, kSw64V128AnyTrue) ++ ++#define SIMD_SHIFT_OP_LIST(V) \ ++ V(I64x2Shl) \ ++ V(I64x2ShrS) \ ++ V(I64x2ShrU) \ ++ V(I32x4Shl) \ ++ V(I32x4ShrS) \ ++ V(I32x4ShrU) \ ++ V(I16x8Shl) \ ++ V(I16x8ShrS) \ ++ V(I16x8ShrU) \ ++ V(I8x16Shl) \ ++ V(I8x16ShrS) \ ++ V(I8x16ShrU) ++ ++#define SIMD_BINOP_LIST(V) \ ++ V(F64x2Add, kSw64F64x2Add) \ ++ V(F64x2Sub, kSw64F64x2Sub) \ ++ V(F64x2Mul, kSw64F64x2Mul) \ ++ V(F64x2Div, kSw64F64x2Div) \ ++ V(F64x2Min, kSw64F64x2Min) \ ++ V(F64x2Max, kSw64F64x2Max) \ ++ V(F64x2Eq, kSw64F64x2Eq) \ ++ V(F64x2Ne, kSw64F64x2Ne) \ ++ V(F64x2Lt, kSw64F64x2Lt) \ ++ V(F64x2Le, kSw64F64x2Le) \ ++ V(I64x2Eq, kSw64I64x2Eq) \ ++ V(I64x2Ne, kSw64I64x2Ne) \ ++ V(I64x2Add, kSw64I64x2Add) \ ++ V(I64x2Sub, kSw64I64x2Sub) \ ++ V(I64x2Mul, kSw64I64x2Mul) \ ++ V(I64x2GtS, kSw64I64x2GtS) \ ++ V(I64x2GeS, kSw64I64x2GeS) \ ++ V(F32x4Add, kSw64F32x4Add) \ ++ V(F32x4Sub, kSw64F32x4Sub) \ ++ V(F32x4Mul, kSw64F32x4Mul) \ ++ V(F32x4Div, kSw64F32x4Div) \ ++ V(F32x4Max, kSw64F32x4Max) \ ++ V(F32x4Min, kSw64F32x4Min) \ ++ V(F32x4Eq, kSw64F32x4Eq) \ ++ V(F32x4Ne, kSw64F32x4Ne) \ ++ V(F32x4Lt, kSw64F32x4Lt) \ ++ V(F32x4Le, kSw64F32x4Le) \ ++ V(I32x4Add, kSw64I32x4Add) \ ++ V(I32x4Sub, kSw64I32x4Sub) \ ++ V(I32x4Mul, kSw64I32x4Mul) \ ++ V(I32x4MaxS, kSw64I32x4MaxS) \ ++ V(I32x4MinS, kSw64I32x4MinS) \ ++ V(I32x4MaxU, kSw64I32x4MaxU) \ ++ V(I32x4MinU, kSw64I32x4MinU) \ ++ V(I32x4Eq, kSw64I32x4Eq) \ ++ V(I32x4Ne, kSw64I32x4Ne) \ ++ V(I32x4GtS, kSw64I32x4GtS) \ ++ V(I32x4GeS, kSw64I32x4GeS) \ ++ V(I32x4GtU, kSw64I32x4GtU) \ ++ V(I32x4GeU, kSw64I32x4GeU) \ ++ V(I32x4DotI16x8S, kSw64I32x4DotI16x8S) \ ++ V(I16x8Add, kSw64I16x8Add) \ ++ V(I16x8AddSatS, kSw64I16x8AddSatS) \ ++ V(I16x8AddSatU, kSw64I16x8AddSatU) \ ++ V(I16x8Sub, kSw64I16x8Sub) \ ++ V(I16x8SubSatS, kSw64I16x8SubSatS) \ ++ V(I16x8SubSatU, kSw64I16x8SubSatU) \ ++ V(I16x8Mul, kSw64I16x8Mul) \ ++ V(I16x8MaxS, kSw64I16x8MaxS) \ ++ V(I16x8MinS, kSw64I16x8MinS) \ ++ V(I16x8MaxU, kSw64I16x8MaxU) \ ++ V(I16x8MinU, kSw64I16x8MinU) \ ++ V(I16x8Eq, kSw64I16x8Eq) \ ++ V(I16x8Ne, kSw64I16x8Ne) \ ++ V(I16x8GtS, kSw64I16x8GtS) \ ++ V(I16x8GeS, kSw64I16x8GeS) \ ++ V(I16x8GtU, kSw64I16x8GtU) \ ++ V(I16x8GeU, kSw64I16x8GeU) \ ++ V(I16x8RoundingAverageU, kSw64I16x8RoundingAverageU) \ ++ V(I16x8SConvertI32x4, kSw64I16x8SConvertI32x4) \ ++ V(I16x8UConvertI32x4, kSw64I16x8UConvertI32x4) \ ++ V(I16x8Q15MulRSatS, kSw64I16x8Q15MulRSatS) \ ++ V(I8x16Add, kSw64I8x16Add) \ ++ V(I8x16AddSatS, kSw64I8x16AddSatS) \ ++ V(I8x16AddSatU, kSw64I8x16AddSatU) \ ++ V(I8x16Sub, kSw64I8x16Sub) \ ++ V(I8x16SubSatS, kSw64I8x16SubSatS) \ ++ V(I8x16SubSatU, kSw64I8x16SubSatU) \ ++ V(I8x16MaxS, kSw64I8x16MaxS) \ ++ V(I8x16MinS, kSw64I8x16MinS) \ ++ V(I8x16MaxU, kSw64I8x16MaxU) \ ++ V(I8x16MinU, kSw64I8x16MinU) \ ++ V(I8x16Eq, kSw64I8x16Eq) \ ++ V(I8x16Ne, kSw64I8x16Ne) \ ++ V(I8x16GtS, kSw64I8x16GtS) \ ++ V(I8x16GeS, kSw64I8x16GeS) \ ++ V(I8x16GtU, kSw64I8x16GtU) \ ++ V(I8x16GeU, kSw64I8x16GeU) \ ++ V(I8x16RoundingAverageU, kSw64I8x16RoundingAverageU) \ ++ V(I8x16SConvertI16x8, kSw64I8x16SConvertI16x8) \ ++ V(I8x16UConvertI16x8, kSw64I8x16UConvertI16x8) \ ++ V(S128And, kSw64S128And) \ ++ V(S128Or, kSw64S128Or) \ ++ V(S128Xor, kSw64S128Xor) \ ++ V(S128AndNot, kSw64S128AndNot) ++ ++// SKTODO ++void InstructionSelector::VisitS128Const(Node* node) { ++ Sw64OperandGenerator g(this); ++ static const int kUint32Immediates = kSimd128Size / sizeof(uint32_t); ++ uint32_t val[kUint32Immediates]; ++ memcpy(val, S128ImmediateParameterOf(node->op()).data(), kSimd128Size); ++ // If all bytes are zeros or ones, avoid emitting code for generic constants ++ bool all_zeros = !(val[0] || val[1] || val[2] || val[3]); ++ bool all_ones = val[0] == UINT32_MAX && val[1] == UINT32_MAX && ++ val[2] == UINT32_MAX && val[3] == UINT32_MAX; ++ InstructionOperand dst = g.DefineAsRegister(node); ++ if (all_zeros) { ++ Emit(kSw64S128Zero, dst); ++ } else if (all_ones) { ++ Emit(kSw64S128AllOnes, dst); ++ } else { ++ Emit(kSw64S128Const, dst, g.UseImmediate(val[0]), g.UseImmediate(val[1]), ++ g.UseImmediate(val[2]), g.UseImmediate(val[3])); ++ } ++} ++ ++void InstructionSelector::VisitS128Zero(Node* node) { ++ Sw64OperandGenerator g(this); ++ Emit(kSw64S128Zero, g.DefineAsRegister(node)); ++} ++ ++#define SIMD_VISIT_SPLAT(Type) \ ++ void InstructionSelector::Visit##Type##Splat(Node* node) { \ ++ VisitRR(this, kSw64##Type##Splat, node); \ ++ } ++SIMD_TYPE_LIST(SIMD_VISIT_SPLAT) ++#undef SIMD_VISIT_SPLAT ++ ++#define SIMD_VISIT_EXTRACT_LANE(Type, Sign) \ ++ void InstructionSelector::Visit##Type##ExtractLane##Sign(Node* node) { \ ++ VisitRRI(this, kSw64##Type##ExtractLane##Sign, node); \ ++ } ++SIMD_VISIT_EXTRACT_LANE(F64x2, ) ++SIMD_VISIT_EXTRACT_LANE(F32x4, ) ++SIMD_VISIT_EXTRACT_LANE(I64x2, ) ++SIMD_VISIT_EXTRACT_LANE(I32x4, ) ++SIMD_VISIT_EXTRACT_LANE(I16x8, U) ++SIMD_VISIT_EXTRACT_LANE(I16x8, S) ++SIMD_VISIT_EXTRACT_LANE(I8x16, U) ++SIMD_VISIT_EXTRACT_LANE(I8x16, S) ++#undef SIMD_VISIT_EXTRACT_LANE ++ ++#define SIMD_VISIT_REPLACE_LANE(Type) \ ++ void InstructionSelector::Visit##Type##ReplaceLane(Node* node) { \ ++ VisitRRIR(this, kSw64##Type##ReplaceLane, node); \ ++ } ++SIMD_TYPE_LIST(SIMD_VISIT_REPLACE_LANE) ++#undef SIMD_VISIT_REPLACE_LANE ++ ++#define SIMD_VISIT_UNOP(Name, instruction) \ ++ void InstructionSelector::Visit##Name(Node* node) { \ ++ VisitRR(this, instruction, node); \ ++ } ++SIMD_UNOP_LIST(SIMD_VISIT_UNOP) ++#undef SIMD_VISIT_UNOP ++ ++#define SIMD_VISIT_SHIFT_OP(Name) \ ++ void InstructionSelector::Visit##Name(Node* node) { \ ++ VisitSimdShift(this, kSw64##Name, node); \ ++ } ++SIMD_SHIFT_OP_LIST(SIMD_VISIT_SHIFT_OP) ++#undef SIMD_VISIT_SHIFT_OP ++ ++#define SIMD_VISIT_BINOP(Name, instruction) \ ++ void InstructionSelector::Visit##Name(Node* node) { \ ++ VisitRRR(this, instruction, node); \ ++ } ++SIMD_BINOP_LIST(SIMD_VISIT_BINOP) ++#undef SIMD_VISIT_BINOP ++ ++void InstructionSelector::VisitS128Select(Node* node) { ++ VisitRRRR(this, kSw64S128Select, node); ++} ++ ++#if V8_ENABLE_WEBASSEMBLY ++namespace { ++ ++struct ShuffleEntry { ++ uint8_t shuffle[kSimd128Size]; ++ ArchOpcode opcode; ++}; ++ ++static const ShuffleEntry arch_shuffles[] = { ++ {{0, 1, 2, 3, 16, 17, 18, 19, 4, 5, 6, 7, 20, 21, 22, 23}, ++ kSw64S32x4InterleaveRight}, ++ {{8, 9, 10, 11, 24, 25, 26, 27, 12, 13, 14, 15, 28, 29, 30, 31}, ++ kSw64S32x4InterleaveLeft}, ++ {{0, 1, 2, 3, 8, 9, 10, 11, 16, 17, 18, 19, 24, 25, 26, 27}, ++ kSw64S32x4PackEven}, ++ {{4, 5, 6, 7, 12, 13, 14, 15, 20, 21, 22, 23, 28, 29, 30, 31}, ++ kSw64S32x4PackOdd}, ++ {{0, 1, 2, 3, 16, 17, 18, 19, 8, 9, 10, 11, 24, 25, 26, 27}, ++ kSw64S32x4InterleaveEven}, ++ {{4, 5, 6, 7, 20, 21, 22, 23, 12, 13, 14, 15, 28, 29, 30, 31}, ++ kSw64S32x4InterleaveOdd}, ++ ++ {{0, 1, 16, 17, 2, 3, 18, 19, 4, 5, 20, 21, 6, 7, 22, 23}, ++ kSw64S16x8InterleaveRight}, ++ {{8, 9, 24, 25, 10, 11, 26, 27, 12, 13, 28, 29, 14, 15, 30, 31}, ++ kSw64S16x8InterleaveLeft}, ++ {{0, 1, 4, 5, 8, 9, 12, 13, 16, 17, 20, 21, 24, 25, 28, 29}, ++ kSw64S16x8PackEven}, ++ {{2, 3, 6, 7, 10, 11, 14, 15, 18, 19, 22, 23, 26, 27, 30, 31}, ++ kSw64S16x8PackOdd}, ++ {{0, 1, 16, 17, 4, 5, 20, 21, 8, 9, 24, 25, 12, 13, 28, 29}, ++ kSw64S16x8InterleaveEven}, ++ {{2, 3, 18, 19, 6, 7, 22, 23, 10, 11, 26, 27, 14, 15, 30, 31}, ++ kSw64S16x8InterleaveOdd}, ++ {{6, 7, 4, 5, 2, 3, 0, 1, 14, 15, 12, 13, 10, 11, 8, 9}, kSw64S16x4Reverse}, ++ {{2, 3, 0, 1, 6, 7, 4, 5, 10, 11, 8, 9, 14, 15, 12, 13}, kSw64S16x2Reverse}, ++ ++ {{0, 16, 1, 17, 2, 18, 3, 19, 4, 20, 5, 21, 6, 22, 7, 23}, ++ kSw64S8x16InterleaveRight}, ++ {{8, 24, 9, 25, 10, 26, 11, 27, 12, 28, 13, 29, 14, 30, 15, 31}, ++ kSw64S8x16InterleaveLeft}, ++ {{0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30}, ++ kSw64S8x16PackEven}, ++ {{1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31}, ++ kSw64S8x16PackOdd}, ++ {{0, 16, 2, 18, 4, 20, 6, 22, 8, 24, 10, 26, 12, 28, 14, 30}, ++ kSw64S8x16InterleaveEven}, ++ {{1, 17, 3, 19, 5, 21, 7, 23, 9, 25, 11, 27, 13, 29, 15, 31}, ++ kSw64S8x16InterleaveOdd}, ++ {{7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8}, kSw64S8x8Reverse}, ++ {{3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8, 15, 14, 13, 12}, kSw64S8x4Reverse}, ++ {{1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14}, kSw64S8x2Reverse}}; ++ ++bool TryMatchArchShuffle(const uint8_t* shuffle, const ShuffleEntry* table, ++ size_t num_entries, bool is_swizzle, ++ ArchOpcode* opcode) { ++ uint8_t mask = is_swizzle ? kSimd128Size - 1 : 2 * kSimd128Size - 1; ++ for (size_t i = 0; i < num_entries; ++i) { ++ const ShuffleEntry& entry = table[i]; ++ int j = 0; ++ for (; j < kSimd128Size; ++j) { ++ if ((entry.shuffle[j] & mask) != (shuffle[j] & mask)) { ++ break; ++ } ++ } ++ if (j == kSimd128Size) { ++ *opcode = entry.opcode; ++ return true; ++ } ++ } ++ return false; ++} ++ ++} // namespace ++ ++void InstructionSelector::VisitI8x16Shuffle(Node* node) { ++ uint8_t shuffle[kSimd128Size]; ++ bool is_swizzle; ++ CanonicalizeShuffle(node, shuffle, &is_swizzle); ++ uint8_t shuffle32x4[4]; ++ ArchOpcode opcode; ++ if (TryMatchArchShuffle(shuffle, arch_shuffles, arraysize(arch_shuffles), ++ is_swizzle, &opcode)) { ++ VisitRRR(this, opcode, node); ++ return; ++ } ++ Node* input0 = node->InputAt(0); ++ Node* input1 = node->InputAt(1); ++ uint8_t offset; ++ Sw64OperandGenerator g(this); ++ if (wasm::SimdShuffle::TryMatchConcat(shuffle, &offset)) { ++ Emit(kSw64S8x16Concat, g.DefineSameAsFirst(node), g.UseRegister(input1), ++ g.UseRegister(input0), g.UseImmediate(offset)); ++ return; ++ } ++ if (wasm::SimdShuffle::TryMatch32x4Shuffle(shuffle, shuffle32x4)) { ++ Emit(kSw64S32x4Shuffle, g.DefineAsRegister(node), g.UseRegister(input0), ++ g.UseRegister(input1), ++ g.UseImmediate(wasm::SimdShuffle::Pack4Lanes(shuffle32x4))); ++ return; ++ } ++ Emit(kSw64I8x16Shuffle, g.DefineAsRegister(node), g.UseRegister(input0), ++ g.UseRegister(input1), ++ g.UseImmediate(wasm::SimdShuffle::Pack4Lanes(shuffle)), ++ g.UseImmediate(wasm::SimdShuffle::Pack4Lanes(shuffle + 4)), ++ g.UseImmediate(wasm::SimdShuffle::Pack4Lanes(shuffle + 8)), ++ g.UseImmediate(wasm::SimdShuffle::Pack4Lanes(shuffle + 12))); ++} ++#else ++void InstructionSelector::VisitI8x16Shuffle(Node* node) { UNREACHABLE(); } ++#endif // V8_ENABLE_WEBASSEMBLY ++ ++void InstructionSelector::VisitI8x16Swizzle(Node* node) { ++ Sw64OperandGenerator g(this); ++ InstructionOperand temps[] = {g.TempSimd128Register()}; ++ // We don't want input 0 or input 1 to be the same as output, since we will ++ // modify output before do the calculation. ++ Emit(kSw64I8x16Swizzle, g.DefineAsRegister(node), ++ g.UseUniqueRegister(node->InputAt(0)), ++ g.UseUniqueRegister(node->InputAt(1)), arraysize(temps), temps); ++} ++ ++void InstructionSelector::VisitSignExtendWord8ToInt32(Node* node) { ++ Sw64OperandGenerator g(this); ++ Emit(kSw64Sextb, g.DefineAsRegister(node), g.UseRegister(node->InputAt(0))); ++} ++ ++void InstructionSelector::VisitSignExtendWord16ToInt32(Node* node) { ++ Sw64OperandGenerator g(this); ++ Emit(kSw64Sexth, g.DefineAsRegister(node), g.UseRegister(node->InputAt(0))); ++} ++ ++void InstructionSelector::VisitSignExtendWord8ToInt64(Node* node) { ++ Sw64OperandGenerator g(this); ++ Emit(kSw64Sextb, g.DefineAsRegister(node), g.UseRegister(node->InputAt(0))); ++} ++ ++void InstructionSelector::VisitSignExtendWord16ToInt64(Node* node) { ++ Sw64OperandGenerator g(this); ++ Emit(kSw64Sexth, g.DefineAsRegister(node), g.UseRegister(node->InputAt(0))); ++} ++ ++void InstructionSelector::VisitSignExtendWord32ToInt64(Node* node) { ++ Sw64OperandGenerator g(this); ++ Emit(kSw64Sllw, g.DefineAsRegister(node), g.UseRegister(node->InputAt(0)), ++ g.TempImmediate(0)); ++} ++ ++void InstructionSelector::VisitF32x4Pmin(Node* node) { ++ UNREACHABLE(); ++ // VisitUniqueRRR(this, kSw64F32x4Pmin, node); ++} ++ ++void InstructionSelector::VisitF32x4Pmax(Node* node) { ++ UNREACHABLE(); ++ // VisitUniqueRRR(this, kSw64F32x4Pmax, node); ++} ++ ++void InstructionSelector::VisitF64x2Pmin(Node* node) { ++ UNREACHABLE(); ++ // VisitUniqueRRR(this, kSw64F64x2Pmin, node); ++} ++ ++void InstructionSelector::VisitF64x2Pmax(Node* node) { ++ UNREACHABLE(); ++ // VisitUniqueRRR(this, kSw64F64x2Pmax, node); ++} ++ ++#define VISIT_EXT_MUL(OPCODE1, OPCODE2, TYPE) \ ++ void InstructionSelector::Visit##OPCODE1##ExtMulLow##OPCODE2(Node* node) {} \ ++ void InstructionSelector::Visit##OPCODE1##ExtMulHigh##OPCODE2(Node* node) {} ++ ++VISIT_EXT_MUL(I64x2, I32x4S, SWSAS32) ++VISIT_EXT_MUL(I64x2, I32x4U, SWSAU32) ++VISIT_EXT_MUL(I32x4, I16x8S, SWSAS16) ++VISIT_EXT_MUL(I32x4, I16x8U, SWSAU16) ++VISIT_EXT_MUL(I16x8, I8x16S, SWSAS8) ++VISIT_EXT_MUL(I16x8, I8x16U, SWSAU8) ++#undef VISIT_EXT_MUL ++ ++#define VISIT_EXTADD_PAIRWISE(OPCODE, TYPE) \ ++ void InstructionSelector::Visit##OPCODE(Node* node) { \ ++ Sw64OperandGenerator g(this); \ ++ Emit(kSw64ExtAddPairwise, g.DefineAsRegister(node), \ ++ g.UseRegister(node->InputAt(0))); \ ++ } ++VISIT_EXTADD_PAIRWISE(I16x8ExtAddPairwiseI8x16S, SWSAS8) ++VISIT_EXTADD_PAIRWISE(I16x8ExtAddPairwiseI8x16U, SWSAU8) ++VISIT_EXTADD_PAIRWISE(I32x4ExtAddPairwiseI16x8S, SWSAS16) ++VISIT_EXTADD_PAIRWISE(I32x4ExtAddPairwiseI16x8U, SWSAU16) ++#undef VISIT_EXTADD_PAIRWISE ++ ++void InstructionSelector::AddOutputToSelectContinuation(OperandGenerator* g, ++ int first_input_index, ++ Node* node) { ++ UNREACHABLE(); ++} ++ ++// static ++MachineOperatorBuilder::Flags ++InstructionSelector::SupportedMachineOperatorFlags() { ++ MachineOperatorBuilder::Flags flags = MachineOperatorBuilder::kNoFlags; ++ return flags | MachineOperatorBuilder::kWord32Ctz | ++ MachineOperatorBuilder::kWord64Ctz | ++ MachineOperatorBuilder::kWord32Popcnt | ++ MachineOperatorBuilder::kWord64Popcnt | ++ MachineOperatorBuilder::kWord32ShiftIsSafe | ++ MachineOperatorBuilder::kInt32DivIsSafe | ++ MachineOperatorBuilder::kUint32DivIsSafe | ++ MachineOperatorBuilder::kFloat64RoundDown | ++ MachineOperatorBuilder::kFloat32RoundDown | ++ MachineOperatorBuilder::kFloat64RoundUp | ++ MachineOperatorBuilder::kFloat32RoundUp | ++ MachineOperatorBuilder::kFloat64RoundTruncate | ++ MachineOperatorBuilder::kFloat32RoundTruncate | ++ MachineOperatorBuilder::kFloat64RoundTiesEven | ++ MachineOperatorBuilder::kFloat32RoundTiesEven; ++} ++ ++// static ++MachineOperatorBuilder::AlignmentRequirements ++InstructionSelector::AlignmentRequirements() { ++ return MachineOperatorBuilder::AlignmentRequirements:: ++ NoUnalignedAccessSupport(); ++} ++ ++#undef SIMD_BINOP_LIST ++#undef SIMD_SHIFT_OP_LIST ++#undef SIMD_UNOP_LIST ++#undef SIMD_TYPE_LIST ++#undef TRACE_UNIMPL ++#undef TRACE ++ ++} // namespace compiler ++} // namespace internal ++} // namespace v8 +diff --git a/deps/v8/src/compiler/c-linkage.cc b/deps/v8/src/compiler/c-linkage.cc +index 951550c4..9cd5d501 100644 +--- a/deps/v8/src/compiler/c-linkage.cc ++++ b/deps/v8/src/compiler/c-linkage.cc +@@ -90,6 +90,14 @@ namespace { + #define CALLEE_SAVE_REGISTERS s0, s1, s2, s3, s4, s5, s6, s7, s8, fp + #define CALLEE_SAVE_FP_REGISTERS f24, f25, f26, f27, f28, f29, f30, f31 + ++#elif V8_TARGET_ARCH_SW64 ++// =========================================================================== ++// == sw64 =================================================================== ++// =========================================================================== ++#define PARAM_REGISTERS a0, a1, a2, a3, a4, a5 ++#define CALLEE_SAVE_REGISTERS s0, s1, s2, s3, s4, s5 ++#define CALLEE_SAVE_FP_REGISTERS f2, f3, f4, f5, f6, f7, f8, f9 ++ + #elif V8_TARGET_ARCH_PPC64 + // =========================================================================== + // == ppc & ppc64 ============================================================ +diff --git a/deps/v8/src/debug/debug-evaluate.cc b/deps/v8/src/debug/debug-evaluate.cc +index bb9183f6..0085b89e 100644 +--- a/deps/v8/src/debug/debug-evaluate.cc ++++ b/deps/v8/src/debug/debug-evaluate.cc +@@ -1223,7 +1223,7 @@ void DebugEvaluate::VerifyTransitiveBuiltins(Isolate* isolate) { + } + CHECK(!failed); + #if defined(V8_TARGET_ARCH_PPC) || defined(V8_TARGET_ARCH_PPC64) || \ +- defined(V8_TARGET_ARCH_MIPS64) ++ defined(V8_TARGET_ARCH_MIPS64) || defined(V8_TARGET_ARCH_SW64) + // Isolate-independent builtin calls and jumps do not emit reloc infos + // on PPC. We try to avoid using PC relative code due to performance + // issue with especially older hardwares. +diff --git a/deps/v8/src/deoptimizer/sw64/deoptimizer-sw64.cc b/deps/v8/src/deoptimizer/sw64/deoptimizer-sw64.cc +new file mode 100644 +index 00000000..98ca4306 +--- /dev/null ++++ b/deps/v8/src/deoptimizer/sw64/deoptimizer-sw64.cc +@@ -0,0 +1,34 @@ ++// Copyright 2011 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "src/deoptimizer/deoptimizer.h" ++ ++namespace v8 { ++namespace internal { ++ ++const int Deoptimizer::kEagerDeoptExitSize = 2 * kInstrSize; ++const int Deoptimizer::kLazyDeoptExitSize = 2 * kInstrSize; ++ ++Float32 RegisterValues::GetFloatRegister(unsigned n) const { ++ return Float32::FromBits( ++ static_cast(double_registers_[n].get_bits())); ++} ++ ++void FrameDescription::SetCallerPc(unsigned offset, intptr_t value) { ++ SetFrameSlot(offset, value); ++} ++ ++void FrameDescription::SetCallerFp(unsigned offset, intptr_t value) { ++ SetFrameSlot(offset, value); ++} ++ ++void FrameDescription::SetCallerConstantPool(unsigned offset, intptr_t value) { ++ // No embedded constant pool support. ++ UNREACHABLE(); ++} ++ ++void FrameDescription::SetPc(intptr_t pc) { pc_ = pc; } ++ ++} // namespace internal ++} // namespace v8 +diff --git a/deps/v8/src/diagnostics/gdb-jit.cc b/deps/v8/src/diagnostics/gdb-jit.cc +index bc03a189..15515fc0 100644 +--- a/deps/v8/src/diagnostics/gdb-jit.cc ++++ b/deps/v8/src/diagnostics/gdb-jit.cc +@@ -1095,6 +1095,8 @@ class DebugInfoSection : public DebugSection { + UNIMPLEMENTED(); + #elif V8_TARGET_ARCH_LOONG64 + UNIMPLEMENTED(); ++#elif V8_TARGET_ARCH_SW64 ++ UNIMPLEMENTED(); + #elif V8_TARGET_ARCH_PPC64 && V8_OS_LINUX + w->Write(DW_OP_reg31); // The frame pointer is here on PPC64. + #elif V8_TARGET_ARCH_S390 +diff --git a/deps/v8/src/diagnostics/perf-jit.h b/deps/v8/src/diagnostics/perf-jit.h +index 488b5a14..96f530f1 100644 +--- a/deps/v8/src/diagnostics/perf-jit.h ++++ b/deps/v8/src/diagnostics/perf-jit.h +@@ -91,6 +91,7 @@ class PerfJitLogger : public CodeEventLogger { + static const uint32_t kElfMachARM64 = 183; + static const uint32_t kElfMachS390x = 22; + static const uint32_t kElfMachPPC64 = 21; ++ static const uint32_t kElfMachSW64 = 0x9916; + static const uint32_t kElfMachRISCV = 243; + + uint32_t GetElfMach() { +@@ -106,6 +107,8 @@ class PerfJitLogger : public CodeEventLogger { + return kElfMachMIPS64; + #elif V8_TARGET_ARCH_LOONG64 + return kElfMachLOONG64; ++#elif V8_TARGET_ARCH_SW64 ++ return kElfMachSW64; + #elif V8_TARGET_ARCH_ARM64 + return kElfMachARM64; + #elif V8_TARGET_ARCH_S390X +diff --git a/deps/v8/src/diagnostics/sw64/disasm-sw64.cc b/deps/v8/src/diagnostics/sw64/disasm-sw64.cc +new file mode 100644 +index 00000000..b458f5dc +--- /dev/null ++++ b/deps/v8/src/diagnostics/sw64/disasm-sw64.cc +@@ -0,0 +1,1598 @@ ++// Copyright 2012 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// 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. ++// ++// NameConverter converter; ++// Disassembler d(converter); ++// for (byte* pc = begin; pc < end;) { ++// v8::base::EmbeddedVector buffer; ++// byte* 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 ++#include ++#include ++#include ++#include ++#include ++ ++#if V8_TARGET_ARCH_SW64 ++ ++#include "src/base/platform/platform.h" ++#include "src/base/strings.h" ++#include "src/base/vector.h" ++#include "src/codegen/macro-assembler.h" ++#include "src/codegen/sw64/constants-sw64.h" ++#include "src/diagnostics/disasm.h" ++ ++namespace v8 { ++namespace internal { ++ ++//------------------------------------------------------------------------------ ++ ++// 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, ++ v8::base::Vector out_buffer) ++ : converter_(converter), out_buffer_(out_buffer), out_buffer_pos_(0) { ++ out_buffer_[out_buffer_pos_] = '\0'; ++ } ++ ++ ~Decoder() {} ++ ++ Decoder(const Decoder&) = delete; ++ Decoder& operator=(const Decoder&) = delete; ++ ++ // Writes one disassembled instruction into 'buffer' (0-terminated). ++ // Returns the length of the disassembled machine instruction in bytes. ++ int InstructionDecode(byte* instruction); ++ // address decode_instructions(address start, address end); ++ byte* decode_instructions(byte* start, byte* end); ++ // tries to load library and return whether it succedded. ++ static bool load_library(); ++ ++ private: ++ // this is the type of the dll entry point: ++ typedef void* (*decode_func)(void* start, void* end, ++ void* (*event_callback)(void*, const char*, void*), ++ void* event_stream, ++ int (*printf_callback)(void*, const char*, ...), ++ void* printf_stream, ++ const char* options); ++ // points to the library. ++ static void* _library; ++ // bailout ++ static bool _tried_to_load_library; ++ // points to the decode function. ++ static decode_func _decode_instructions; ++ ++ 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); ++ ++ // Handle formatting of instructions and their options. ++ int FormatRegister(Instruction* instr, const char* option); ++ int FormatOption(Instruction* instr, const char* option); ++ void Format(Instruction* instr, const char* format); ++ void Unknown(Instruction* instr); ++ ++ void SwDecodeTypeSyscall(Instruction* instr); ++ void SwDecodeTypeTransferance(Instruction* instr); ++ void SwDecodeTypeStorage(Instruction* instr); ++ void SwDecodeTypeSimpleCalculation(Instruction* instr); ++ void SwDecodeTypeCompositeCalculation(Instruction* instr); ++ int SwGetInstructionRange(const char* format, int& hi, int& lo); ++ ++ void SwPrintRa(Instruction* instr); ++ void SwPrintRb(Instruction* instr); ++ int SwPrintRc(Instruction* instr, const char* format); ++ void SwPrintRd(Instruction* instr); ++ void SwPrintFa(Instruction* instr); ++ void SwPrintFb(Instruction* instr); ++ int SwPrintFc(Instruction* instr, const char* format); ++ void SwPrintFd(Instruction* instr); ++ ++ //Print unsigned immediate value ++ int SwPrintImm(Instruction* instr, const char* format); ++ ++ //Print signed immediate value ++ int SwPrintDisp(Instruction* instr, const char* format); ++ int SwPrintDispTransfer(Instruction* instr, const char* format);//ld 20150320 ++ ++ //Print int to hex ++ int SwPrintHex(Instruction* instr,const char* format);//cjq 20150319 ++ ++private: ++ void SwDecodeTypeCompositeCalculationInteger(Instruction* instr); ++ void SwDecodeTypeCompositeCalculationFloatintPoint(Instruction* instr); ++ ++ const disasm::NameConverter& converter_; ++ v8::base::Vector out_buffer_; ++ int out_buffer_pos_; ++ byte* instr_pc_; //ld 20150323; ++}; ++ ++// 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_ < (out_buffer_.length() - 1))) { ++ PrintChar(cur); ++ cur = *str++; ++ } ++ out_buffer_[out_buffer_pos_] = 0; ++} ++ ++// Print the register name according to the active name converter. ++void Decoder::PrintRegister(int reg) { ++ Print(converter_.NameOfCPURegister(reg)); ++} ++ ++// Print the FPUregister name according to the active name converter. ++void Decoder::PrintFPURegister(int freg) { ++ Print(converter_.NameOfXMMRegister(freg)); ++} ++ ++ ++// Handle all register based formatting in this function to reduce the ++// complexity of FormatOption. ++int Decoder::FormatRegister(Instruction* instr, const char* format) { ++ DCHECK_EQ(format[0], 'r'); ++ if (format[1] == 's') { // 'rs: Rs register. ++ int reg = instr->RsValue(); ++ PrintRegister(reg); ++ return 2; ++ } else if (format[1] == 't') { // 'rt: rt register. ++ int reg = instr->RtValue(); ++ PrintRegister(reg); ++ return 2; ++ } else if (format[1] == 'd') { // 'rd: rd register. ++ int reg = instr->RdValue(); ++ PrintRegister(reg); ++ return 2; ++ } ++ UNREACHABLE(); ++} ++ ++// 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 'r': { ++ switch (format[1]) { ++ case 'a' : ++ SwPrintRa(instr); ++ break; ++ case 'b': ++ SwPrintRb(instr); ++ break; ++ case 'c': ++ return SwPrintRc(instr, format); ++ case 'd': ++ SwPrintRd(instr); ++ break; ++ } ++ return 2; ++ } ++ ++ case 'f': { ++ switch (format[1]) { ++ case 'a' : ++ SwPrintFa(instr); ++ break; ++ case 'b': ++ SwPrintFb(instr); ++ break; ++ case 'c': ++ return SwPrintFc(instr, format); ++ case 'd': ++ SwPrintFd(instr); ++ break; ++ } ++ return 2; ++ } ++ ++ case 'i': ++ return SwPrintImm(instr, format); ++ case 'd': ++ return SwPrintDisp(instr, format); ++ case 't'://ld 20150323 ++ return SwPrintDispTransfer(instr,format); ++ //modified by cjq 20150318 ++ case '0': ++ return SwPrintHex(instr, format); ++ } ++ UNREACHABLE(); ++ } ++ ++// 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'; ++} ++ ++// 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"); } ++ ++#define OP(x) (((x) & 0x3F) << 26) ++#define OPR(oo,ff) (OP(oo) | (((ff) & 0xFF) << 5)) ++ ++void Decoder::SwPrintRa(Instruction* instr){ ++ int reg = instr->SwRaValue(); ++ PrintRegister(reg); ++} ++ ++void Decoder::SwPrintRb(Instruction* instr){ ++ int reg = instr->SwRbValue(); ++ PrintRegister(reg); ++} ++ ++int Decoder::SwPrintRc(Instruction* instr, const char* format) { ++ int len, hi, lo; ++ len = SwGetInstructionRange(format, hi, lo); ++ int reg = instr->SwRcValue(hi, lo); ++ PrintRegister(reg); ++ return len; ++} ++ ++void Decoder::SwPrintRd(Instruction* instr){ ++ int reg = instr->SwRdValue(); ++ PrintRegister(reg); ++} ++void Decoder::SwPrintFa(Instruction* instr){ ++ int reg = instr->SwFaValue(); ++ PrintFPURegister(reg); ++} ++void Decoder::SwPrintFb(Instruction* instr){ ++ int reg = instr->SwFbValue(); ++ PrintFPURegister(reg); ++} ++int Decoder::SwPrintFc(Instruction* instr, const char* format){ ++ int len, hi, lo; ++ len = SwGetInstructionRange(format, hi, lo); ++ int reg = instr->SwFcValue(hi, lo); ++ PrintFPURegister(reg); ++ return len; ++} ++void Decoder::SwPrintFd(Instruction* instr) { ++ int reg = instr->SwFdValue(); ++ PrintFPURegister(reg); ++} ++ ++int Decoder::SwPrintDisp(Instruction* instr, const char* format) { ++ int32_t len, hi, lo; ++ len = SwGetInstructionRange(format, hi, lo); ++ ++ int32_t imm = instr->SwImmOrDispFieldValue(hi, lo); ++ out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", imm); ++ ++ return len; ++} ++//for transfer ld 20150320 ++int Decoder::SwPrintDispTransfer(Instruction* instr, const char* format) { ++ int32_t len, hi, lo; ++ len = SwGetInstructionRange(format, hi, lo); ++ ++ int32_t imm = instr->SwImmOrDispFieldValue(hi, lo); ++ //int64_t imm_transfer = imm*4+Instruction::kInstrSize+(*reinterpret_cast(&instr_pc_)); ++ void* imm_transfer = imm + 1 + reinterpret_cast(instr_pc_); //ld 20150429 ++ out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, " %p", imm_transfer); ++ ++ return len; ++} ++ ++int Decoder::SwPrintImm(Instruction* instr, const char* format) { ++ int32_t len, hi, lo; ++ len = SwGetInstructionRange(format, hi, lo); ++ ++ int32_t imm = instr->SwImmOrDispFieldRaw(hi, lo) >> lo; ++ out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%u", imm); ++ ++ return len; ++} ++ ++//modified by cjq ++/**change int to hex ++ * return length of format string ++ */ ++int Decoder::SwPrintHex(Instruction* instr, const char* format){ ++ int32_t len, hi, lo; ++ len = SwGetInstructionRange(format, hi, lo); ++ ++ int32_t imm = instr->SwImmOrDispFieldRaw(hi, lo) >> lo; ++ out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "0x%x", imm); ++ ++ return len; ++} ++ ++//imm(15-12) disp(25-0) retrun length(format) ++int Decoder::SwGetInstructionRange(const char* format, int& hi, int& lo) { ++ const char* p = format; ++ p = strstr(format, "("); ++ DCHECK('(' == *p ); ++ hi = atoi(++p); ++ p = strstr(format, "-"); ++ DCHECK('-' == *p ); ++ lo = atoi(++p); ++ p = strstr(format, ")"); ++ DCHECK(')' == *p ); ++ return (int)(p-format+1); ++} ++ ++/** modified by cjq ++ * * add function to parse 'sys_call' ++ * */ ++void Decoder::SwDecodeTypeSyscall(Instruction* instr){ ++ if (instr->OpcodeFieldValue() == op_sys_call){ ++ return Format(instr, "sys_call '0x(25-0)"); ++ } ++} ++ ++void Decoder::SwDecodeTypeTransferance(Instruction* instr){//ld 20150319 ++ switch(instr->OpcodeFieldValue()){ ++ case op_br: ++ Format(instr,"br 'ra, 'tr_disp(20-0)");//ld 20150320 ++ break; ++ case op_bsr: ++ Format(instr,"bsr 'ra, 'tr_disp(20-0)"); ++ break; ++ case op_beq: ++ Format(instr,"beq 'ra, 'tr_disp(20-0)"); ++ break; ++ case op_bne: ++ Format(instr,"bne 'ra, 'tr_disp(20-0)"); ++ break; ++ case op_blt: ++ Format(instr,"blt 'ra, 'tr_disp(20-0)"); ++ break; ++ case op_ble: ++ Format(instr,"ble 'ra, 'tr_disp(20-0)"); ++ break; ++ case op_bgt: ++ Format(instr,"bgt 'ra, 'tr_disp(20-0)"); ++ break; ++ case op_bge: ++ Format(instr,"bge 'ra, 'tr_disp(20-0)"); ++ break; ++ case op_blbc: ++ Format(instr,"blbc 'ra, 'tr_disp(20-0)"); ++ break; ++ case op_blbs: ++ Format(instr,"blbs 'ra, 'tr_disp(20-0)"); ++ break; ++ case op_fbeq: ++ Format(instr,"fbeq 'fa, 'tr_disp(20-0)"); ++ break; ++ case op_fbne: ++ Format(instr,"fbne 'fa, 'tr_disp(20-0)"); ++ break; ++ case op_fblt: ++ Format(instr,"fblt 'fa, 'tr_disp(20-0)"); ++ break; ++ case op_fble: ++ Format(instr,"fble 'fa, 'tr_disp(20-0)"); ++ break; ++ case op_fbgt: ++ Format(instr,"fbgt 'fa, 'tr_disp(20-0)"); ++ break; ++ case op_fbge: ++ Format(instr,"fbge 'fa, 'tr_disp(20-0)"); ++ break; ++ default: ++ printf("a 0x%x \n", instr->OpcodeFieldRaw()); ++ UNREACHABLE(); ++ } ++} ++void Decoder::SwDecodeTypeStorage(Instruction* instr){ ++ int opcode_func_raw; ++ switch (instr->OpcodeFieldValue()) { ++ case op_call: ++ Format(instr, "call 'ra, ('rb)"); ++ break; ++ case op_ret: ++ Format(instr, "ret 'ra, ('rb)"); ++ break; ++ case op_jmp: ++ Format(instr, "jmp 'ra, ('rb)"); ++ break; ++ ++ case op_ldbu: ++ Format(instr, "ldbu 'ra, 'disp(15-0)('rb)"); ++ break; ++ case op_ldhu: ++ Format(instr, "ldhu 'ra, 'disp(15-0)('rb)"); ++ break; ++ case op_ldw: ++ Format(instr, "ldw 'ra, 'disp(15-0)('rb)"); ++ break; ++ case op_ldl: ++ Format(instr, "ldl 'ra, 'disp(15-0)('rb)"); ++ break; ++ case op_ldl_u: ++ Format(instr, "ldl_u 'ra, 'disp(15-0)('rb)"); ++ break; ++ case op_stb: ++ Format(instr, "stb 'ra, 'disp(15-0)('rb)"); ++ break; ++ case op_sth: ++ Format(instr, "sth 'ra, 'disp(15-0)('rb)"); ++ break; ++ case op_stw: ++ Format(instr, "stw 'ra, 'disp(15-0)('rb)"); ++ break; ++ case op_stl: ++ Format(instr, "stl 'ra, 'disp(15-0)('rb)"); ++ break; ++ case op_stl_u: ++ Format(instr, "stl_u 'ra, 'disp(15-0)('rb)"); ++ break; ++ case op_ldi: ++ Format(instr, "ldi 'ra, 'disp(15-0)('rb)"); ++ break; ++ case op_ldih: ++ Format(instr, "ldih 'ra, 'disp(15-0)('rb)"); ++ break; ++ ++ case op_flds: ++ Format(instr, "flds 'fa, 'disp(15-0)('rb)"); ++ break; ++ case op_fldd: ++ Format(instr, "fldd 'fa, 'disp(15-0)('rb)"); ++ break; ++ case op_fsts: ++ Format(instr, "fsts 'fa, 'disp(15-0)('rb)"); ++ break; ++ case op_fstd: ++ Format(instr, "fstd 'fa, 'disp(15-0)('rb)"); ++ break; ++ ++ case op_ldwe: ++ case op_ldse: ++ case op_ldde: ++ case op_vlds: ++ case op_vldd: ++ case op_vsts: ++ case op_vstd: ++ UNIMPLEMENTED_SW64(); ++ break; ++ ++ case OP(0x08): ++ case OP(0x06): ++ opcode_func_raw = instr->SwFunctionFieldRaw(15, 0) | instr->OpcodeFieldRaw(); ++ switch (opcode_func_raw) { ++ case op_lldw: ++ Format(instr, "lldw 'ra, 'disp(11-0)('rb)"); ++ break; ++ case op_lldl: ++ Format(instr, "lldl 'ra, 'disp(11-0)('rb)"); ++ break; ++ case op_ldw_inc: ++ Format(instr, "ldw_inc 'ra, 'disp(11-0)('rb)"); ++ break; ++ case op_ldl_inc: ++ Format(instr, "ldl_inc 'ra, 'disp(11-0)('rb)"); ++ break; ++ case op_ldw_dec: ++ Format(instr, "ldw_dec 'ra, 'disp(11-0)('rb)"); ++ break; ++ case op_ldl_dec: ++ Format(instr, "ldl_dec 'ra, 'disp(11-0)('rb)"); ++ break; ++ case op_ldw_set: ++ Format(instr, "ldw_set 'ra, 'disp(11-0)('rb)"); ++ break; ++ case op_ldl_set: ++ Format(instr, "ldl_set 'ra, 'disp(11-0)('rb)"); ++ break; ++ case op_lstw: ++ Format(instr, "lstw 'ra, 'disp(11-0)('rb)"); ++ break; ++ case op_lstl: ++ Format(instr, "lstl 'ra, 'disp(11-0)('rb)"); ++ break; ++ case op_ldbu_a: ++ Format(instr, "ldbu_a 'ra, 'disp(11-0)('rb)"); ++ break; ++ case op_ldhu_a: ++ Format(instr, "ldhu_a 'ra, 'disp(11-0)('rb)"); ++ break; ++ case op_ldw_a: ++ Format(instr, "ldw_a 'ra, 'disp(11-0)('rb)"); ++ break; ++ case op_ldl_a: ++ Format(instr, "ldl_a 'ra, 'disp(11-0)('rb)"); ++ break; ++ case op_flds_a: ++ Format(instr, "flds_a 'ra, 'disp(11-0)('rb)"); ++ break; ++ case op_fldd_a: ++ Format(instr, "fldd_a 'ra, 'disp(11-0)('rb)"); ++ break; ++ case op_stb_a: ++ Format(instr, "stb_a 'ra, 'disp(11-0)('rb)"); ++ break; ++ case op_sth_a: ++ Format(instr, "sth_a 'ra, 'disp(11-0)('rb)"); ++ break; ++ case op_stw_a: ++ Format(instr, "stw_a 'ra, 'disp(11-0)('rb)"); ++ break; ++ case op_stl_a: ++ Format(instr, "stl_a 'ra, 'disp(11-0)('rb)"); ++ break; ++ case op_fsts_a: ++ Format(instr, "fsts_a 'ra, 'disp(11-0)('rb)"); ++ break; ++ case op_fstd_a: ++ Format(instr, "fstd_a 'ra, 'disp(11-0)('rb)"); ++ break; ++ case op_memb: ++ Format(instr, "memb"); ++ break; ++ case op_imemb: ++ Format(instr, "imemb"); ++ break; ++ case op_wmemb: ++ Format(instr, "wmemb"); ++ break; ++ case op_rtc: ++ Format(instr, "rtc 'ra, 'rb"); ++ break; ++ case op_rcid: ++ Format(instr, "rcid 'ra"); ++ break; ++ case op_halt: ++ Format(instr, "halt"); ++ break; ++ case op_rd_f: ++ Format(instr, "rd_f 'ra"); ++ break; ++ case op_wr_f: ++ Format(instr, "wr_f, 'ra"); ++ break; ++ default: ++ UNIMPLEMENTED_SW64(); ++ } ++ break; ++ ++ default: ++ printf("a 0x%x \n", instr->OpcodeFieldRaw()); ++ UNREACHABLE(); ++ } ++} ++void Decoder::SwDecodeTypeSimpleCalculation(Instruction* instr){ ++ int simple_calculation_op = instr->SwFunctionFieldRaw(12, 5) | instr->OpcodeFieldValue(); ++ switch (simple_calculation_op) { ++ case op_addw: ++ Format(instr, "addw 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_subw: ++ Format(instr, "subw 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_s4addw: ++ Format(instr, "s4addw 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_s4subw: ++ Format(instr, "s4subw 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_s8addw: ++ Format(instr, "s8addw 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_s8subw: ++ Format(instr, "s8subw 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_addl: ++ Format(instr, "addl 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_subl: ++ Format(instr, "subl 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_s4addl: ++ Format(instr, "s4addl 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_s4subl: ++ Format(instr, "s4subl 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_s8addl: ++ Format(instr, "s8addl 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_s8subl: ++ Format(instr, "s8subl 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_mulw: ++ Format(instr, "mulw 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_mull: ++ Format(instr, "mull 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_divw: ++ Format(instr, "divw 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_udivw: ++ Format(instr, "udivw 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_remw: ++ Format(instr, "divw 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_uremw: ++ Format(instr, "udivw 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_divl: ++ Format(instr, "divw 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_udivl: ++ Format(instr, "udivw 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_reml: ++ Format(instr, "divw 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_ureml: ++ Format(instr, "udivw 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_addpi: ++ Format(instr, "addpi 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_addpis: ++ Format(instr, "addpis 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_sbt: ++ Format(instr, "sbt 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_cbt: ++ Format(instr, "cbt 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_umulh: ++ Format(instr, "umulh 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_cmpeq: ++ Format(instr, "cmpeq 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_cmplt: ++ Format(instr, "cmplt 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_cmple: ++ Format(instr, "cmple 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_cmpult: ++ Format(instr, "cmpult 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_cmpule: ++ Format(instr, "cmpule 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_and: ++ Format(instr, "and 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_bic: ++ Format(instr, "bic 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_bis: //case op_or: ++ Format(instr, "or 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_ornot: ++ Format(instr, "ornot 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_xor: ++ Format(instr, "xor 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_eqv: ++ Format(instr, "eqv 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_inslb: ++ Format(instr, "inslb 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_inslh: ++ Format(instr, "inslh 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_inslw: ++ Format(instr, "inslw 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_insll: ++ Format(instr, "insll 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_inshb: ++ Format(instr, "inshb 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_inshh: ++ Format(instr, "inshh 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_inshw: ++ Format(instr, "inshw 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_inshl: ++ Format(instr, "inshl 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_slll: ++ Format(instr, "slll 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_srll: ++ Format(instr, "srll 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_sral: ++ Format(instr, "sral 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_roll: ++ Format(instr, "roll 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_sllw: ++ Format(instr, "sllw 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_srlw: ++ Format(instr, "srlw 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_sraw: ++ Format(instr, "sraw 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_rolw: ++ Format(instr, "rolw 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_extlb: ++ Format(instr, "extlb 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_extlh: ++ Format(instr, "extlh 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_extlw: ++ Format(instr, "extlw 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_extll: ++ Format(instr, "extll 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_exthb: ++ Format(instr, "exthb 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_exthh: ++ Format(instr, "exthh 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_exthw: ++ Format(instr, "exthw 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_exthl: ++ Format(instr, "exthl 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_ctpop: ++ Format(instr, "ctpop 'rb, 'rc(4-0)"); ++ break; ++ case op_ctlz: ++ Format(instr, "ctlz 'rb, 'rc(4-0)"); ++ break; ++ case op_cttz: ++ Format(instr, "cttz 'rb, 'rc(4-0)"); ++ break; ++ case op_revbh: ++ Format(instr, "revbh 'rb, 'rc(4-0)"); ++ break; ++ case op_revbw: ++ Format(instr, "revbw 'rb, 'rc(4-0)"); ++ break; ++ case op_revbl: ++ Format(instr, "revbl 'rb, 'rc(4-0)"); ++ break; ++ case op_casw: ++ Format(instr, "casw 'rb, 'rc(4-0)"); ++ break; ++ case op_casl: ++ Format(instr, "casl 'rb, 'rc(4-0)"); ++ break; ++ case op_masklb: ++ Format(instr, "masklb 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_masklh: ++ Format(instr, "masklh 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_masklw: ++ Format(instr, "masklw 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_maskll: ++ Format(instr, "maskll 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_maskhb: ++ Format(instr, "maskhb 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_maskhh: ++ Format(instr, "maskhh 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_maskhw: ++ Format(instr, "maskhw 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_maskhl: ++ Format(instr, "maskhl 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_zap: ++ Format(instr, "zap 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_zapnot: ++ Format(instr, "zapnot 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_sextb: ++ Format(instr, "sextb 'rb, 'rc(4-0)"); ++ break; ++ case op_sexth: ++ Format(instr, "sexth 'rb, 'rc(4-0)"); ++ break; ++ case op_cmpgeb: ++ Format(instr, "cmpgeb 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_fimovs: ++ Format(instr, "fimovs 'fa, 'rc(4-0)"); ++ break; ++ case op_fimovd: ++ Format(instr, "fimovd 'fa, 'rc(4-0)"); ++ break; ++ ++ case op_addw_l: ++ Format(instr, "addw 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_subw_l: ++ Format(instr, "subw 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_s4addw_l: ++ Format(instr, "s4addw 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_s4subw_l: ++ Format(instr, "s4subw 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_s8addw_l: ++ Format(instr, "s8addw 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_s8subw_l: ++ Format(instr, "s8subw 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_addl_l: ++ Format(instr, "addl 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_subl_l: ++ Format(instr, "subl 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_s4addl_l: ++ Format(instr, "s4addl 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_s4subl_l: ++ Format(instr, "s4subl 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_s8addl_l: ++ Format(instr, "s8addl 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_s8subl_l: ++ Format(instr, "s8subl 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_mulw_l: ++ Format(instr, "mulw 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_mull_l: ++ Format(instr, "mull 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_umulh_l: ++ Format(instr, "umulh 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_cmpeq_l: ++ Format(instr, "cmpeq 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_cmplt_l: ++ Format(instr, "cmplt 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_cmple_l: ++ Format(instr, "cmple 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_cmpult_l: ++ Format(instr, "cmpult 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_cmpule_l: ++ Format(instr, "cmpule 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_and_l: ++ Format(instr, "and 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_bic_l: ++ Format(instr, "bic 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_bis_l: //case op_or_l: ++ Format(instr, "or 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_ornot_l: ++ Format(instr, "ornot 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_xor_l: ++ Format(instr, "xor 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_eqv_l: ++ Format(instr, "eqv 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_inslb_l: ++ Format(instr, "inslb 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_inslh_l: ++ Format(instr, "inslh 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_inslw_l: ++ Format(instr, "inslw 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_insll_l: ++ Format(instr, "insll 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_inshb_l: ++ Format(instr, "inshb 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_inshh_l: ++ Format(instr, "inshh 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_inshw_l: ++ Format(instr, "inshw 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_inshl_l: ++ Format(instr, "inshl 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_slll_l: ++ Format(instr, "slll 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_srll_l: ++ Format(instr, "srll 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_sral_l: ++ Format(instr, "sral 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_roll_l: ++ Format(instr, "roll 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_sllw_l: ++ Format(instr, "sllw 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_srlw_l: ++ Format(instr, "srlw 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_sraw_l: ++ Format(instr, "sraw 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_rolw_l: ++ Format(instr, "rolw 'ra, 'rb, 'rc(4-0)"); ++ break; ++ case op_extlb_l: ++ Format(instr, "extlb 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_extlh_l: ++ Format(instr, "extlh 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_extlw_l: ++ Format(instr, "extlw 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_extll_l: ++ Format(instr, "extll 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_exthb_l: ++ Format(instr, "exthb 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_exthh_l: ++ Format(instr, "exthh 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_exthw_l: ++ Format(instr, "exthw 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_exthl_l: ++ Format(instr, "exthl 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_masklb_l: ++ Format(instr, "masklb 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_masklh_l: ++ Format(instr, "masklh 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_masklw_l: ++ Format(instr, "masklw 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_maskll_l: ++ Format(instr, "maskll 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_maskhb_l: ++ Format(instr, "maskhb 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_maskhh_l: ++ Format(instr, "maskhh 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_maskhw_l: ++ Format(instr, "maskhw 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_maskhl_l: ++ Format(instr, "maskhl 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_zap_l: ++ Format(instr, "zap 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_zapnot_l: ++ Format(instr, "zapnot 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_sextb_l: ++ Format(instr, "sextb 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_sexth_l: ++ Format(instr, "sexth 'imm(20-13), 'rc(4-0)"); ++ break; ++ case op_cmpgeb_l: ++ Format(instr, "cmpgeb 'ra, 'imm(20-13), 'rc(4-0)"); ++ break; ++ ++ case op_fadds: ++ Format(instr, "fadds 'fa, 'fb, 'fc(4-0)"); ++ break; ++ case op_faddd: ++ Format(instr, "faddd 'fa, 'fb, 'fc(4-0)"); ++ break; ++ case op_fsubs: ++ Format(instr, "fsubs 'fa, 'fb, 'fc(4-0)"); ++ break; ++ case op_fsubd: ++ Format(instr, "fsubd 'fa, 'fb, 'fc(4-0)"); ++ break; ++ case op_fmuls: ++ Format(instr, "fmuls 'fa, 'fb, 'fc(4-0)"); ++ break; ++ case op_fmuld: ++ Format(instr, "fmuld 'fa, 'fb, 'fc(4-0)"); ++ break; ++ case op_fdivs: ++ Format(instr, "fdivs 'fa, 'fb, 'fc(4-0)"); ++ break; ++ case op_fdivd: ++ Format(instr, "fdivd 'fa, 'fb, 'fc(4-0)"); ++ break; ++ case op_fsqrts: ++ Format(instr, "fsqrts 'fb, 'fc(4-0)"); ++ break; ++ case op_fsqrtd: ++ Format(instr, "fsqrtd 'fb, 'fc(4-0)"); ++ break; ++ case op_fcmpeq: ++ Format(instr, "fcmpeq 'fa, 'fb, 'fc(4-0)"); ++ break; ++ case op_fcmple: ++ Format(instr, "fcmple 'fa, 'fb, 'fc(4-0)"); ++ break; ++ case op_fcmplt: ++ Format(instr, "fcmplt 'fa, 'fb, 'fc(4-0)"); ++ break; ++ case op_fcmpun: ++ Format(instr, "fcmpun 'fa, 'fb, 'fc(4-0)"); ++ break; ++ case op_fcvtsd: ++ Format(instr, "fcvtsd 'fb, 'fc(4-0)"); ++ break; ++ case op_fcvtds: ++ Format(instr, "fcvtds 'fb, 'fc(4-0)"); ++ break; ++ case op_fcvtdl_g: ++ Format(instr, "fcvtdl_g 'fb, 'fc(4-0)"); ++ break; ++ case op_fcvtdl_p: ++ Format(instr, "fcvtdl_p 'fb, 'fc(4-0)"); ++ break; ++ case op_fcvtdl_z: ++ Format(instr, "fcvtdl_z 'fb, 'fc(4-0)"); ++ break; ++ case op_fcvtdl_n: ++ Format(instr, "fcvtdl_n 'fb, 'fc(4-0)"); ++ break; ++ case op_fcvtdl: ++ Format(instr, "fcvtdl 'fb, 'fc(4-0)"); ++ break; ++ case op_fcvtwl: ++ Format(instr, "fcvtwl 'fb, 'fc(4-0)"); ++ break; ++ case op_fcvtlw: ++ Format(instr, "fcvtlw 'fb, 'fc(4-0)"); ++ break; ++ case op_fcvtls: ++ Format(instr, "fcvtls 'fb, 'fc(4-0)"); ++ break; ++ case op_fcvtld: ++ Format(instr, "fcvtld 'fb, 'fc(4-0)"); ++ break; ++ case op_cmovdl: ++ Format(instr, "cmovdl 'fb, 'fc(4-0)"); ++ break; ++ case op_cmovdl_g: ++ Format(instr, "cmovdl_g 'fb, 'fc(4-0)"); ++ break; ++ case op_cmovdl_p: ++ Format(instr, "cmovdl_p 'fb, 'fc(4-0)"); ++ break; ++ case op_cmovdl_z: ++ Format(instr, "cmovdl_z 'fb, 'fc(4-0)"); ++ break; ++ case op_cmovdl_n: ++ Format(instr, "cmovdl_n 'fb, 'fc(4-0)"); ++ break; ++ case op_cmovdlu: ++ Format(instr, "cmovdlu 'fb, 'fc(4-0)"); ++ break; ++ case op_cmovdlu_g: ++ Format(instr, "cmovdlu_g 'fb, 'fc(4-0)"); ++ break; ++ case op_cmovdlu_p: ++ Format(instr, "cmovdlu_p 'fb, 'fc(4-0)"); ++ break; ++ case op_cmovdlu_z: ++ Format(instr, "cmovdlu_z 'fb, 'fc(4-0)"); ++ break; ++ case op_cmovdlu_n: ++ Format(instr, "cmovdlu_n 'fb, 'fc(4-0)"); ++ break; ++ case op_cmovdw: ++ Format(instr, "cmovdw 'fb, 'fc(4-0)"); ++ break; ++ case op_cmovdw_g: ++ Format(instr, "cmovdw_g 'fb, 'fc(4-0)"); ++ break; ++ case op_cmovdw_p: ++ Format(instr, "cmovdw_p 'fb, 'fc(4-0)"); ++ break; ++ case op_cmovdw_z: ++ Format(instr, "cmovdw_z 'fb, 'fc(4-0)"); ++ break; ++ case op_cmovdw_n: ++ Format(instr, "cmovdw_n 'fb, 'fc(4-0)"); ++ break; ++ case op_cmovdwu: ++ Format(instr, "cmovdwu 'fb, 'fc(4-0)"); ++ break; ++ case op_cmovdwu_g: ++ Format(instr, "cmovdwu_g 'fb, 'fc(4-0)"); ++ break; ++ case op_cmovdwu_p: ++ Format(instr, "cmovdwu_p 'fb, 'fc(4-0)"); ++ break; ++ case op_cmovdwu_z: ++ Format(instr, "cmovdwu_z 'fb, 'fc(4-0)"); ++ break; ++ case op_cmovdwu_n: ++ Format(instr, "cmovdwu_n 'fb, 'fc(4-0)"); ++ break; ++ case op_cmovls: ++ Format(instr, "cmovls 'fb, 'fc(4-0)"); ++ break; ++ case op_cmovws: ++ Format(instr, "cmovws 'fb, 'fc(4-0)"); ++ break; ++ case op_cmovld: ++ Format(instr, "cmovld 'fb, 'fc(4-0)"); ++ break; ++ case op_cmovwd: ++ Format(instr, "cmovwd 'fb, 'fc(4-0)"); ++ break; ++ case op_cmovuls: ++ Format(instr, "cmovuls 'fb, 'fc(4-0)"); ++ break; ++ case op_cmovuws: ++ Format(instr, "cmovuws 'fb, 'fc(4-0)"); ++ break; ++ case op_cmovuld: ++ Format(instr, "cmovuld 'fb, 'fc(4-0)"); ++ break; ++ case op_cmovuwd: ++ Format(instr, "cmovuwd 'fb, 'fc(4-0)"); ++ break; ++ case op_fcpys: ++ Format(instr, "fcpys 'fa, 'fb, 'fc(4-0)"); ++ break; ++ case op_fcpyse: ++ Format(instr, "fcpyse 'fa, 'fb, 'fc(4-0)"); ++ break; ++ case op_fcpysn: ++ Format(instr, "fcpysn 'fa, 'fb, 'fc(4-0)"); ++ break; ++ case op_ifmovs: ++ Format(instr, "ifmovs 'ra, 'fc(4-0)"); ++ break; ++ case op_ifmovd: ++ Format(instr, "ifmovd 'ra, 'fc(4-0)"); ++ break; ++ case op_rfpcr: ++ Format(instr, "rfpcr 'fa, FPCR"); ++ break; ++ case op_wfpcr: ++ Format(instr, "wfpcr 'fa, FPCR"); ++ break; ++ case op_setfpec0: ++ Format(instr, "setfpec0"); ++ break; ++ case op_setfpec1: ++ Format(instr, "setfpec1"); ++ break; ++ case op_setfpec2: ++ Format(instr, "setfpec2"); ++ break; ++ case op_setfpec3: ++ Format(instr, "setfpec3"); ++ break; ++ case op_frecs: ++ Format(instr, "frecs 'ra, 'fc(4-0)"); ++ break; ++ case op_frecd: ++ Format(instr, "frecd 'ra, 'fc(4-0)"); ++ break; ++ case op_fris: ++ Format(instr, "fris 'ra, 'fc(4-0)"); ++ break; ++ case op_fris_g: ++ Format(instr, "fris_g 'ra, 'fc(4-0)"); ++ break; ++ case op_fris_p: ++ Format(instr, "fris_p 'ra, 'fc(4-0)"); ++ break; ++ case op_fris_z: ++ Format(instr, "fris_z 'ra, 'fc(4-0)"); ++ break; ++ case op_fris_n: ++ Format(instr, "fris_n 'ra, 'fc(4-0)"); ++ break; ++ case op_frid: ++ Format(instr, "frid 'ra, 'fc(4-0)"); ++ break; ++ case op_frid_g: ++ Format(instr, "fris_g 'ra, 'fc(4-0)"); ++ break; ++ case op_frid_p: ++ Format(instr, "fris_p 'ra, 'fc(4-0)"); ++ break; ++ case op_frid_z: ++ Format(instr, "fris_z 'ra, 'fc(4-0)"); ++ break; ++ case op_frid_n: ++ Format(instr, "fris_n 'ra, 'fc(4-0)"); ++ break; ++ default: ++ printf("a 0x%x \n", instr->OpcodeFieldRaw()); ++ UNREACHABLE(); ++ } ++} ++void Decoder::SwDecodeTypeCompositeCalculation(Instruction* instr){ ++ switch (instr->OpcodeFieldValue()) { ++ case OP(0x11): ++ case OP(0x13): ++ SwDecodeTypeCompositeCalculationInteger(instr); ++ break; ++ case OP(0x19): ++ SwDecodeTypeCompositeCalculationFloatintPoint(instr); ++ break; ++ } ++} ++ ++void Decoder::SwDecodeTypeCompositeCalculationInteger(Instruction* instr) { ++ int composite_calculation_op = instr->SwFunctionFieldRaw(12, 10) | instr->OpcodeFieldValue(); ++ switch (composite_calculation_op) { ++ case op_seleq: ++ Format(instr, "seleq 'ra, 'rb, 'rc(9-5), 'rd"); ++ break; ++ case op_selge: ++ Format(instr, "selge 'ra, 'rb, 'rc(9-5), 'rd"); ++ break; ++ case op_selgt: ++ Format(instr, "selgt 'ra, 'rb, 'rc(9-5), 'rd"); ++ break; ++ case op_selle: ++ Format(instr, "selle 'ra, 'rb, 'rc(9-5), 'rd"); ++ break; ++ case op_sellt: ++ Format(instr, "sellt 'ra, 'rb, 'rc(9-5), 'rd"); ++ break; ++ case op_selne: ++ Format(instr, "selne 'ra, 'rb, 'rc(9-5), 'rd"); ++ break; ++ case op_sellbc: ++ Format(instr, "sellbc 'ra, 'rb, 'rc(9-5), 'rd"); ++ break; ++ case op_sellbs: ++ Format(instr, "sellbs 'ra, 'rb, 'rc(9-5), 'rd"); ++ break; ++ case op_seleq_l: ++ Format(instr, "seleq 'ra, 'imm(20-13), 'rc(9-5), 'rd"); ++ break; ++ case op_selge_l: ++ Format(instr, "selge 'ra, 'imm(20-13), 'rc(9-5), 'rd"); ++ break; ++ case op_selgt_l: ++ Format(instr, "selgt 'ra, 'imm(20-13), 'rc(9-5), 'rd"); ++ break; ++ case op_selle_l: ++ Format(instr, "selle 'ra, 'imm(20-13), 'rc(9-5), 'rd"); ++ break; ++ case op_sellt_l: ++ Format(instr, "sellt 'ra, 'imm(20-13), 'rc(9-5), 'rd"); ++ break; ++ case op_selne_l: ++ Format(instr, "selne 'ra, 'imm(20-13), 'rc(9-5), 'rd"); ++ break; ++ case op_sellbc_l: ++ Format(instr, "sellbc 'ra, 'imm(20-13), 'rc(9-5), 'rd"); ++ break; ++ case op_sellbs_l: ++ Format(instr, "sellbs 'ra, 'imm(20-13), 'rc(9-5), 'rd"); ++ break; ++ ++ default: ++ printf("a 0x%x \n", instr->OpcodeFieldRaw()); ++ UNREACHABLE(); ++ } ++} ++ ++void Decoder::SwDecodeTypeCompositeCalculationFloatintPoint(Instruction* instr) { ++ int composite_fp_calculation_op = instr->SwFunctionFieldRaw(15, 10) | instr->OpcodeFieldValue(); ++ switch (composite_fp_calculation_op) { ++ case op_fmas: ++ Format(instr, "fmas 'fa, 'fb, 'fc(9-5), 'fd"); ++ break; ++ case op_fmad: ++ Format(instr, "fmad 'fa, 'fb, 'fc(9-5), 'fd"); ++ break; ++ case op_fmss: ++ Format(instr, "fmss 'fa, 'fb, 'fc(9-5), 'fd"); ++ break; ++ case op_fmsd: ++ Format(instr, "fmsd 'fa, 'fb, 'fc(9-5), 'fd"); ++ break; ++ case op_fnmas: ++ Format(instr, "fnmas 'fa, 'fb, 'fc(9-5), 'fd"); ++ break; ++ case op_fnmad: ++ Format(instr, "fnmad 'fa, 'fb, 'fc(9-5), 'fd"); ++ break; ++ case op_fnmss: ++ Format(instr, "fnmss 'fa, 'fb, 'fc(9-5), 'fd"); ++ break; ++ case op_fnmsd: ++ Format(instr, "fnmsd 'fa, 'fb, 'fc(9-5), 'fd"); ++ break; ++ case op_fseleq: ++ Format(instr, "fseleq 'fa, 'fb, 'fc(9-5), 'fd"); ++ break; ++ case op_fselne: ++ Format(instr, "fselne 'fa, 'fb, 'fc(9-5), 'fd"); ++ break; ++ case op_fsellt: ++ Format(instr, "fsellt 'fa, 'fb, 'fc(9-5), 'fd"); ++ break; ++ case op_fselle: ++ Format(instr, "fselle 'fa, 'fb, 'fc(9-5), 'fd"); ++ break; ++ case op_fselgt: ++ Format(instr, "fselgt 'fa, 'fb, 'fc(9-5), 'fd"); ++ break; ++ case op_fselge: ++ Format(instr, "fselge 'fa, 'fb, 'fc(9-5), 'fd"); ++ break; ++ ++ default: ++ printf("a 0x%x \n", instr->OpcodeFieldRaw()); ++ UNREACHABLE(); ++ } ++} ++ ++#undef OP ++#undef OPR ++ ++// Disassemble the instruction at *instr_ptr into the output buffer. ++// All instructions are one word long, except for the simulator ++// pseudo-instruction stop(msg). For that one special case, we return ++// size larger than one kInstrSize. ++int Decoder::InstructionDecode(byte* instr_ptr) { ++ Instruction* instr = Instruction::At(instr_ptr); ++ instr_pc_ = instr_ptr; //ld 20150323 ++ // Print raw instruction bytes. ++ out_buffer_pos_ += base::SNPrintF(out_buffer_ + out_buffer_pos_, ++ "%08x ", instr->InstructionBits()); ++ switch (instr->InstructionType()) { ++ case Instruction::kSwStorageType: { ++ SwDecodeTypeStorage(instr); ++ break; ++ } ++ case Instruction::kSwSimpleCalculationType: { ++ SwDecodeTypeSimpleCalculation(instr); ++ break; ++ } ++ case Instruction::kSwTransferanceType:{ ++ SwDecodeTypeTransferance(instr);//ld 20150319 ++ break; ++ } ++ case Instruction::kSwCompositeCalculationType: { ++ SwDecodeTypeCompositeCalculation(instr); ++ break; ++ } ++//cjq 20150317:TODO ++ case Instruction::kSwSyscallType:{ ++ SwDecodeTypeSyscall(instr); ++ break; ++ } ++ case Instruction::kSwSimulatorTrap:{ ++ Format(instr, "op_trap '0x(25-0)"); ++ break; ++ } ++ default: { ++ Format(instr, "UNSUPPORTED"); ++ UNSUPPORTED_SW64(); ++ } ++ } ++ return kInstrSize; ++} ++ ++byte* Decoder::decode_instructions(byte* start, byte* end) { ++ // decode a series of instructions and return the end of the last instruction ++ ++ return (uint8_t*)(*Decoder::_decode_instructions)( ++ start, end, NULL, (void*)this, NULL, (void*)this, NULL /*options()*/); ++} ++ ++void* Decoder::_library = NULL; ++bool Decoder::_tried_to_load_library = false; ++ ++// This routine is in the shared library: ++Decoder::decode_func Decoder::_decode_instructions = NULL; ++ ++//static const char hsdis_library_name[] = "hsdis-sw64"; ++static const char decode_instructions_name[] = "decode_instructions"; ++ ++/* Used to protect dlsym() calls */ ++static pthread_mutex_t dl_mutex; ++ ++void* dll_load(const char *filename, char *ebuf, int ebuflen) { ++ void* result= dlopen(filename, RTLD_LAZY); ++ if (result != NULL) { ++ // Successful loading ++ return result; ++ } ++ ++ return NULL; ++} ++ ++void* dll_lookup(void* handle, const char* name) { ++ pthread_mutex_lock(&dl_mutex); ++ void* res = dlsym(handle, name); ++ pthread_mutex_unlock(&dl_mutex); ++ return res; ++} ++ ++bool Decoder::load_library() { ++ if (_decode_instructions != NULL) { ++ // Already succeeded. ++ return true; ++ } ++ ++ if (_tried_to_load_library) { ++ // Do not try twice. ++ // To force retry in debugger: assign _tried_to_load_library=0 ++ return false; ++ } ++ ++ // Try to load it. ++ // v8::internal::Decoder d(converter_, buffer); ++ char ebuf[1024]; ++ char buf[4096]; ++ // Find the disassembler shared library. ++ // 4. hsdis-.so (using LD_LIBRARY_PATH) ++ if (_library == NULL) { ++ // 4. hsdis-.so (using LD_LIBRARY_PATH) ++ strcpy(&buf[0], "./hsdis-sw64.so"); ++ _library = dll_load(buf, ebuf, sizeof ebuf); ++ } ++ if (_library != NULL) { ++ _decode_instructions = decode_func((byte*) (dll_lookup(_library, decode_instructions_name)) ); ++ } ++ _tried_to_load_library = true; ++ ++ if (_decode_instructions == NULL) { ++ v8::internal::PrintF("Could not load %s; %s\n", buf, ((_library != NULL) ? "entry point is missing" ++ : "library not loadable")); ++ return false; ++ } ++ ++ // Success. ++ v8::internal::PrintF("Loaded disassembler from %s\n", buf); ++ return true; ++} ++ ++} // namespace internal ++} // namespace v8 ++ ++//------------------------------------------------------------------------------ ++ ++namespace disasm { ++ ++const char* NameConverter::NameOfAddress(byte* addr) const { ++ v8::base::SNPrintF(tmp_buffer_, "%p", static_cast(addr)); ++ return tmp_buffer_.begin(); ++} ++ ++const char* NameConverter::NameOfConstant(byte* addr) const { ++ return NameOfAddress(addr); ++} ++ ++const char* NameConverter::NameOfCPURegister(int reg) const { ++ return v8::internal::Registers::Name(reg); ++} ++ ++const char* NameConverter::NameOfXMMRegister(int reg) const { ++ return v8::internal::FPURegisters::Name(reg); ++} ++ ++const char* NameConverter::NameOfByteCPURegister(int reg) const { ++ UNREACHABLE(); // SW64 does not have the concept of a byte register. ++} ++ ++const char* NameConverter::NameInCode(byte* addr) const { ++ // The default name converter is called for unknown code. So we will not try ++ // to access any memory. ++ return ""; ++} ++ ++//------------------------------------------------------------------------------ ++ ++int Disassembler::InstructionDecode(v8::base::Vector buffer, ++ byte* instruction) { ++ v8::internal::Decoder d(converter_, buffer); ++ return d.InstructionDecode(instruction); ++} ++ ++// The SW64 assembler does not currently use constant pools. ++int Disassembler::ConstantPoolSizeAt(byte* instruction) { ++ return -1; ++} ++ ++void Disassembler::Disassemble(FILE* f, byte* begin, byte* end, ++ UnimplementedOpcodeAction unimplemented_action) { ++ NameConverter converter; ++ Disassembler d(converter, unimplemented_action); ++ for (byte* pc = begin; pc < end;) { ++ v8::base::EmbeddedVector buffer; ++ buffer[0] = '\0'; ++ byte* prev_pc = pc; ++ pc += d.InstructionDecode(buffer, pc); ++ v8::internal::PrintF(f, "%p %08x %s\n", static_cast(prev_pc), ++ *reinterpret_cast(prev_pc), buffer.begin()); ++ } ++} ++ ++#undef STRING_STARTS_WITH ++ ++} // namespace disasm ++ ++#endif // V8_TARGET_ARCH_SW64 +diff --git a/deps/v8/src/diagnostics/sw64/unwinder-sw64.cc b/deps/v8/src/diagnostics/sw64/unwinder-sw64.cc +new file mode 100644 +index 00000000..03144580 +--- /dev/null ++++ b/deps/v8/src/diagnostics/sw64/unwinder-sw64.cc +@@ -0,0 +1,14 @@ ++// Copyright 2020 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "src/diagnostics/unwinder.h" ++ ++namespace v8 { ++ ++struct RegisterState; ++ ++void GetCalleeSavedRegistersFromEntryFrame(void* fp, ++ RegisterState* register_state) {} ++ ++} // namespace v8 +diff --git a/deps/v8/src/execution/clobber-registers.cc b/deps/v8/src/execution/clobber-registers.cc +index a7f5bf80..628eba7d 100644 +--- a/deps/v8/src/execution/clobber-registers.cc ++++ b/deps/v8/src/execution/clobber-registers.cc +@@ -22,6 +22,8 @@ + #include "src/codegen/mips/register-mips.h" + #elif V8_HOST_ARCH_MIPS64 && V8_TARGET_ARCH_MIPS64 + #include "src/codegen/mips64/register-mips64.h" ++#elif V8_HOST_ARCH_SW64 && V8_TARGET_ARCH_SW64 ++#include "src/codegen/sw64/register-sw64.h" + #endif + + namespace v8 { +@@ -58,6 +60,9 @@ namespace internal { + #elif V8_HOST_ARCH_MIPS64 && V8_TARGET_ARCH_MIPS64 + #define CLOBBER_USE_REGISTER(R) __asm__ volatile("dmtc1 $zero,$" #R :::); + ++#elif V8_HOST_ARCH_SW64 && V8_TARGET_ARCH_SW64 ++#define CLOBBER_USE_REGISTER(R) __asm__ volatile("ifmovd $r31, $" #R :::); ++ + #endif // V8_HOST_ARCH_XXX && V8_TARGET_ARCH_XXX + + #endif // V8_CC_MSVC +diff --git a/deps/v8/src/execution/frame-constants.h b/deps/v8/src/execution/frame-constants.h +index d171bdea..82cd6fd3 100644 +--- a/deps/v8/src/execution/frame-constants.h ++++ b/deps/v8/src/execution/frame-constants.h +@@ -421,6 +421,8 @@ inline static int FrameSlotToFPOffset(int slot) { + #include "src/execution/mips64/frame-constants-mips64.h" + #elif V8_TARGET_ARCH_LOONG64 + #include "src/execution/loong64/frame-constants-loong64.h" ++#elif V8_TARGET_ARCH_SW64 ++#include "src/execution/sw64/frame-constants-sw64.h" + #elif V8_TARGET_ARCH_S390 + #include "src/execution/s390/frame-constants-s390.h" + #elif V8_TARGET_ARCH_RISCV64 +diff --git a/deps/v8/src/execution/simulator-base.h b/deps/v8/src/execution/simulator-base.h +index ab8679ac..d0845d4c 100644 +--- a/deps/v8/src/execution/simulator-base.h ++++ b/deps/v8/src/execution/simulator-base.h +@@ -101,7 +101,7 @@ class SimulatorBase { + static typename std::enable_if::value, intptr_t>::type + ConvertArg(T arg) { + static_assert(sizeof(T) <= sizeof(intptr_t), "type bigger than ptrsize"); +-#if V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_RISCV64 || V8_TARGET_ARCH_LOONG64 ++#if V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_RISCV64 || V8_TARGET_ARCH_LOONG64 || V8_TARGET_ARCH_SW64 + // The MIPS64, LOONG64 and RISCV64 calling convention is to sign extend all + // values, even unsigned ones. + using signed_t = typename std::make_signed::type; +diff --git a/deps/v8/src/execution/simulator.h b/deps/v8/src/execution/simulator.h +index 6b6b845e..adae923c 100644 +--- a/deps/v8/src/execution/simulator.h ++++ b/deps/v8/src/execution/simulator.h +@@ -26,6 +26,8 @@ + #include "src/execution/mips64/simulator-mips64.h" + #elif V8_TARGET_ARCH_LOONG64 + #include "src/execution/loong64/simulator-loong64.h" ++#elif V8_TARGET_ARCH_SW64 ++#include "src/execution/sw64/simulator-sw64.h" + #elif V8_TARGET_ARCH_S390 + #include "src/execution/s390/simulator-s390.h" + #elif V8_TARGET_ARCH_RISCV64 +diff --git a/deps/v8/src/execution/sw64/frame-constants-sw64.cc b/deps/v8/src/execution/sw64/frame-constants-sw64.cc +new file mode 100644 +index 00000000..6e744b63 +--- /dev/null ++++ b/deps/v8/src/execution/sw64/frame-constants-sw64.cc +@@ -0,0 +1,34 @@ ++// 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. ++ ++#if V8_TARGET_ARCH_SW64 ++ ++#include "src/execution/sw64/frame-constants-sw64.h" ++ ++#include "src/codegen/sw64/assembler-sw64-inl.h" ++#include "src/execution/frame-constants.h" ++#include "src/execution/frames.h" ++ ++#include "src/execution/sw64/frame-constants-sw64.h" ++ ++namespace v8 { ++namespace internal { ++ ++Register JavaScriptFrame::fp_register() { return v8::internal::fp; } ++Register JavaScriptFrame::context_register() { return cp; } ++Register JavaScriptFrame::constant_pool_pointer_register() { UNREACHABLE(); } ++ ++int UnoptimizedFrameConstants::RegisterStackSlotCount(int register_count) { ++ return register_count; ++} ++ ++int BuiltinContinuationFrameConstants::PaddingSlotCount(int register_count) { ++ USE(register_count); ++ return 0; ++} ++ ++} // namespace internal ++} // namespace v8 ++ ++#endif // V8_TARGET_ARCH_SW64 +diff --git a/deps/v8/src/execution/sw64/frame-constants-sw64.h b/deps/v8/src/execution/sw64/frame-constants-sw64.h +new file mode 100644 +index 00000000..68f3402c +--- /dev/null ++++ b/deps/v8/src/execution/sw64/frame-constants-sw64.h +@@ -0,0 +1,83 @@ ++// Copyright 2011 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef V8_EXECUTION_SW64_FRAME_CONSTANTS_SW64_H_ ++#define V8_EXECUTION_SW64_FRAME_CONSTANTS_SW64_H_ ++ ++#include "src/base/bits.h" ++#include "src/base/macros.h" ++#include "src/codegen/register.h" ++#include "src/execution/frame-constants.h" ++ ++namespace v8 { ++namespace internal { ++ ++class EntryFrameConstants : public AllStatic { ++ public: ++ // This is the offset to where JSEntry pushes the current value of ++ // Isolate::c_entry_fp onto the stack. ++ static constexpr int kCallerFPOffset = -3 * kSystemPointerSize; ++}; ++ ++class WasmCompileLazyFrameConstants : public TypedFrameConstants { ++ public: ++ static constexpr int kNumberOfSavedGpParamRegs = 6; ++ static constexpr int kNumberOfSavedFpParamRegs = 6; ++ static constexpr int kNumberOfSavedAllParamRegs = 12; ++ ++// FP-relative. ++ // See Generate_WasmCompileLazy in builtins-sw64.cc. ++ // SKTODO ++ static constexpr int kWasmInstanceOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(12); ++ ++ static constexpr int kFixedFrameSizeFromFp = ++ TypedFrameConstants::kFixedFrameSizeFromFp + ++ kNumberOfSavedGpParamRegs * kPointerSize + ++ kNumberOfSavedFpParamRegs * kSimd128Size; ++}; ++ ++// Frame constructed by the {WasmDebugBreak} builtin. ++// After pushing the frame type marker, the builtin pushes all Liftoff cache ++// registers (see liftoff-assembler-defs.h). ++class WasmDebugBreakFrameConstants : public TypedFrameConstants { ++ public: ++ // {a0, a1, a2, a3, a4, a5, t0, t1, t2, t3, t4, t9, t10, s5, v0} ++ static constexpr RegList kPushedGpRegs = {a0, a1, a2, a3, a4, a5, t0, t1, ++ t2, t3, t4, t9, t10, s5, v0}; ++ // {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} ++ static constexpr DoubleRegList kPushedFpRegs = { ++ 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}; ++ ++ static constexpr int kNumPushedGpRegisters = kPushedGpRegs.Count(); ++ static constexpr int kNumPushedFpRegisters = kPushedFpRegs.Count(); ++ ++ static constexpr int kLastPushedGpRegisterOffset = ++ -kFixedFrameSizeFromFp - kNumPushedGpRegisters * kSystemPointerSize; ++ static constexpr int kLastPushedFpRegisterOffset = ++ kLastPushedGpRegisterOffset - kNumPushedFpRegisters * kDoubleSize; ++ ++ // Offsets are fp-relative. ++ static int GetPushedGpRegisterOffset(int reg_code) { ++ DCHECK_NE(0, kPushedGpRegs.bits() & (1 << reg_code)); ++ uint32_t lower_regs = ++ kPushedGpRegs.bits() & ((uint32_t{1} << reg_code) - 1); ++ return kLastPushedGpRegisterOffset + ++ base::bits::CountPopulation(lower_regs) * kSystemPointerSize; ++ } ++ ++ static int GetPushedFpRegisterOffset(int reg_code) { ++ DCHECK_NE(0, kPushedFpRegs.bits() & (1 << reg_code)); ++ uint32_t lower_regs = ++ kPushedFpRegs.bits() & ((uint32_t{1} << reg_code) - 1); ++ return kLastPushedFpRegisterOffset + ++ base::bits::CountPopulation(lower_regs) * kDoubleSize; ++ } ++}; ++ ++} // namespace internal ++} // namespace v8 ++ ++#endif // V8_SW64_FRAME_CONSTANTS_SW64_H_ +diff --git a/deps/v8/src/execution/sw64/simulator-sw64.cc b/deps/v8/src/execution/sw64/simulator-sw64.cc +new file mode 100644 +index 00000000..f9d93f99 +--- /dev/null ++++ b/deps/v8/src/execution/sw64/simulator-sw64.cc +@@ -0,0 +1,11 @@ ++// Copyright 2011 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "src/execution/sw64/simulator-sw64.h" ++ ++// Only build the simulator if not compiling for real SW64 hardware. ++#if defined(USE_SIMULATOR) ++// TODO ++ ++#endif // USE_SIMULATOR +diff --git a/deps/v8/src/execution/sw64/simulator-sw64.h b/deps/v8/src/execution/sw64/simulator-sw64.h +new file mode 100644 +index 00000000..dbb44075 +--- /dev/null ++++ b/deps/v8/src/execution/sw64/simulator-sw64.h +@@ -0,0 +1,39 @@ ++// 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. ++ ++// Declares a Simulator for SW64 instructions if we are not generating a native ++// SW64 binary. This Simulator allows us to run and debug SW64 code generation ++// on regular desktop machines. ++// V8 calls into generated code via the GeneratedCode wrapper, ++// which will start execution in the Simulator or forwards to the real entry ++// on a SW64 HW platform. ++ ++#ifndef V8_EXECUTION_SW64_SIMULATOR_SW64_H_ ++#define V8_EXECUTION_SW64_SIMULATOR_SW64_H_ ++ ++// globals.h defines USE_SIMULATOR. ++#include "src/common/globals.h" ++ ++template ++int Compare(const T& a, const T& b) { ++ if (a == b) ++ return 0; ++ else if (a < b) ++ return -1; ++ else ++ return 1; ++} ++ ++// Returns the negative absolute value of its argument. ++template ::value>::type> ++T Nabs(T a) { ++ return a < 0 ? a : -a; ++} ++ ++#if defined(USE_SIMULATOR) ++// TODO ++ ++#endif // defined(USE_SIMULATOR) ++#endif // V8_EXECUTION_SW64_SIMULATOR_SW64_H_ +diff --git a/deps/v8/src/flags/flag-definitions.h b/deps/v8/src/flags/flag-definitions.h +index 3e81ff98..acea182d 100644 +--- a/deps/v8/src/flags/flag-definitions.h ++++ b/deps/v8/src/flags/flag-definitions.h +@@ -837,7 +837,42 @@ DEFINE_BOOL(verify_csa, DEBUG_BOOL, + // non-ENABLE_VERIFY_CSA configuration. + DEFINE_BOOL_READONLY(verify_csa, false, + "verify TurboFan machine graph of code stubs") +-#endif ++#endif // ENABLE_VERIFY_CSA ++#define SW64_ARCH_DEFAULT "native" ++ ++DEFINE_STRING(sw64_arch, SW64_ARCH_DEFAULT, ++ "generate instructions for the selected SW64 architecture if " ++ "available: sw6b, sw8a or native") ++DEFINE_BOOL(sw8a_structure, false, "use 8a_structure in sw8a: the same reg in f-intructions; unaligned access...") ++DEFINE_BOOL(use_sw8a, false, "use intructions in sw8a") ++ ++DEFINE_BOOL(use_cmovx, false, "use cmovx intructions in sw8a") ++DEFINE_BOOL(use_cmovdw, false, "use cmovdw intructions in sw8a") ++DEFINE_BOOL(use_cmovdwu, false, "use cmovdwu intructions in sw8a") ++DEFINE_BOOL(use_cmovdl, false, "use cmovdl intructions in sw8a") ++DEFINE_BOOL(use_cmovdlu, false, "use cmovdlu intructions in sw8a") ++DEFINE_BOOL(use_cmovls, false, "use cmovls intructions in sw8a") ++DEFINE_BOOL(use_cmovuls, false, "use cmovuls intructions in sw8a") ++DEFINE_BOOL(use_cmovld, false, "use cmovld intructions in sw8a") ++DEFINE_BOOL(use_cmovuld, false, "use cmovuld intructions in sw8a") ++DEFINE_BOOL(use_cmovws, false, "use cmovws intructions in sw8a") ++DEFINE_BOOL(use_cmovuws, false, "use cmovuws intructions in sw8a") ++DEFINE_BOOL(use_cmovwd, false, "use cmovwd intructions in sw8a") ++DEFINE_BOOL(use_cmovuwd, false, "use cmovuwd intructions in sw8a") ++ ++DEFINE_BOOL(use_frecx, false, "use frecx intructions in sw8a") ++DEFINE_BOOL(use_frisx, false, "use frisx intructions in sw8a") ++DEFINE_BOOL(use_fridx, false, "use fridx intructions in sw8a") ++DEFINE_BOOL(use_revbx, false, "use revbx intructions in sw8a") ++DEFINE_BOOL(use_sllx, false, "use sllx intructions in sw8a") ++DEFINE_BOOL(use_divx, false, "use divx intructions in sw8a") ++ ++DEFINE_BOOL(use_ldx_a, false, "use ldx_a intructions in sw8a") ++DEFINE_BOOL(use_lldx, false, "use lldx stlx intructions in sw8a") ++DEFINE_BOOL(use_csax, false, "use csax intructions in sw8a") ++DEFINE_BOOL(use_addpi, false, "use addpi/addpis intructions in sw8a") ++ ++DEFINE_BOOL(octane_test, false, "some optimizes when run octane") + DEFINE_BOOL(trace_verify_csa, false, "trace code stubs verification") + DEFINE_STRING(csa_trap_on_node, nullptr, + "trigger break point when a node with given id is created in " +@@ -1705,7 +1740,7 @@ DEFINE_BOOL(check_icache, false, + DEFINE_INT(stop_sim_at, 0, "Simulator stop after x number of instructions") + #if defined(V8_TARGET_ARCH_ARM64) || defined(V8_TARGET_ARCH_MIPS64) || \ + defined(V8_TARGET_ARCH_PPC64) || defined(V8_TARGET_ARCH_RISCV64) || \ +- defined(V8_TARGET_ARCH_LOONG64) ++ defined(V8_TARGET_ARCH_LOONG64) || defined(V8_TARGET_ARCH_SW64) + DEFINE_INT(sim_stack_alignment, 16, + "Stack alignment in bytes in simulator. This must be a power of two " + "and it must be at least 16. 16 is default.") +diff --git a/deps/v8/src/heap/base/asm/sw64/push_registers_asm.cc b/deps/v8/src/heap/base/asm/sw64/push_registers_asm.cc +new file mode 100644 +index 00000000..54f05541 +--- /dev/null ++++ b/deps/v8/src/heap/base/asm/sw64/push_registers_asm.cc +@@ -0,0 +1,47 @@ ++// Copyright 2020 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. ++ ++// Push all callee-saved registers to get them on the stack for conservative ++// stack scanning. ++// ++// See asm/x64/push_registers_clang.cc for why the function is not generated ++// using clang. ++// ++// Do not depend on V8_TARGET_OS_* defines as some embedders may override the ++// GN toolchain (e.g. ChromeOS) and not provide them. ++asm(".text \n" ++ ".set noreorder \n" ++ ".global PushAllRegistersAndIterateStack \n" ++ ".type PushAllRegistersAndIterateStack, %function \n" ++ ".hidden PushAllRegistersAndIterateStack \n" ++ "PushAllRegistersAndIterateStack: \n" ++ // Push all callee-saved registers and save return address. ++ " subl $30, 80, $30 \n" ++ " stl $26, 72($30) \n" ++ " stl $15, 64($30) \n" ++ " stl $30, 56($30) \n" ++ " stl $29, 48($30) \n" ++ " stl $14, 40($30) \n" ++ " stl $13, 32($30) \n" ++ " stl $12, 24($30) \n" ++ " stl $11, 16($30) \n" ++ " stl $10, 8($30) \n" ++ " stl $9, 0($30) \n" ++ // Maintain frame pointer. ++ " bis $31, $30, $15 \n" ++ // Pass 1st parameter (a0) unchanged (Stack*). ++ // Pass 2nd parameter (a1) unchanged (StackVisitor*). ++ // Save 3rd parameter (a2; IterateStackCallback). ++ " bis $31, $18, $27 \n" ++ // Pass 3rd parameter as sp (stack pointer). ++ " bis $31, $30, $18 \n" ++ // Call the callback. ++ " call $26, ($27), 0 \n" ++ // Load return address. ++ " ldl $26, 72($30) \n" ++ // Restore frame pointer. ++ " ldl $15, 64($30) \n" ++ // Discard all callee-saved registers. ++ " addl $30, 80, $30 \n" ++ " ret $31, ($26), 0 \n"); +diff --git a/deps/v8/src/interpreter/interpreter-assembler.cc b/deps/v8/src/interpreter/interpreter-assembler.cc +index 415db8cb..e1ac07cd 100644 +--- a/deps/v8/src/interpreter/interpreter-assembler.cc ++++ b/deps/v8/src/interpreter/interpreter-assembler.cc +@@ -1401,11 +1401,11 @@ void InterpreterAssembler::TraceBytecodeDispatch(TNode target_bytecode) { + + // static + bool InterpreterAssembler::TargetSupportsUnalignedAccess() { +-#if V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_RISCV64 ++#if V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_RISCV64 || V8_TARGET_ARCH_SW64 + return false; + #elif V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X64 || V8_TARGET_ARCH_S390 || \ + V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_ARM64 || V8_TARGET_ARCH_PPC || \ +- V8_TARGET_ARCH_PPC64 || V8_TARGET_ARCH_LOONG64 ++ V8_TARGET_ARCH_PPC64 || V8_TARGET_ARCH_LOONG64 + return true; + #else + #error "Unknown Architecture" +diff --git a/deps/v8/src/libsampler/sampler.cc b/deps/v8/src/libsampler/sampler.cc +index 9e556945..df1e2699 100644 +--- a/deps/v8/src/libsampler/sampler.cc ++++ b/deps/v8/src/libsampler/sampler.cc +@@ -419,6 +419,10 @@ void SignalHandler::FillRegisterState(void* context, RegisterState* state) { + state->pc = reinterpret_cast(mcontext.__pc); + state->sp = reinterpret_cast(mcontext.__gregs[3]); + state->fp = reinterpret_cast(mcontext.__gregs[22]); ++#elif V8_HOST_ARCH_SW64 ++ state->pc = reinterpret_cast(mcontext.sc_pc); ++ state->sp = reinterpret_cast(mcontext.sc_regs[30]); ++ state->fp = reinterpret_cast(mcontext.sc_regs[15]); + #elif V8_HOST_ARCH_PPC || V8_HOST_ARCH_PPC64 + #if V8_LIBC_GLIBC + state->pc = reinterpret_cast(ucontext->uc_mcontext.regs->nip); +diff --git a/deps/v8/src/logging/log.cc b/deps/v8/src/logging/log.cc +index 8a640ac2..2ba0c421 100644 +--- a/deps/v8/src/logging/log.cc ++++ b/deps/v8/src/logging/log.cc +@@ -632,6 +632,8 @@ void LowLevelLogger::LogCodeInfo() { + const char arch[] = "mips"; + #elif V8_TARGET_ARCH_LOONG64 + const char arch[] = "loong64"; ++#elif V8_TARGET_ARCH_SW64 ++ const char arch[] = "sw_64"; + #elif V8_TARGET_ARCH_ARM64 + const char arch[] = "arm64"; + #elif V8_TARGET_ARCH_S390 +diff --git a/deps/v8/src/objects/code.cc b/deps/v8/src/objects/code.cc +index 95e312fe..6820176a 100644 +--- a/deps/v8/src/objects/code.cc ++++ b/deps/v8/src/objects/code.cc +@@ -311,7 +311,8 @@ bool Code::IsIsolateIndependent(Isolate* isolate) { + #elif defined(V8_TARGET_ARCH_X64) || defined(V8_TARGET_ARCH_ARM64) || \ + defined(V8_TARGET_ARCH_ARM) || defined(V8_TARGET_ARCH_MIPS) || \ + defined(V8_TARGET_ARCH_S390) || defined(V8_TARGET_ARCH_IA32) || \ +- defined(V8_TARGET_ARCH_RISCV64) || defined(V8_TARGET_ARCH_LOONG64) ++ defined(V8_TARGET_ARCH_RISCV64) || defined(V8_TARGET_ARCH_LOONG64) \ ++ || defined(V8_TARGET_ARCH_SW64) + for (RelocIterator it(*this, kModeMask); !it.done(); it.next()) { + // On these platforms we emit relative builtin-to-builtin + // jumps for isolate independent builtins in the snapshot. They are later +diff --git a/deps/v8/src/objects/code.h b/deps/v8/src/objects/code.h +index 4c2679e3..97a1146d 100644 +--- a/deps/v8/src/objects/code.h ++++ b/deps/v8/src/objects/code.h +@@ -648,6 +648,8 @@ class Code : public HeapObject { + static constexpr int kHeaderPaddingSize = 24; + #elif V8_TARGET_ARCH_LOONG64 + static constexpr int kHeaderPaddingSize = 24; ++#elif V8_TARGET_ARCH_SW64 ++ static constexpr int kHeaderPaddingSize = 24; + #elif V8_TARGET_ARCH_X64 + static constexpr int kHeaderPaddingSize = + V8_EXTERNAL_CODE_SPACE_BOOL ? 8 : (COMPRESS_POINTERS_BOOL ? 12 : 56); +diff --git a/deps/v8/src/regexp/regexp-macro-assembler-arch.h b/deps/v8/src/regexp/regexp-macro-assembler-arch.h +index 5d4663e3..722c4220 100644 +--- a/deps/v8/src/regexp/regexp-macro-assembler-arch.h ++++ b/deps/v8/src/regexp/regexp-macro-assembler-arch.h +@@ -23,6 +23,8 @@ + #include "src/regexp/mips64/regexp-macro-assembler-mips64.h" + #elif V8_TARGET_ARCH_LOONG64 + #include "src/regexp/loong64/regexp-macro-assembler-loong64.h" ++#elif V8_TARGET_ARCH_SW64 ++#include "src/regexp/sw64/regexp-macro-assembler-sw64.h" + #elif V8_TARGET_ARCH_S390 + #include "src/regexp/s390/regexp-macro-assembler-s390.h" + #elif V8_TARGET_ARCH_RISCV64 +diff --git a/deps/v8/src/regexp/regexp-macro-assembler.h b/deps/v8/src/regexp/regexp-macro-assembler.h +index 16a23414..b9df47c3 100644 +--- a/deps/v8/src/regexp/regexp-macro-assembler.h ++++ b/deps/v8/src/regexp/regexp-macro-assembler.h +@@ -170,6 +170,7 @@ class RegExpMacroAssembler { + V(S390) \ + V(PPC) \ + V(X64) \ ++ V(SW64) \ + V(Bytecode) + + enum IrregexpImplementation { +diff --git a/deps/v8/src/regexp/regexp.cc b/deps/v8/src/regexp/regexp.cc +index df50034b..5f3613ea 100644 +--- a/deps/v8/src/regexp/regexp.cc ++++ b/deps/v8/src/regexp/regexp.cc +@@ -946,6 +946,9 @@ bool RegExpImpl::Compile(Isolate* isolate, Zone* zone, RegExpCompileData* data, + #elif V8_TARGET_ARCH_LOONG64 + macro_assembler.reset(new RegExpMacroAssemblerLOONG64( + isolate, zone, mode, output_register_count)); ++#elif V8_TARGET_ARCH_SW64 ++ macro_assembler.reset(new RegExpMacroAssemblerSW64(isolate, zone, mode, ++ output_register_count)); + #else + #error "Unsupported architecture" + #endif +diff --git a/deps/v8/src/regexp/sw64/regexp-macro-assembler-sw64.cc b/deps/v8/src/regexp/sw64/regexp-macro-assembler-sw64.cc +new file mode 100644 +index 00000000..06f93d8a +--- /dev/null ++++ b/deps/v8/src/regexp/sw64/regexp-macro-assembler-sw64.cc +@@ -0,0 +1,1383 @@ ++// Copyright 2012 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#if V8_TARGET_ARCH_SW64 ++ ++#include "src/regexp/sw64/regexp-macro-assembler-sw64.h" ++ ++#include "src/codegen/macro-assembler.h" ++#include "src/codegen/sw64/assembler-sw64-inl.h" ++#include "src/heap/factory.h" ++#include "src/logging/log.h" ++#include "src/objects/code-inl.h" ++#include "src/regexp/regexp-stack.h" ++#include "src/snapshot/embedded/embedded-data-inl.h" ++ ++namespace v8 { ++namespace internal { ++ ++/* clang-format off ++ * ++ * This assembler uses the following register assignment convention ++ * - s0 : Unused. ++ * - s1 : Pointer to current Code object including heap object tag. ++ * - s2 : Current position in input, as negative offset from end of string. ++ * Please notice that this is the byte offset, not the character offset! ++ * - s3 : Currently loaded character. Must be loaded using ++ * LoadCurrentCharacter before using any of the dispatch methods. ++ * - s4 : Points to tip of backtrack stack ++ * - s5 : End of input (points to byte after last character in input). ++ * - fp : Frame pointer. Used to access arguments, local variables and ++ * RegExp registers. ++ * - sp : Points to tip of C stack. ++ * ++ * The remaining registers are free for computations. ++ * Each call to a public method should retain this convention. ++ * ++ * TODO(plind): O32 documented here with intent of having single 32/64 codebase ++ * in the future. ++ * ++ * The O32 stack will have the following structure: ++ * ++ * - fp[72] Isolate* isolate (address of the current isolate) ++ * - fp[68] direct_call (if 1, direct call from JavaScript code, ++ * if 0, call through the runtime system). ++ * - fp[64] stack_area_base (High end of the memory area to use as ++ * backtracking stack). ++ * - fp[60] capture array size (may fit multiple sets of matches) ++ * - fp[44..59] SW64 O32 four argument slots ++ * - fp[40] int* capture_array (int[num_saved_registers_], for output). ++ * --- sp when called --- ++ * - fp[36] return address (lr). ++ * - fp[32] old frame pointer (r11). ++ * - fp[0..31] backup of registers s0..s7. ++ * --- frame pointer ---- ++ * - fp[-4] end of input (address of end of string). ++ * - fp[-8] start of input (address of first character in string). ++ * - fp[-12] start index (character index of start). ++ * - fp[-16] void* input_string (location of a handle containing the string). ++ * - fp[-20] success counter (only for global regexps to count matches). ++ * - fp[-24] Offset of location before start of input (effectively character ++ * string start - 1). Used to initialize capture registers to a ++ * non-position. ++ * - fp[-28] At start (if 1, we are starting at the start of the ++ * string, otherwise 0) ++ * - fp[-32] register 0 (Only positions must be stored in the first ++ * - register 1 num_saved_registers_ registers) ++ * - ... ++ * - register num_registers-1 ++ * --- sp --- ++ * ++ * ++ * The N64 stack will have the following structure: ++ * ++ * - fp[80] Isolate* isolate (address of the current isolate) kIsolate ++ * kStackFrameHeader ++ * --- sp when called --- ++ * - fp[72] ra Return from RegExp code (ra). kReturnAddress ++ * - fp[64] s9, old-fp Old fp, callee saved(s9). ++ * - fp[0..63] s0..s7 Callee-saved registers s0..s7. ++ * --- frame pointer ---- ++ * - fp[-8] direct_call (1 = direct call from JS, 0 = from runtime) kDirectCall ++ * - fp[-16] capture array size (may fit multiple sets of matches) kNumOutputRegisters ++ * - fp[-24] int* capture_array (int[num_saved_registers_], for output). kRegisterOutput ++ * - fp[-32] end of input (address of end of string). kInputEnd ++ * - fp[-40] start of input (address of first character in string). kInputStart ++ * - fp[-48] start index (character index of start). kStartIndex ++ * - fp[-56] void* input_string (location of a handle containing the string). kInputString ++ * - fp[-64] success counter (only for global regexps to count matches). kSuccessfulCaptures ++ * - fp[-72] Offset of location before start of input (effectively character kStringStartMinusOne ++ * position -1). Used to initialize capture registers to a ++ * non-position. ++ * --------- The following output registers are 32-bit values. --------- ++ * - fp[-80] register 0 (Only positions must be stored in the first kRegisterZero ++ * - register 1 num_saved_registers_ registers) ++ * - ... ++ * - register num_registers-1 ++ * --- sp --- ++ * ++ * ++ * The sw64 stack will have the following structure: ++ * ++ * - fp[72] Isolate* isolate (address of the current isolate) kIsolate ++ * - fp[64] direct_call (1 = direct call from JS, 0 = from runtime) kDirectCall ++ * kStackFrameHeader ++ * --- sp when called --- ++ * - fp[56] ra Return from RegExp code (ra). kReturnAddress ++ * - fp[48] fp fp, callee saved(s6). ++ * - fp[0..47] s0..s5 Callee-saved registers s0..s5. ++ * --- frame pointer ---- ++ * - fp[-8] capture array size (may fit multiple sets of matches) kNumOutputRegisters ++ * - fp[-16] int* capture_array (int[num_saved_registers_], for output). kRegisterOutput ++ * - fp[-24] end of input (address of end of string). kInputEnd ++ * - fp[-32] start of input (address of first character in string). kInputStart ++ * - fp[-40] start index (character index of start). kStartIndex ++ * - fp[-48] void* input_string (location of a handle containing the string). kInputString ++ * - fp[-56] success counter (only for global regexps to count matches). kSuccessfulCaptures ++ * - fp[-64] Offset of location before start of input (effectively character kInputStartMinusOne ++ * position -1). Used to initialize capture registers to a ++ * non-position. ++ * --------- The following output registers are 32-bit values. --------- ++ * - fp[-72] register 0 (Only positions must be stored in the first kRegisterZero ++ * - register 1 num_saved_registers_ registers) ++ * - ... ++ * - register num_registers-1 ++ * --- sp --- ++ * ++ * The first num_saved_registers_ registers are initialized to point to ++ * "character -1" in the string (i.e., char_size() bytes before the first ++ * character of the string). The remaining registers start out as garbage. ++ * ++ * The data up to the return address must be placed there by the calling ++ * code and the remaining arguments are passed in registers, e.g. by calling the ++ * code entry as cast to a function with the signature: ++ * int (*match)(String input_string, ++ * int start_index, ++ * Address start, ++ * Address end, ++ * int* capture_output_array, ++ * int num_capture_registers, ++ * bool direct_call = false, ++ * Isolate* isolate); ++ * The call is performed by NativeRegExpMacroAssembler::Execute() ++ * (in regexp-macro-assembler.cc) via the GeneratedCode wrapper. ++ * ++ * clang-format on ++ */ ++ ++#define __ ACCESS_MASM(masm_) ++ ++const int RegExpMacroAssemblerSW64::kRegExpCodeSize; ++ ++RegExpMacroAssemblerSW64::RegExpMacroAssemblerSW64(Isolate* isolate, Zone* zone, ++ Mode mode, ++ int registers_to_save) ++ : NativeRegExpMacroAssembler(isolate, zone), ++ masm_(std::make_unique( ++ isolate, CodeObjectRequired::kYes, ++ NewAssemblerBuffer(kRegExpCodeSize))), ++ no_root_array_scope_(masm_.get()), ++ mode_(mode), ++ num_registers_(registers_to_save), ++ num_saved_registers_(registers_to_save), ++ entry_label_(), ++ start_label_(), ++ success_label_(), ++ backtrack_label_(), ++ exit_label_(), ++ internal_failure_label_() { ++ DCHECK_EQ(0, registers_to_save % 2); ++ __ jmp(&entry_label_); // We'll write the entry code later. ++ // If the code gets too big or corrupted, an internal exception will be ++ // raised, and we will exit right away. ++ __ bind(&internal_failure_label_); ++ __ li(v0, Operand(FAILURE)); ++ __ Ret(); ++ __ bind(&start_label_); // And then continue from here. ++} ++ ++RegExpMacroAssemblerSW64::~RegExpMacroAssemblerSW64() { ++ // Unuse labels in case we throw away the assembler without calling GetCode. ++ entry_label_.Unuse(); ++ start_label_.Unuse(); ++ success_label_.Unuse(); ++ backtrack_label_.Unuse(); ++ exit_label_.Unuse(); ++ check_preempt_label_.Unuse(); ++ stack_overflow_label_.Unuse(); ++ internal_failure_label_.Unuse(); ++ fallback_label_.Unuse(); ++} ++ ++int RegExpMacroAssemblerSW64::stack_limit_slack() { ++ return RegExpStack::kStackLimitSlack; ++} ++ ++void RegExpMacroAssemblerSW64::AdvanceCurrentPosition(int by) { ++ if (by != 0) { ++ __ Addl(current_input_offset(), current_input_offset(), ++ Operand(by * char_size())); ++ } ++} ++ ++void RegExpMacroAssemblerSW64::AdvanceRegister(int reg, int by) { ++ DCHECK_LE(0, reg); ++ DCHECK_GT(num_registers_, reg); ++ if (by != 0) { ++ __ Ldl(a0, register_location(reg)); ++ __ Addl(a0, a0, Operand(by)); ++ __ Stl(a0, register_location(reg)); ++ } ++} ++ ++void RegExpMacroAssemblerSW64::Backtrack() { ++ CheckPreemption(); ++ if (has_backtrack_limit()) { ++ Label next; ++ __ Ldl(a0, MemOperand(frame_pointer(), kBacktrackCount)); ++ __ Addl(a0, a0, Operand(1)); ++ __ Stl(a0, MemOperand(frame_pointer(), kBacktrackCount)); ++ __ Branch(&next, ne, a0, Operand(backtrack_limit())); ++ ++ // Backtrack limit exceeded. ++ if (can_fallback()) { ++ __ jmp(&fallback_label_); ++ } else { ++ // Can't fallback, so we treat it as a failed match. ++ Fail(); ++ } ++ ++ __ bind(&next); ++ } ++ // Pop Code offset from backtrack stack, add Code and jump to location. ++ Pop(a0); ++ __ Addl(a0, a0, code_pointer()); ++ __ Jump(a0); ++} ++ ++void RegExpMacroAssemblerSW64::Bind(Label* label) { ++ __ bind(label); ++} ++ ++void RegExpMacroAssemblerSW64::CheckCharacter(uint32_t c, Label* on_equal) { ++ BranchOrBacktrack(on_equal, eq, current_character(), Operand(c)); ++} ++ ++void RegExpMacroAssemblerSW64::CheckCharacterGT(base::uc16 limit, ++ Label* on_greater) { ++ BranchOrBacktrack(on_greater, gt, current_character(), Operand(limit)); ++} ++ ++void RegExpMacroAssemblerSW64::CheckAtStart(int cp_offset, Label* on_at_start) { ++ __ Ldl(a1, MemOperand(frame_pointer(), kStringStartMinusOne)); ++ __ Addl(a0, current_input_offset(), ++ Operand(-char_size() + cp_offset * char_size())); ++ BranchOrBacktrack(on_at_start, eq, a0, Operand(a1)); ++} ++ ++void RegExpMacroAssemblerSW64::CheckNotAtStart(int cp_offset, ++ Label* on_not_at_start) { ++ __ Ldl(a1, MemOperand(frame_pointer(), kStringStartMinusOne)); ++ __ Addl(a0, current_input_offset(), ++ Operand(-char_size() + cp_offset * char_size())); ++ BranchOrBacktrack(on_not_at_start, ne, a0, Operand(a1)); ++} ++ ++void RegExpMacroAssemblerSW64::CheckCharacterLT(base::uc16 limit, ++ Label* on_less) { ++ BranchOrBacktrack(on_less, lt, current_character(), Operand(limit)); ++} ++ ++void RegExpMacroAssemblerSW64::CheckGreedyLoop(Label* on_equal) { ++ Label backtrack_non_equal; ++ __ Ldw(a0, MemOperand(backtrack_stackpointer(), 0)); ++ __ Branch(&backtrack_non_equal, ne, current_input_offset(), Operand(a0)); ++ __ Addl(backtrack_stackpointer(), backtrack_stackpointer(), ++ Operand(kIntSize)); ++ __ bind(&backtrack_non_equal); ++ BranchOrBacktrack(on_equal, eq, current_input_offset(), Operand(a0)); ++} ++ ++void RegExpMacroAssemblerSW64::CheckNotBackReferenceIgnoreCase( ++ int start_reg, bool read_backward, bool unicode, Label* on_no_match) { ++ Label fallthrough; ++ __ Ldl(a0, register_location(start_reg)); // Index of start of capture. ++ __ Ldl(a1, register_location(start_reg + 1)); // Index of end of capture. ++ __ Subl(a1, a1, a0); // Length of capture. ++ ++ // At this point, the capture registers are either both set or both cleared. ++ // If the capture length is zero, then the capture is either empty or cleared. ++ // Fall through in both cases. ++ __ Branch(&fallthrough, eq, a1, Operand(zero_reg)); ++ ++ if (read_backward) { ++ __ Ldl(t1, MemOperand(frame_pointer(), kStringStartMinusOne)); ++ __ Addl(t1, t1, a1); ++ BranchOrBacktrack(on_no_match, le, current_input_offset(), Operand(t1)); ++ } else { ++ __ Addl(t1, a1, current_input_offset()); ++ // Check that there are enough characters left in the input. ++ BranchOrBacktrack(on_no_match, gt, t1, Operand(zero_reg)); ++ } ++ ++ if (mode_ == LATIN1) { ++ Label success; ++ Label fail; ++ Label loop_check; ++ ++ // a0 - offset of start of capture. ++ // a1 - length of capture. ++ __ Addl(a0, a0, Operand(end_of_input_address())); ++ __ Addl(a2, end_of_input_address(), Operand(current_input_offset())); ++ if (read_backward) { ++ __ Subl(a2, a2, Operand(a1)); ++ } ++ __ Addl(a1, a0, Operand(a1)); ++ ++ // a0 - Address of start of capture. ++ // a1 - Address of end of capture. ++ // a2 - Address of current input position. ++ ++ Label loop; ++ __ bind(&loop); ++ __ Ldbu(a3, MemOperand(a0, 0)); ++ __ addl(a0, char_size(), a0); ++ __ Ldbu(a4, MemOperand(a2, 0)); ++ __ addl(a2, char_size(), a2); ++ ++ __ Branch(&loop_check, eq, a4, Operand(a3)); ++ ++ // Mismatch, try case-insensitive match (converting letters to lower-case). ++ __ Or(a3, a3, Operand(0x20)); // Convert capture character to lower-case. ++ __ Or(a4, a4, Operand(0x20)); // Also convert input character. ++ __ Branch(&fail, ne, a4, Operand(a3)); ++ __ Subl(a3, a3, Operand('a')); ++ __ Branch(&loop_check, ls, a3, Operand('z' - 'a')); ++ // Latin-1: Check for values in range [224,254] but not 247. ++ __ Subl(a3, a3, Operand(224 - 'a')); ++ // Weren't Latin-1 letters. ++ __ Branch(&fail, hi, a3, Operand(254 - 224)); ++ // Check for 247. ++ __ Branch(&fail, eq, a3, Operand(247 - 224)); ++ ++ __ bind(&loop_check); ++ __ Branch(&loop, lt, a0, Operand(a1)); ++ __ jmp(&success); ++ ++ __ bind(&fail); ++ GoTo(on_no_match); ++ ++ __ bind(&success); ++ // Compute new value of character position after the matched part. ++ __ Subl(current_input_offset(), a2, end_of_input_address()); ++ if (read_backward) { ++ __ Ldl(t1, register_location(start_reg)); // Index of start of capture. ++ __ Ldl(a2, register_location(start_reg + 1)); // Index of end of capture. ++ __ Addl(current_input_offset(), current_input_offset(), Operand(t1)); ++ __ Subl(current_input_offset(), current_input_offset(), Operand(a2)); ++ } ++ } else { ++ DCHECK(mode_ == UC16); ++ ++ int argument_count = 4; ++ __ PrepareCallCFunction(argument_count, a2); ++ ++ // a0 - offset of start of capture. ++ // a1 - length of capture. ++ ++ // Put arguments into arguments registers. ++ // Parameters are ++ // a0: Address byte_offset1 - Address captured substring's start. ++ // a1: Address byte_offset2 - Address of current character position. ++ // a2: size_t byte_length - length of capture in bytes(!). ++ // a3: Isolate* isolate. ++ ++ // Address of start of capture. ++ __ Addl(a0, a0, Operand(end_of_input_address())); ++ // Length of capture. ++ __ mov(a2, a1); ++ // Save length in callee-save register for use on return. ++ __ mov(s3, a1); // Can we use s3 here? ++ // Address of current input position. ++ __ Addl(a1, current_input_offset(), Operand(end_of_input_address())); ++ if (read_backward) { ++ __ Subl(a1, a1, Operand(s3)); ++ } ++ // Isolate. ++ __ li(a3, Operand(ExternalReference::isolate_address(masm_->isolate()))); ++ ++ { ++ AllowExternalCallThatCantCauseGC scope(masm_.get()); ++ ExternalReference function = ++ unicode ++ ? ExternalReference::re_case_insensitive_compare_unicode() ++ : ExternalReference::re_case_insensitive_compare_non_unicode(); ++ __ CallCFunction(function, argument_count); ++ } ++ ++ // Check if function returned non-zero for success or zero for failure. ++ BranchOrBacktrack(on_no_match, eq, v0, Operand(zero_reg)); ++ // On success, increment position by length of capture. ++ if (read_backward) { ++ __ Subl(current_input_offset(), current_input_offset(), Operand(s3)); ++ } else { ++ __ Addl(current_input_offset(), current_input_offset(), Operand(s3)); ++ } ++ } ++ ++ __ bind(&fallthrough); ++} ++ ++void RegExpMacroAssemblerSW64::CheckNotBackReference(int start_reg, ++ bool read_backward, ++ Label* on_no_match) { ++ Label fallthrough; ++ ++ // Find length of back-referenced capture. ++ __ Ldl(a0, register_location(start_reg)); ++ __ Ldl(a1, register_location(start_reg + 1)); ++ __ Subl(a1, a1, a0); // Length to check. ++ ++ // At this point, the capture registers are either both set or both cleared. ++ // If the capture length is zero, then the capture is either empty or cleared. ++ // Fall through in both cases. ++ __ Branch(&fallthrough, eq, a1, Operand(zero_reg)); ++ ++ if (read_backward) { ++ __ Ldl(t1, MemOperand(frame_pointer(), kStringStartMinusOne)); ++ __ Addl(t1, t1, a1); ++ BranchOrBacktrack(on_no_match, le, current_input_offset(), Operand(t1)); ++ } else { ++ __ Addl(t1, a1, current_input_offset()); ++ // Check that there are enough characters left in the input. ++ BranchOrBacktrack(on_no_match, gt, t1, Operand(zero_reg)); ++ } ++ ++ // Compute pointers to match string and capture string. ++ __ Addl(a0, a0, Operand(end_of_input_address())); ++ __ Addl(a2, end_of_input_address(), Operand(current_input_offset())); ++ if (read_backward) { ++ __ Subl(a2, a2, Operand(a1)); ++ } ++ __ Addl(a1, a1, Operand(a0)); ++ ++ Label loop; ++ __ bind(&loop); ++ if (mode_ == LATIN1) { ++ __ Ldbu(a3, MemOperand(a0, 0)); ++ __ addl(a0, char_size(), a0); ++ __ Ldbu(a4, MemOperand(a2, 0)); ++ __ addl(a2, char_size(), a2); ++ } else { ++ DCHECK(mode_ == UC16); ++ __ Ldhu(a3, MemOperand(a0, 0)); ++ __ addl(a0, char_size(), a0); ++ __ Ldhu(a4, MemOperand(a2, 0)); ++ __ addl(a2, char_size(), a2); ++ } ++ BranchOrBacktrack(on_no_match, ne, a3, Operand(a4)); ++ __ Branch(&loop, lt, a0, Operand(a1)); ++ ++ // Move current character position to position after match. ++ __ Subl(current_input_offset(), a2, end_of_input_address()); ++ if (read_backward) { ++ __ Ldl(t1, register_location(start_reg)); // Index of start of capture. ++ __ Ldl(a2, register_location(start_reg + 1)); // Index of end of capture. ++ __ Addl(current_input_offset(), current_input_offset(), Operand(t1)); ++ __ Subl(current_input_offset(), current_input_offset(), Operand(a2)); ++ } ++ __ bind(&fallthrough); ++} ++ ++void RegExpMacroAssemblerSW64::CheckNotCharacter(uint32_t c, ++ Label* on_not_equal) { ++ BranchOrBacktrack(on_not_equal, ne, current_character(), Operand(c)); ++} ++ ++void RegExpMacroAssemblerSW64::CheckCharacterAfterAnd(uint32_t c, ++ uint32_t mask, ++ Label* on_equal) { ++ __ And(a0, current_character(), Operand(mask)); ++ Operand rhs = (c == 0) ? Operand(zero_reg) : Operand(c); ++ BranchOrBacktrack(on_equal, eq, a0, rhs); ++} ++ ++void RegExpMacroAssemblerSW64::CheckNotCharacterAfterAnd(uint32_t c, ++ uint32_t mask, ++ Label* on_not_equal) { ++ __ And(a0, current_character(), Operand(mask)); ++ Operand rhs = (c == 0) ? Operand(zero_reg) : Operand(c); ++ BranchOrBacktrack(on_not_equal, ne, a0, rhs); ++} ++ ++void RegExpMacroAssemblerSW64::CheckNotCharacterAfterMinusAnd( ++ base::uc16 c, base::uc16 minus, base::uc16 mask, Label* on_not_equal) { ++ DCHECK_GT(String::kMaxUtf16CodeUnit, minus); ++ __ Subl(a0, current_character(), Operand(minus)); ++ __ And(a0, a0, Operand(mask)); ++ BranchOrBacktrack(on_not_equal, ne, a0, Operand(c)); ++} ++ ++void RegExpMacroAssemblerSW64::CheckCharacterInRange(base::uc16 from, ++ base::uc16 to, ++ Label* on_in_range) { ++ __ Subl(a0, current_character(), Operand(from)); ++ // Unsigned lower-or-same condition. ++ BranchOrBacktrack(on_in_range, ls, a0, Operand(to - from)); ++} ++ ++void RegExpMacroAssemblerSW64::CheckCharacterNotInRange( ++ base::uc16 from, base::uc16 to, Label* on_not_in_range) { ++ __ Subl(a0, current_character(), Operand(from)); ++ // Unsigned higher condition. ++ BranchOrBacktrack(on_not_in_range, hi, a0, Operand(to - from)); ++} ++ ++void RegExpMacroAssemblerSW64::CallIsCharacterInRangeArray( ++ const ZoneList* ranges) { ++ static const int kNumArguments = 3; ++ __ PrepareCallCFunction(kNumArguments, a0); ++ ++ __ mov(a0, current_character()); ++ __ li(a1, Operand(GetOrAddRangeArray(ranges))); ++ __ li(a2, Operand(ExternalReference::isolate_address(isolate()))); ++ ++ { ++ // We have a frame (set up in GetCode), but the assembler doesn't know. ++ FrameScope scope(masm_.get(), StackFrame::MANUAL); ++ __ CallCFunction(ExternalReference::re_is_character_in_range_array(), ++ kNumArguments); ++ } ++ ++ __ li(code_pointer(), Operand(masm_->CodeObject())); ++} ++ ++bool RegExpMacroAssemblerSW64::CheckCharacterInRangeArray( ++ const ZoneList* ranges, Label* on_in_range) { ++ CallIsCharacterInRangeArray(ranges); ++ BranchOrBacktrack(on_in_range, ne, v0, Operand(zero_reg)); ++ return true; ++} ++ ++bool RegExpMacroAssemblerSW64::CheckCharacterNotInRangeArray( ++ const ZoneList* ranges, Label* on_not_in_range) { ++ CallIsCharacterInRangeArray(ranges); ++ BranchOrBacktrack(on_not_in_range, eq, v0, Operand(zero_reg)); ++ return true; ++} ++ ++void RegExpMacroAssemblerSW64::CheckBitInTable(Handle table, ++ Label* on_bit_set) { ++ __ li(a0, Operand(table)); ++ if (mode_ != LATIN1 || kTableMask != String::kMaxOneByteCharCode) { ++ __ And(a1, current_character(), Operand(kTableSize - 1)); ++ __ Addl(a0, a0, a1); ++ } else { ++ __ Addl(a0, a0, current_character()); ++ } ++ ++ __ Ldbu(a0, FieldMemOperand(a0, ByteArray::kHeaderSize)); ++ BranchOrBacktrack(on_bit_set, ne, a0, Operand(zero_reg)); ++} ++ ++bool RegExpMacroAssemblerSW64::CheckSpecialCharacterClass( ++ StandardCharacterSet type, Label* on_no_match) { ++ // Range checks (c in min..max) are generally implemented by an unsigned ++ // (c - min) <= (max - min) check. ++ // TODO(jgruber): No custom implementation (yet): s(UC16), S(UC16). ++ switch (type) { ++ case StandardCharacterSet::kWhitespace: ++ // Match space-characters. ++ if (mode_ == LATIN1) { ++ // One byte space characters are '\t'..'\r', ' ' and \u00a0. ++ Label success; ++ __ Branch(&success, eq, current_character(), Operand(' ')); ++ // Check range 0x09..0x0D. ++ __ Subl(a0, current_character(), Operand('\t')); ++ __ Branch(&success, ls, a0, Operand('\r' - '\t')); ++ // \u00a0 (NBSP). ++ BranchOrBacktrack(on_no_match, ne, a0, Operand(0x00A0 - '\t')); ++ __ bind(&success); ++ return true; ++ } ++ return false; ++ case StandardCharacterSet::kNotWhitespace: ++ // The emitted code for generic character classes is good enough. ++ return false; ++ case StandardCharacterSet::kDigit: ++ // Match Latin1 digits ('0'..'9'). ++ __ Subl(a0, current_character(), Operand('0')); ++ BranchOrBacktrack(on_no_match, hi, a0, Operand('9' - '0')); ++ return true; ++ case StandardCharacterSet::kNotDigit: ++ // Match non Latin1-digits. ++ __ Subl(a0, current_character(), Operand('0')); ++ BranchOrBacktrack(on_no_match, ls, a0, Operand('9' - '0')); ++ return true; ++ case StandardCharacterSet::kNotLineTerminator: { ++ // Match non-newlines (not 0x0A('\n'), 0x0D('\r'), 0x2028 and 0x2029). ++ __ Xor(a0, current_character(), Operand(0x01)); ++ // See if current character is '\n'^1 or '\r'^1, i.e., 0x0B or 0x0C. ++ __ Subl(a0, a0, Operand(0x0B)); ++ BranchOrBacktrack(on_no_match, ls, a0, Operand(0x0C - 0x0B)); ++ if (mode_ == UC16) { ++ // Compare original value to 0x2028 and 0x2029, using the already ++ // computed (current_char ^ 0x01 - 0x0B). I.e., check for ++ // 0x201D (0x2028 - 0x0B) or 0x201E. ++ __ Subl(a0, a0, Operand(0x2028 - 0x0B)); ++ BranchOrBacktrack(on_no_match, ls, a0, Operand(1)); ++ } ++ return true; ++ } ++ case StandardCharacterSet::kLineTerminator: { ++ // Match newlines (0x0A('\n'), 0x0D('\r'), 0x2028 and 0x2029). ++ __ Xor(a0, current_character(), Operand(0x01)); ++ // See if current character is '\n'^1 or '\r'^1, i.e., 0x0B or 0x0C. ++ __ Subl(a0, a0, Operand(0x0B)); ++ if (mode_ == LATIN1) { ++ BranchOrBacktrack(on_no_match, hi, a0, Operand(0x0C - 0x0B)); ++ } else { ++ Label done; ++ BranchOrBacktrack(&done, ls, a0, Operand(0x0C - 0x0B)); ++ // Compare original value to 0x2028 and 0x2029, using the already ++ // computed (current_char ^ 0x01 - 0x0B). I.e., check for ++ // 0x201D (0x2028 - 0x0B) or 0x201E. ++ __ Subl(a0, a0, Operand(0x2028 - 0x0B)); ++ BranchOrBacktrack(on_no_match, hi, a0, Operand(1)); ++ __ bind(&done); ++ } ++ return true; ++ } ++ case StandardCharacterSet::kWord: { ++ if (mode_ != LATIN1) { ++ // Table is 256 entries, so all Latin1 characters can be tested. ++ BranchOrBacktrack(on_no_match, hi, current_character(), Operand('z')); ++ } ++ ExternalReference map = ExternalReference::re_word_character_map(); ++ __ li(a0, Operand(map)); ++ __ Addl(a0, a0, current_character()); ++ __ Ldbu(a0, MemOperand(a0, 0)); ++ BranchOrBacktrack(on_no_match, eq, a0, Operand(zero_reg)); ++ return true; ++ } ++ case StandardCharacterSet::kNotWord: { ++ Label done; ++ if (mode_ != LATIN1) { ++ // Table is 256 entries, so all Latin1 characters can be tested. ++ __ Branch(&done, hi, current_character(), Operand('z')); ++ } ++ ExternalReference map = ExternalReference::re_word_character_map(); ++ __ li(a0, Operand(map)); ++ __ Addl(a0, a0, current_character()); ++ __ Ldbu(a0, MemOperand(a0, 0)); ++ BranchOrBacktrack(on_no_match, ne, a0, Operand(zero_reg)); ++ if (mode_ != LATIN1) { ++ __ bind(&done); ++ } ++ return true; ++ } ++ case StandardCharacterSet::kEverything: ++ // Match any character. ++ return true; ++ } ++} ++ ++void RegExpMacroAssemblerSW64::Fail() { ++ __ li(v0, Operand(FAILURE)); ++ __ jmp(&exit_label_); ++} ++ ++void RegExpMacroAssemblerSW64::LoadRegExpStackPointerFromMemory(Register dst) { ++ ExternalReference ref = ++ ExternalReference::address_of_regexp_stack_stack_pointer(isolate()); ++ __ li(dst, Operand(ref)); ++ __ Ldl(dst, MemOperand(dst)); ++} ++ ++void RegExpMacroAssemblerSW64::StoreRegExpStackPointerToMemory( ++ Register src, Register scratch) { ++ ExternalReference ref = ++ ExternalReference::address_of_regexp_stack_stack_pointer(isolate()); ++ __ li(scratch, Operand(ref)); ++ __ Stl(src, MemOperand(scratch)); ++} ++ ++void RegExpMacroAssemblerSW64::PushRegExpBasePointer(Register stack_pointer, ++ Register scratch) { ++ ExternalReference ref = ++ ExternalReference::address_of_regexp_stack_memory_top_address(isolate()); ++ __ li(scratch, Operand(ref)); ++ __ Ldl(scratch, MemOperand(scratch)); ++ __ Subl(scratch, stack_pointer, scratch); ++ __ Stl(scratch, MemOperand(frame_pointer(), kRegExpStackBasePointer)); ++} ++ ++void RegExpMacroAssemblerSW64::PopRegExpBasePointer(Register stack_pointer_out, ++ Register scratch) { ++ ExternalReference ref = ++ ExternalReference::address_of_regexp_stack_memory_top_address(isolate()); ++ __ Ldl(stack_pointer_out, ++ MemOperand(frame_pointer(), kRegExpStackBasePointer)); ++ __ li(scratch, Operand(ref)); ++ __ Ldl(scratch, MemOperand(scratch)); ++ __ Addl(stack_pointer_out, stack_pointer_out, scratch); ++ StoreRegExpStackPointerToMemory(stack_pointer_out, scratch); ++} ++ ++Handle RegExpMacroAssemblerSW64::GetCode(Handle source) { ++ Label return_v0; ++ if (masm_->has_exception()) { ++ // If the code gets corrupted due to long regular expressions and lack of ++ // space on trampolines, an internal exception flag is set. If this case ++ // is detected, we will jump into exit sequence right away. ++ __ bind_to(&entry_label_, internal_failure_label_.pos()); ++ } else { ++ // Finalize code - write the entry point code now we know how many ++ // registers we need. ++ ++ // Entry code: ++ __ bind(&entry_label_); ++ ++ // Tell the system that we have a stack frame. Because the type is MANUAL, ++ // no is generated. ++ FrameScope scope(masm_.get(), StackFrame::MANUAL); ++ ++ // Actually emit code to start a new stack frame. ++ // Push arguments ++ // Save callee-save registers. ++ // Start new stack frame. ++ // Store link register in existing stack-cell. ++ // Order here should correspond to order of offset constants in header file. ++ // TODO(plind): we save s0..s5, but ONLY use s3 here - use the regs ++ // or dont save. ++ RegList registers_to_retain = {s0, s1, s2, s3, s4, s5, fp}; ++ RegList argument_registers = {a0, a1, a2, a3, a4, a5}; ++ ++ __ MultiPush(registers_to_retain | ra); ++ __ MultiPush(argument_registers); ++ // Set frame pointer in space for it if this is not a direct call ++ // from generated code. ++ __ Addl(frame_pointer(), sp, Operand(6 * kPointerSize)); // 6 argument regs ++ STATIC_ASSERT(kSuccessfulCaptures == kInputString - kSystemPointerSize); ++ __ mov(a0, zero_reg); ++ __ Push(a0); // Make room for success counter and initialize it to 0. ++ STATIC_ASSERT(kStringStartMinusOne == ++ kSuccessfulCaptures - kSystemPointerSize); ++ __ Push(a0); // Make room for "string start - 1" constant. ++ STATIC_ASSERT(kBacktrackCount == kStringStartMinusOne - kSystemPointerSize); ++ __ Push(a0); // The backtrack counter ++ STATIC_ASSERT(kRegExpStackBasePointer == ++ kBacktrackCount - kSystemPointerSize); ++ __ Push(a0); // The regexp stack base ptr. ++ ++ // Initialize backtrack stack pointer. It must not be clobbered from here ++ // on. Note the backtrack_stackpointer is callee-saved. ++//SKTODO ++ STATIC_ASSERT(backtrack_stackpointer() == s5); ++ LoadRegExpStackPointerFromMemory(backtrack_stackpointer()); ++ ++ // Store the regexp base pointer - we'll later restore it / write it to ++ // memory when returning from this irregexp code object. ++ PushRegExpBasePointer(backtrack_stackpointer(), a1); ++ ++ { ++ // Check if we have space on the stack for registers. ++ Label stack_limit_hit, stack_ok; ++ ++ ExternalReference stack_limit = ++ ExternalReference::address_of_jslimit(masm_->isolate()); ++ __ li(a0, Operand(stack_limit)); ++ __ Ldl(a0, MemOperand(a0)); ++ __ Subl(a0, sp, a0); ++ // Handle it if the stack pointer is already below the stack limit. ++ __ Branch(&stack_limit_hit, le, a0, Operand(zero_reg)); ++ // Check if there is room for the variable number of registers above ++ // the stack limit. ++ __ Branch(&stack_ok, hs, a0, Operand(num_registers_ * kPointerSize)); ++ // Exit with OutOfMemory exception. There is not enough space on the stack ++ // for our working registers. ++ __ li(v0, Operand(EXCEPTION)); ++ __ jmp(&return_v0); ++ ++ __ bind(&stack_limit_hit); ++ CallCheckStackGuardState(a0); ++ // If returned value is non-zero, we exit with the returned value as ++ // result. ++ __ Branch(&return_v0, ne, v0, Operand(zero_reg)); ++ ++ __ bind(&stack_ok); ++ } ++ ++ // Allocate space on stack for registers. ++ __ Subl(sp, sp, Operand(num_registers_ * kPointerSize)); ++ // Load string end. ++ __ Ldl(end_of_input_address(), MemOperand(frame_pointer(), kInputEnd)); ++ // Load input start. ++ __ Ldl(a0, MemOperand(frame_pointer(), kInputStart)); ++ // Find negative length (offset of start relative to end). ++ __ Subl(current_input_offset(), a0, end_of_input_address()); ++ // Set a0 to address of char before start of the input string ++ // (effectively string position -1). ++ __ Ldl(a1, MemOperand(frame_pointer(), kStartIndex)); ++ __ Subl(a0, current_input_offset(), Operand(char_size())); ++ __ slll(a1, (mode_ == UC16) ? 1 : 0, t1); ++ __ Subl(a0, a0, t1); ++ // Store this value in a local variable, for use when clearing ++ // position registers. ++ __ Stl(a0, MemOperand(frame_pointer(), kStringStartMinusOne)); ++ ++ // Initialize code pointer register ++ __ li(code_pointer(), Operand(masm_->CodeObject()), CONSTANT_SIZE); ++ ++ Label load_char_start_regexp; ++ { ++ Label start_regexp; ++ // Load newline if index is at start, previous character otherwise. ++ __ Branch(&load_char_start_regexp, ne, a1, Operand(zero_reg)); ++ __ li(current_character(), Operand('\n')); ++ __ jmp(&start_regexp); ++ ++ // Global regexp restarts matching here. ++ __ bind(&load_char_start_regexp); ++ // Load previous char as initial value of current character register. ++ LoadCurrentCharacterUnchecked(-1, 1); ++ __ bind(&start_regexp); ++ } ++ ++ // Initialize on-stack registers. ++ if (num_saved_registers_ > 0) { // Always is, if generated from a regexp. ++ // Fill saved registers with initial value = start offset - 1. ++ if (num_saved_registers_ > 8) { ++ // Address of register 0. ++ __ Addl(a1, frame_pointer(), Operand(kRegisterZero)); ++ __ li(a2, Operand(num_saved_registers_)); ++ Label init_loop; ++ __ bind(&init_loop); ++ __ Stl(a0, MemOperand(a1)); ++ __ Addl(a1, a1, Operand(-kPointerSize)); ++ __ Subl(a2, a2, Operand(1)); ++ __ Branch(&init_loop, ne, a2, Operand(zero_reg)); ++ } else { ++ for (int i = 0; i < num_saved_registers_; i++) { ++ __ Stl(a0, register_location(i)); ++ } ++ } ++ } ++ ++ __ jmp(&start_label_); ++ ++ // Exit code: ++ if (success_label_.is_linked()) { ++ // Save captures when successful. ++ __ bind(&success_label_); ++ if (num_saved_registers_ > 0) { ++ // Copy captures to output. ++ __ Ldl(a1, MemOperand(frame_pointer(), kInputStart)); ++ __ Ldl(a0, MemOperand(frame_pointer(), kRegisterOutput)); ++ __ Ldl(a2, MemOperand(frame_pointer(), kStartIndex)); ++ __ Subl(a1, end_of_input_address(), a1); ++ // a1 is length of input in bytes. ++ if (mode_ == UC16) { ++ __ srll(a1, 1, a1); ++ } ++ // a1 is length of input in characters. ++ __ Addl(a1, a1, Operand(a2)); ++ // a1 is length of string in characters. ++ ++ DCHECK_EQ(0, num_saved_registers_ % 2); ++ // Always an even number of capture registers. This allows us to ++ // unroll the loop once to add an operation between a load of a register ++ // and the following use of that register. ++ for (int i = 0; i < num_saved_registers_; i += 2) { ++ __ Ldl(a2, register_location(i)); ++ __ Ldl(a3, register_location(i + 1)); ++ if (i == 0 && global_with_zero_length_check()) { ++ // Keep capture start in a4 for the zero-length check later. ++ __ mov(t3, a2); ++ } ++ if (mode_ == UC16) { ++ __ sral(a2, 1, a2); ++ __ Addl(a2, a2, a1); ++ __ sral(a3, 1, a3); ++ __ Addl(a3, a3, a1); ++ } else { ++ __ Addl(a2, a1, Operand(a2)); ++ __ Addl(a3, a1, Operand(a3)); ++ } ++ // V8 expects the output to be an int32_t array. ++ __ Stw(a2, MemOperand(a0)); ++ __ Addl(a0, a0, kIntSize); ++ __ Stw(a3, MemOperand(a0)); ++ __ Addl(a0, a0, kIntSize); ++ } ++ } ++ ++ if (global()) { ++ // Restart matching if the regular expression is flagged as global. ++ __ Ldl(a0, MemOperand(frame_pointer(), kSuccessfulCaptures)); ++ __ Ldl(a1, MemOperand(frame_pointer(), kNumOutputRegisters)); ++ __ Ldl(a2, MemOperand(frame_pointer(), kRegisterOutput)); ++ // Increment success counter. ++ __ Addl(a0, a0, 1); ++ __ Stl(a0, MemOperand(frame_pointer(), kSuccessfulCaptures)); ++ // Capture results have been stored, so the number of remaining global ++ // output registers is reduced by the number of stored captures. ++ __ Subl(a1, a1, num_saved_registers_); ++ // Check whether we have enough room for another set of capture results. ++ __ mov(v0, a0); ++ __ Branch(&return_v0, lt, a1, Operand(num_saved_registers_)); ++ ++ __ Stl(a1, MemOperand(frame_pointer(), kNumOutputRegisters)); ++ // Advance the location for output. ++ __ Addl(a2, a2, num_saved_registers_ * kIntSize); ++ __ Stl(a2, MemOperand(frame_pointer(), kRegisterOutput)); ++ ++ // Prepare a0 to initialize registers with its value in the next run. ++ __ Ldl(a0, MemOperand(frame_pointer(), kStringStartMinusOne)); ++ ++ // Restore the original regexp stack pointer value (effectively, pop the ++ // stored base pointer). ++ PopRegExpBasePointer(backtrack_stackpointer(), a2); ++ ++ if (global_with_zero_length_check()) { ++ // Special case for zero-length matches. ++ // t3: capture start index ++ // Not a zero-length match, restart. ++ __ Branch( ++ &load_char_start_regexp, ne, current_input_offset(), Operand(t3)); ++ // Offset from the end is zero if we already reached the end. ++ __ Branch(&exit_label_, eq, current_input_offset(), ++ Operand(zero_reg)); ++ // Advance current position after a zero-length match. ++ Label advance; ++ __ bind(&advance); ++ __ Addl(current_input_offset(), current_input_offset(), ++ Operand((mode_ == UC16) ? 2 : 1)); ++ if (global_unicode()) CheckNotInSurrogatePair(0, &advance); ++ } ++ ++ __ Branch(&load_char_start_regexp); ++ } else { ++ __ li(v0, Operand(SUCCESS)); ++ } ++ } ++ // Exit and return v0. ++ __ bind(&exit_label_); ++ if (global()) { ++ __ Ldl(v0, MemOperand(frame_pointer(), kSuccessfulCaptures)); ++ } ++ ++ __ bind(&return_v0); ++ // Restore the original regexp stack pointer value (effectively, pop the ++ // stored base pointer). ++ PopRegExpBasePointer(backtrack_stackpointer(), a1); ++ ++ // Skip sp past regexp registers and local variables.. ++ __ mov(sp, frame_pointer()); ++ // Restore registers s0..s7 and return (restoring ra to pc). ++ __ MultiPop(registers_to_retain | ra); ++ __ Ret(); ++ ++ // Backtrack code (branch target for conditional backtracks). ++ if (backtrack_label_.is_linked()) { ++ __ bind(&backtrack_label_); ++ Backtrack(); ++ } ++ ++ Label exit_with_exception; ++ ++ // Preempt-code. ++ if (check_preempt_label_.is_linked()) { ++ SafeCallTarget(&check_preempt_label_); ++ StoreRegExpStackPointerToMemory(backtrack_stackpointer(), a0); ++ ++ CallCheckStackGuardState(a0); ++ // If returning non-zero, we should end execution with the given ++ // result as return value. ++ __ Branch(&return_v0, ne, v0, Operand(zero_reg)); ++ ++ LoadRegExpStackPointerFromMemory(backtrack_stackpointer()); ++ ++ // String might have moved: Reload end of string from frame. ++ __ Ldl(end_of_input_address(), MemOperand(frame_pointer(), kInputEnd)); ++ SafeReturn(); ++ } ++ ++ // Backtrack stack overflow code. ++ if (stack_overflow_label_.is_linked()) { ++ SafeCallTarget(&stack_overflow_label_); ++ StoreRegExpStackPointerToMemory(backtrack_stackpointer(), a0); ++ // Reached if the backtrack-stack limit has been hit. ++ ++ // Call GrowStack(isolate) ++ static constexpr int kNumArguments = 1; ++ __ PrepareCallCFunction(kNumArguments, a0); ++ __ li(a0, Operand(ExternalReference::isolate_address(masm_->isolate()))); ++ ExternalReference grow_stack = ExternalReference::re_grow_stack(); ++ __ CallCFunction(grow_stack, kNumArguments); ++ // If nullptr is returned, we have failed to grow the stack, and must exit ++ // with a stack-overflow exception. ++ __ Branch(&exit_with_exception, eq, v0, Operand(zero_reg)); ++ // Otherwise use return value as new stack pointer. ++ __ mov(backtrack_stackpointer(), v0); ++ SafeReturn(); ++ } ++ ++ if (exit_with_exception.is_linked()) { ++ // If any of the code above needed to exit with an exception. ++ __ bind(&exit_with_exception); ++ // Exit with Result EXCEPTION(-1) to signal thrown exception. ++ __ li(v0, Operand(EXCEPTION)); ++ __ jmp(&return_v0); ++ } ++ ++ if (fallback_label_.is_linked()) { ++ __ bind(&fallback_label_); ++ __ li(v0, Operand(FALLBACK_TO_EXPERIMENTAL)); ++ __ jmp(&return_v0); ++ } ++ } ++ ++ CodeDesc code_desc; ++ masm_->GetCode(isolate(), &code_desc); ++ Handle code = ++ Factory::CodeBuilder(isolate(), code_desc, CodeKind::REGEXP) ++ .set_self_reference(masm_->CodeObject()) ++ .Build(); ++ LOG(masm_->isolate(), ++ RegExpCodeCreateEvent(Handle::cast(code), source)); ++ return Handle::cast(code); ++} ++ ++void RegExpMacroAssemblerSW64::GoTo(Label* to) { ++ if (to == nullptr) { ++ Backtrack(); ++ return; ++ } ++ __ jmp(to); ++ return; ++} ++ ++void RegExpMacroAssemblerSW64::IfRegisterGE(int reg, ++ int comparand, ++ Label* if_ge) { ++ __ Ldl(a0, register_location(reg)); ++ BranchOrBacktrack(if_ge, ge, a0, Operand(comparand)); ++} ++ ++void RegExpMacroAssemblerSW64::IfRegisterLT(int reg, ++ int comparand, ++ Label* if_lt) { ++ __ Ldl(a0, register_location(reg)); ++ BranchOrBacktrack(if_lt, lt, a0, Operand(comparand)); ++} ++ ++void RegExpMacroAssemblerSW64::IfRegisterEqPos(int reg, ++ Label* if_eq) { ++ __ Ldl(a0, register_location(reg)); ++ BranchOrBacktrack(if_eq, eq, a0, Operand(current_input_offset())); ++} ++ ++RegExpMacroAssembler::IrregexpImplementation ++ RegExpMacroAssemblerSW64::Implementation() { ++ return kSW64Implementation; ++} ++ ++void RegExpMacroAssemblerSW64::PopCurrentPosition() { ++ Pop(current_input_offset()); ++} ++ ++void RegExpMacroAssemblerSW64::PopRegister(int register_index) { ++ Pop(a0); ++ __ Stl(a0, register_location(register_index)); ++} ++ ++void RegExpMacroAssemblerSW64::PushBacktrack(Label* label) { ++ if (label->is_bound()) { ++ int target = label->pos(); ++ __ li(a0, Operand(target + Code::kHeaderSize - kHeapObjectTag)); ++ } else { ++ Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm_.get()); ++ Label after_constant; ++ __ Branch(&after_constant); ++ int offset = masm_->pc_offset(); ++ int cp_offset = offset + Code::kHeaderSize - kHeapObjectTag; ++ __ emit(0); ++ masm_->label_at_put(label, offset); ++ __ bind(&after_constant); ++ if (is_int16(cp_offset)) { ++ __ Ldwu(a0, MemOperand(code_pointer(), cp_offset)); ++ } else { ++ __ Addl(a0, code_pointer(), cp_offset); ++ __ Ldwu(a0, MemOperand(a0, 0)); ++ } ++ } ++ Push(a0); ++ CheckStackLimit(); ++} ++ ++void RegExpMacroAssemblerSW64::PushCurrentPosition() { ++ Push(current_input_offset()); ++} ++ ++void RegExpMacroAssemblerSW64::PushRegister(int register_index, ++ StackCheckFlag check_stack_limit) { ++ __ Ldl(a0, register_location(register_index)); ++ Push(a0); ++ if (check_stack_limit) CheckStackLimit(); ++} ++ ++void RegExpMacroAssemblerSW64::ReadCurrentPositionFromRegister(int reg) { ++ __ Ldl(current_input_offset(), register_location(reg)); ++} ++ ++void RegExpMacroAssemblerSW64::WriteStackPointerToRegister(int reg) { ++ ExternalReference ref = ++ ExternalReference::address_of_regexp_stack_memory_top_address(isolate()); ++ __ li(a0, Operand(ref)); ++ __ Ldl(a0, MemOperand(a0)); ++ __ Subl(a0, backtrack_stackpointer(), a0); ++ __ Stl(a0, register_location(reg)); ++} ++ ++void RegExpMacroAssemblerSW64::ReadStackPointerFromRegister(int reg) { ++ ExternalReference ref = ++ ExternalReference::address_of_regexp_stack_memory_top_address(isolate()); ++ __ li(a0, Operand(ref)); ++ __ Ldl(a0, MemOperand(a0)); ++ __ Ldl(backtrack_stackpointer(), register_location(reg)); ++ __ Addl(backtrack_stackpointer(), backtrack_stackpointer(), Operand(a0)); ++} ++ ++void RegExpMacroAssemblerSW64::SetCurrentPositionFromEnd(int by) { ++ Label after_position; ++ __ Branch(&after_position, ge, current_input_offset(), ++ Operand(-by * char_size())); ++ __ li(current_input_offset(), -by * char_size()); ++ // On RegExp code entry (where this operation is used), the character before ++ // the current position is expected to be already loaded. ++ // We have advanced the position, so it's safe to read backwards. ++ LoadCurrentCharacterUnchecked(-1, 1); ++ __ bind(&after_position); ++} ++ ++void RegExpMacroAssemblerSW64::SetRegister(int register_index, int to) { ++ DCHECK(register_index >= num_saved_registers_); // Reserved for positions! ++ __ li(a0, Operand(to)); ++ __ Stl(a0, register_location(register_index)); ++} ++ ++bool RegExpMacroAssemblerSW64::Succeed() { ++ __ jmp(&success_label_); ++ return global(); ++} ++ ++void RegExpMacroAssemblerSW64::WriteCurrentPositionToRegister(int reg, ++ int cp_offset) { ++ if (cp_offset == 0) { ++ __ Stl(current_input_offset(), register_location(reg)); ++ } else { ++ __ Addl(a0, current_input_offset(), Operand(cp_offset * char_size())); ++ __ Stl(a0, register_location(reg)); ++ } ++} ++ ++void RegExpMacroAssemblerSW64::ClearRegisters(int reg_from, int reg_to) { ++ DCHECK(reg_from <= reg_to); ++ __ Ldl(a0, MemOperand(frame_pointer(), kStringStartMinusOne)); ++ for (int reg = reg_from; reg <= reg_to; reg++) { ++ __ Stl(a0, register_location(reg)); ++ } ++} ++ ++bool RegExpMacroAssemblerSW64::CanReadUnaligned() const { ++ return false; ++} ++ ++// Private methods: ++ ++void RegExpMacroAssemblerSW64::CallCheckStackGuardState(Register scratch) { ++ DCHECK(!isolate()->IsGeneratingEmbeddedBuiltins()); ++ DCHECK(!masm_->options().isolate_independent_code); ++ ++ int stack_alignment = base::OS::ActivationFrameAlignment(); ++ ++ // Align the stack pointer and save the original sp value on the stack. ++ __ mov(scratch, sp); ++ __ Subl(sp, sp, Operand(kPointerSize)); ++ DCHECK(base::bits::IsPowerOfTwo(stack_alignment)); ++ __ And(sp, sp, Operand(-stack_alignment)); ++ __ Stl(scratch, MemOperand(sp)); ++ ++ __ mov(a2, frame_pointer()); ++ // Code of self. ++ __ li(a1, Operand(masm_->CodeObject()), CONSTANT_SIZE); ++ ++ // We need to make room for the return address on the stack. ++ DCHECK(IsAligned(stack_alignment, kPointerSize)); ++ __ Subl(sp, sp, Operand(stack_alignment)); ++ ++ // The stack pointer now points to cell where the return address will be ++ // written. Arguments are in registers, meaning we treat the return address as ++ // argument 5. Since DirectCEntry will handle allocating space for the C ++ // argument slots, we don't need to care about that here. This is how the ++ // stack will look (sp meaning the value of sp at this moment): ++ // [sp + 3] - empty slot if needed for alignment. ++ // [sp + 2] - saved sp. ++ // [sp + 1] - second word reserved for return value. ++ // [sp + 0] - first word reserved for return value. ++ ++ // a0 will point to the return address, placed by DirectCEntry. ++ __ mov(a0, sp); ++ ++ ExternalReference stack_guard_check = ++ ExternalReference::re_check_stack_guard_state(); ++ __ li(t12, Operand(stack_guard_check)); ++ ++ EmbeddedData d = EmbeddedData::FromBlob(); ++ CHECK(Builtins::IsIsolateIndependent(Builtin::kDirectCEntry)); ++ Address entry = d.InstructionStartOfBuiltin(Builtin::kDirectCEntry); ++ __ li(kScratchReg, Operand(entry, RelocInfo::OFF_HEAP_TARGET)); ++ __ Call(kScratchReg); ++ ++ // DirectCEntryStub allocated space for the C argument slots so we have to ++ // drop them with the return address from the stack with loading saved sp. ++ // At this point stack must look: ++ // [sp + 7] - empty slot if needed for alignment. ++ // [sp + 6] - saved sp. ++ // [sp + 5] - second word reserved for return value. ++ // [sp + 4] - first word reserved for return value. ++ // [sp + 3] - C argument slot. ++ // [sp + 2] - C argument slot. ++ // [sp + 1] - C argument slot. ++ // [sp + 0] - C argument slot. ++ __ Ldl(sp, MemOperand(sp, stack_alignment + kCArgsSlotsSize)); ++ ++ __ li(code_pointer(), Operand(masm_->CodeObject())); ++} ++ ++// Helper function for reading a value out of a stack frame. ++template ++static T& frame_entry(Address re_frame, int frame_offset) { ++ return reinterpret_cast(Memory(re_frame + frame_offset)); ++} ++ ++template ++static T* frame_entry_address(Address re_frame, int frame_offset) { ++ return reinterpret_cast(re_frame + frame_offset); ++} ++ ++int64_t RegExpMacroAssemblerSW64::CheckStackGuardState(Address* return_address, ++ Address raw_code, ++ Address re_frame) { ++ Code re_code = Code::cast(Object(raw_code)); ++ return NativeRegExpMacroAssembler::CheckStackGuardState( ++ frame_entry(re_frame, kIsolate), ++ static_cast(frame_entry(re_frame, kStartIndex)), ++ static_cast( ++ frame_entry(re_frame, kDirectCall)), ++ return_address, re_code, ++ frame_entry_address
(re_frame, kInputString), ++ frame_entry_address(re_frame, kInputStart), ++ frame_entry_address(re_frame, kInputEnd)); ++} ++ ++MemOperand RegExpMacroAssemblerSW64::register_location(int register_index) { ++ DCHECK(register_index < (1<<30)); ++ if (num_registers_ <= register_index) { ++ num_registers_ = register_index + 1; ++ } ++ return MemOperand(frame_pointer(), ++ kRegisterZero - register_index * kPointerSize); ++} ++ ++void RegExpMacroAssemblerSW64::CheckPosition(int cp_offset, ++ Label* on_outside_input) { ++ if (cp_offset >= 0) { ++ BranchOrBacktrack(on_outside_input, ge, current_input_offset(), ++ Operand(-cp_offset * char_size())); ++ } else { ++ __ Ldl(a1, MemOperand(frame_pointer(), kStringStartMinusOne)); ++ __ Addl(a0, current_input_offset(), Operand(cp_offset * char_size())); ++ BranchOrBacktrack(on_outside_input, le, a0, Operand(a1)); ++ } ++} ++ ++void RegExpMacroAssemblerSW64::BranchOrBacktrack(Label* to, ++ Condition condition, ++ Register rs, ++ const Operand& rt) { ++ if (condition == al) { // Unconditional. ++ if (to == nullptr) { ++ Backtrack(); ++ return; ++ } ++ __ jmp(to); ++ return; ++ } ++ if (to == nullptr) { ++ __ Branch(&backtrack_label_, condition, rs, rt); ++ return; ++ } ++ __ Branch(to, condition, rs, rt); ++} ++ ++void RegExpMacroAssemblerSW64::SafeCall(Label* to, ++ Condition cond, ++ Register rs, ++ const Operand& rt) { ++ __ BranchAndLink(to, cond, rs, rt); ++} ++ ++void RegExpMacroAssemblerSW64::SafeReturn() { ++ __ pop(ra); ++ __ Addl(t1, ra, Operand(masm_->CodeObject())); ++ __ Jump(t1); ++} ++ ++void RegExpMacroAssemblerSW64::SafeCallTarget(Label* name) { ++ __ bind(name); ++ __ Subl(ra, ra, Operand(masm_->CodeObject())); ++ __ Push(ra); ++} ++ ++void RegExpMacroAssemblerSW64::Push(Register source) { ++ DCHECK(source != backtrack_stackpointer()); ++ __ Addl(backtrack_stackpointer(), backtrack_stackpointer(), ++ Operand(-kIntSize)); ++ __ Stw(source, MemOperand(backtrack_stackpointer())); ++} ++ ++void RegExpMacroAssemblerSW64::Pop(Register target) { ++ DCHECK(target != backtrack_stackpointer()); ++ __ Ldw(target, MemOperand(backtrack_stackpointer())); ++ __ Addl(backtrack_stackpointer(), backtrack_stackpointer(), kIntSize); ++} ++ ++ ++void RegExpMacroAssemblerSW64::CheckPreemption() { ++ // Check for preemption. ++ ExternalReference stack_limit = ++ ExternalReference::address_of_jslimit(masm_->isolate()); ++ __ li(a0, Operand(stack_limit)); ++ __ Ldl(a0, MemOperand(a0)); ++ SafeCall(&check_preempt_label_, ls, sp, Operand(a0)); ++} ++ ++void RegExpMacroAssemblerSW64::CheckStackLimit() { ++ ExternalReference stack_limit = ++ ExternalReference::address_of_regexp_stack_limit_address( ++ masm_->isolate()); ++ ++ __ li(a0, Operand(stack_limit)); ++ __ Ldl(a0, MemOperand(a0)); ++ SafeCall(&stack_overflow_label_, ls, backtrack_stackpointer(), Operand(a0)); ++} ++ ++void RegExpMacroAssemblerSW64::LoadCurrentCharacterUnchecked(int cp_offset, ++ int characters) { ++ Register offset = current_input_offset(); ++ if (cp_offset != 0) { ++ // t3 is not being used to store the capture start index at this point. ++ __ Addl(t3, current_input_offset(), Operand(cp_offset * char_size())); ++ offset = t3; ++ } ++ // We assume that we cannot do unaligned loads on SW64, so this function ++ // must only be used to load a single character at a time. ++ DCHECK_EQ(1, characters); ++ __ Addl(t1, end_of_input_address(), Operand(offset)); ++ if (mode_ == LATIN1) { ++ __ Ldbu(current_character(), MemOperand(t1, 0)); ++ } else { ++ DCHECK(mode_ == UC16); ++ __ Ldhu(current_character(), MemOperand(t1, 0)); ++ } ++} ++ ++#undef __ ++ ++} // namespace internal ++} // namespace v8 ++ ++#endif // V8_TARGET_ARCH_SW64 +diff --git a/deps/v8/src/regexp/sw64/regexp-macro-assembler-sw64.h b/deps/v8/src/regexp/sw64/regexp-macro-assembler-sw64.h +new file mode 100644 +index 00000000..9de2936b +--- /dev/null ++++ b/deps/v8/src/regexp/sw64/regexp-macro-assembler-sw64.h +@@ -0,0 +1,233 @@ ++// Copyright 2011 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef V8_REGEXP_SW64_REGEXP_MACRO_ASSEMBLER_SW64_H_ ++#define V8_REGEXP_SW64_REGEXP_MACRO_ASSEMBLER_SW64_H_ ++ ++#include "src/codegen/macro-assembler.h" ++#include "src/regexp/regexp-macro-assembler.h" ++ ++namespace v8 { ++namespace internal { ++ ++class V8_EXPORT_PRIVATE RegExpMacroAssemblerSW64 ++ : public NativeRegExpMacroAssembler { ++ public: ++ RegExpMacroAssemblerSW64(Isolate* isolate, Zone* zone, Mode mode, ++ int registers_to_save); ++ ~RegExpMacroAssemblerSW64() override; ++ int stack_limit_slack() override; ++ void AdvanceCurrentPosition(int by) override; ++ void AdvanceRegister(int reg, int by) override; ++ void Backtrack() override; ++ void Bind(Label* label) override; ++ void CheckAtStart(int cp_offset, Label* on_at_start) override; ++ void CheckCharacter(uint32_t c, Label* on_equal) override; ++ void CheckCharacterAfterAnd(uint32_t c, uint32_t mask, ++ Label* on_equal) override; ++ void CheckCharacterGT(base::uc16 limit, Label* on_greater) override; ++ void CheckCharacterLT(base::uc16 limit, Label* on_less) override; ++ // A "greedy loop" is a loop that is both greedy and with a simple ++ // body. It has a particularly simple implementation. ++ void CheckGreedyLoop(Label* on_tos_equals_current_position) override; ++ void CheckNotAtStart(int cp_offset, Label* on_not_at_start) override; ++ void CheckNotBackReference(int start_reg, bool read_backward, ++ Label* on_no_match) override; ++ void CheckNotBackReferenceIgnoreCase(int start_reg, bool read_backward, ++ bool unicode, ++ Label* on_no_match) override; ++ void CheckNotCharacter(uint32_t c, Label* on_not_equal) override; ++ void CheckNotCharacterAfterAnd(uint32_t c, uint32_t mask, ++ Label* on_not_equal) override; ++ void CheckNotCharacterAfterMinusAnd(base::uc16 c, base::uc16 minus, ++ base::uc16 mask, ++ Label* on_not_equal) override; ++ void CheckCharacterInRange(base::uc16 from, base::uc16 to, ++ Label* on_in_range) override; ++ void CheckCharacterNotInRange(base::uc16 from, base::uc16 to, ++ Label* on_not_in_range) override; ++ bool CheckCharacterInRangeArray(const ZoneList* ranges, ++ Label* on_in_range) override; ++ bool CheckCharacterNotInRangeArray(const ZoneList* ranges, ++ Label* on_not_in_range) override; ++ void CheckBitInTable(Handle table, Label* on_bit_set) override; ++ ++ // Checks whether the given offset from the current position is before ++ // the end of the string. ++ void CheckPosition(int cp_offset, Label* on_outside_input) override; ++ bool CheckSpecialCharacterClass(StandardCharacterSet type, ++ Label* on_no_match) override; ++ void Fail() override; ++ Handle GetCode(Handle source) override; ++ void GoTo(Label* label) override; ++ void IfRegisterGE(int reg, int comparand, Label* if_ge) override; ++ void IfRegisterLT(int reg, int comparand, Label* if_lt) override; ++ void IfRegisterEqPos(int reg, Label* if_eq) override; ++ IrregexpImplementation Implementation() override; ++ void LoadCurrentCharacterUnchecked(int cp_offset, ++ int character_count) override; ++ void PopCurrentPosition() override; ++ void PopRegister(int register_index) override; ++ void PushBacktrack(Label* label) override; ++ void PushCurrentPosition() override; ++ void PushRegister(int register_index, ++ StackCheckFlag check_stack_limit) override; ++ void ReadCurrentPositionFromRegister(int reg) override; ++ void ReadStackPointerFromRegister(int reg) override; ++ void SetCurrentPositionFromEnd(int by) override; ++ void SetRegister(int register_index, int to) override; ++ bool Succeed() override; ++ void WriteCurrentPositionToRegister(int reg, int cp_offset) override; ++ void ClearRegisters(int reg_from, int reg_to) override; ++ void WriteStackPointerToRegister(int reg) override; ++ bool CanReadUnaligned() const override; ++ ++ // Called from RegExp if the stack-guard is triggered. ++ // If the code object is relocated, the return address is fixed before ++ // returning. ++ // {raw_code} is an Address because this is called via ExternalReference. ++ static int64_t CheckStackGuardState(Address* return_address, Address raw_code, ++ Address re_frame); ++ ++ void print_regexp_frame_constants(); ++ ++ private: ++ // Offsets from frame_pointer() of function parameters and stored registers. ++ static const int kFramePointer = 0; ++ ++ // Above the frame pointer - Stored registers and stack passed parameters. ++ static const int kStoredRegisters = kFramePointer; ++ // Return address (stored from link register, read into pc on return). ++ ++ // TODO(plind): This 7 - is 6 s-regs (s0..s5) plus fp. ++ static const int kReturnAddress = kStoredRegisters + 7 * kSystemPointerSize; ++ ++ // Stack frame header. ++ static const int kStackFrameHeader = kReturnAddress; ++ // Stack parameters placed by caller. ++ static const int kDirectCall = kStackFrameHeader + kSystemPointerSize; ++ static const int kIsolate = kDirectCall + kSystemPointerSize; ++ ++ // Below the frame pointer. ++ // Register parameters stored by setup code. ++ static const int kNumOutputRegisters = kFramePointer - kSystemPointerSize; ++ static const int kRegisterOutput = kNumOutputRegisters - kSystemPointerSize; ++ static const int kInputEnd = kRegisterOutput - kSystemPointerSize; ++ static const int kInputStart = kInputEnd - kSystemPointerSize; ++ static const int kStartIndex = kInputStart - kSystemPointerSize; ++ static const int kInputString = kStartIndex - kSystemPointerSize; ++ // When adding local variables remember to push space for them in ++ // the frame in GetCode. ++ static const int kSuccessfulCaptures = kInputString - kSystemPointerSize; ++ static const int kStringStartMinusOne = ++ kSuccessfulCaptures - kSystemPointerSize; ++ static const int kBacktrackCount = kStringStartMinusOne - kSystemPointerSize; ++ // Stores the initial value of the regexp stack pointer in a ++ // position-independent representation (in case the regexp stack grows and ++ // thus moves). ++ static const int kRegExpStackBasePointer = ++ kBacktrackCount - kSystemPointerSize; ++ ++ // First register address. Following registers are below it on the stack. ++ static const int kRegisterZero = kRegExpStackBasePointer - kSystemPointerSize; ++ ++ // Initial size of code buffer. ++ static const int kRegExpCodeSize = 1024; ++ ++ void PushCallerSavedRegisters(); ++ void PopCallerSavedRegisters(); ++ ++ // Check whether preemption has been requested. ++ void CheckPreemption(); ++ ++ // Check whether we are exceeding the stack limit on the backtrack stack. ++ void CheckStackLimit(); ++ ++ // Generate a call to CheckStackGuardState. ++ void CallCheckStackGuardState(Register scratch); ++ void CallIsCharacterInRangeArray(const ZoneList* ranges); ++ ++ // The ebp-relative location of a regexp register. ++ MemOperand register_location(int register_index); ++ ++ // Register holding the current input position as negative offset from ++ // the end of the string. ++ static constexpr Register current_input_offset() { return s2; } ++ ++ // The register containing the current character after LoadCurrentCharacter. ++ static constexpr Register current_character() { return s3; } ++ ++ // Register holding address of the end of the input string. ++ static constexpr Register end_of_input_address() { return s4; } ++ ++ // Register holding the frame address. Local variables, parameters and ++ // regexp registers are addressed relative to this. ++ static constexpr Register frame_pointer() { return fp; } ++ ++ // The register containing the backtrack stack top. Provides a meaningful ++ // name to the register. ++ static constexpr Register backtrack_stackpointer() { return s5; } ++ ++ // Register holding pointer to the current code object. ++ static constexpr Register code_pointer() { return s1; } ++ ++ // Byte size of chars in the string to match (decided by the Mode argument). ++ inline int char_size() const { return static_cast(mode_); } ++ ++ // Equivalent to a conditional branch to the label, unless the label ++ // is nullptr, in which case it is a conditional Backtrack. ++ void BranchOrBacktrack(Label* to, Condition condition, Register rs, ++ const Operand& rt); ++ ++ // Call and return internally in the generated code in a way that ++ // is GC-safe (i.e., doesn't leave absolute code addresses on the stack) ++ inline void SafeCall(Label* to, Condition cond, Register rs, ++ const Operand& rt); ++ inline void SafeReturn(); ++ inline void SafeCallTarget(Label* name); ++ ++ // Pushes the value of a register on the backtrack stack. Decrements the ++ // stack pointer by a word size and stores the register's value there. ++ inline void Push(Register source); ++ ++ // Pops a value from the backtrack stack. Reads the word at the stack pointer ++ // and increments it by a word size. ++ inline void Pop(Register target); ++ ++ void LoadRegExpStackPointerFromMemory(Register dst); ++ void StoreRegExpStackPointerToMemory(Register src, Register scratch); ++ void PushRegExpBasePointer(Register stack_pointer, Register scratch); ++ void PopRegExpBasePointer(Register stack_pointer_out, Register scratch); ++ ++ Isolate* isolate() const { return masm_->isolate(); } ++ ++ const std::unique_ptr masm_; ++ const NoRootArrayScope no_root_array_scope_; ++ ++ // Which mode to generate code for (Latin1 or UC16). ++ const Mode mode_; ++ ++ // One greater than maximal register index actually used. ++ int num_registers_; ++ ++ // Number of registers to output at the end (the saved registers ++ // are always 0..num_saved_registers_-1). ++ const int num_saved_registers_; ++ ++ // Labels used internally. ++ Label entry_label_; ++ Label start_label_; ++ Label success_label_; ++ Label backtrack_label_; ++ Label exit_label_; ++ Label check_preempt_label_; ++ Label stack_overflow_label_; ++ Label internal_failure_label_; ++ Label fallback_label_; ++}; ++ ++} // namespace internal ++} // namespace v8 ++ ++#endif // V8_REGEXP_SW64_REGEXP_MACRO_ASSEMBLER_SW64_H_ +diff --git a/deps/v8/src/runtime/runtime-atomics.cc b/deps/v8/src/runtime/runtime-atomics.cc +index e372c2a1..6d068450 100644 +--- a/deps/v8/src/runtime/runtime-atomics.cc ++++ b/deps/v8/src/runtime/runtime-atomics.cc +@@ -21,7 +21,7 @@ namespace internal { + // Other platforms have CSA support, see builtins-sharedarraybuffer-gen.h. + #if V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_PPC64 || \ + V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_S390 || V8_TARGET_ARCH_S390X || \ +- V8_TARGET_ARCH_RISCV64 || V8_TARGET_ARCH_LOONG64 ++ V8_TARGET_ARCH_RISCV64 || V8_TARGET_ARCH_LOONG64 || V8_TARGET_ARCH_SW64 + + namespace { + +@@ -607,7 +607,7 @@ RUNTIME_FUNCTION(Runtime_AtomicsXor) { UNREACHABLE(); } + + #endif // V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_PPC64 + // || V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_S390 || V8_TARGET_ARCH_S390X +- // || V8_TARGET_ARCH_RISCV64 || V8_TARGET_ARCH_LOONG64 ++ // || V8_TARGET_ARCH_RISCV64 || V8_TARGET_ARCH_LOONG64 || V8_TARGET_ARCH_SW64 + + RUNTIME_FUNCTION(Runtime_AtomicsLoadSharedStructField) { + HandleScope scope(isolate); +diff --git a/deps/v8/src/runtime/runtime-utils.h b/deps/v8/src/runtime/runtime-utils.h +index 30437b9b..cbad4a48 100644 +--- a/deps/v8/src/runtime/runtime-utils.h ++++ b/deps/v8/src/runtime/runtime-utils.h +@@ -22,17 +22,30 @@ namespace internal { + // In Win64 calling convention, a struct of two pointers is returned in memory, + // allocated by the caller, and passed as a pointer in a hidden first parameter. + #ifdef V8_HOST_ARCH_64_BIT ++#if defined(SW64) && defined(__sw_64__) ++typedef Address ObjectPair; ++#else + struct ObjectPair { + Address x; + Address y; + }; ++#endif + ++#if defined(SW64) && defined(__sw_64__) ++static inline ObjectPair MakePair(Object x, Object y) { ++ // mov a1 to a5 ++ __asm__ __volatile__ ("bis %0,$31,$21\n\t"::"r"(y.ptr())); ++ return x.ptr(); ++} ++#else + static inline ObjectPair MakePair(Object x, Object y) { + ObjectPair result = {x.ptr(), y.ptr()}; + // Pointers x and y returned in rax and rdx, in AMD-x64-abi. + // In Win64 they are assigned to a hidden first argument. + return result; + } ++#endif ++ + #else + using ObjectPair = uint64_t; + static inline ObjectPair MakePair(Object x, Object y) { +diff --git a/deps/v8/src/snapshot/deserializer.h b/deps/v8/src/snapshot/deserializer.h +index 24e79ed0..916cde8f 100644 +--- a/deps/v8/src/snapshot/deserializer.h ++++ b/deps/v8/src/snapshot/deserializer.h +@@ -35,7 +35,7 @@ class Object; + #if defined(V8_TARGET_ARCH_MIPS) || defined(V8_TARGET_ARCH_MIPS64) || \ + defined(V8_TARGET_ARCH_PPC) || defined(V8_TARGET_ARCH_S390) || \ + defined(V8_TARGET_ARCH_PPC64) || defined(V8_TARGET_ARCH_RISCV64) || \ +- V8_EMBEDDED_CONSTANT_POOL ++ defined(V8_TARGET_ARCH_SW64) || V8_EMBEDDED_CONSTANT_POOL + #define V8_CODE_EMBEDS_OBJECT_POINTER 1 + #else + #define V8_CODE_EMBEDS_OBJECT_POINTER 0 +diff --git a/deps/v8/src/snapshot/embedded/embedded-data.cc b/deps/v8/src/snapshot/embedded/embedded-data.cc +index 89ce411d..23c69e27 100644 +--- a/deps/v8/src/snapshot/embedded/embedded-data.cc ++++ b/deps/v8/src/snapshot/embedded/embedded-data.cc +@@ -230,7 +230,7 @@ void FinalizeEmbeddedCodeTargets(Isolate* isolate, EmbeddedData* blob) { + #if defined(V8_TARGET_ARCH_X64) || defined(V8_TARGET_ARCH_ARM64) || \ + defined(V8_TARGET_ARCH_ARM) || defined(V8_TARGET_ARCH_MIPS) || \ + defined(V8_TARGET_ARCH_IA32) || defined(V8_TARGET_ARCH_S390) || \ +- defined(V8_TARGET_ARCH_RISCV64) || defined(V8_TARGET_ARCH_LOONG64) ++ defined(V8_TARGET_ARCH_RISCV64) || defined(V8_TARGET_ARCH_LOONG64) || defined(V8_TARGET_ARCH_SW64) + // On these platforms we emit relative builtin-to-builtin + // jumps for isolate independent builtins in the snapshot. This fixes up the + // relative jumps to the right offsets in the snapshot. +diff --git a/deps/v8/src/snapshot/embedded/platform-embedded-file-writer-generic.cc b/deps/v8/src/snapshot/embedded/platform-embedded-file-writer-generic.cc +index 9309dbdd..d4bc0930 100644 +--- a/deps/v8/src/snapshot/embedded/platform-embedded-file-writer-generic.cc ++++ b/deps/v8/src/snapshot/embedded/platform-embedded-file-writer-generic.cc +@@ -164,7 +164,7 @@ int PlatformEmbeddedFileWriterGeneric::IndentedDataDirective( + DataDirective PlatformEmbeddedFileWriterGeneric::ByteChunkDataDirective() + const { + #if defined(V8_TARGET_ARCH_MIPS) || defined(V8_TARGET_ARCH_MIPS64) || \ +- defined(V8_TARGET_ARCH_LOONG64) ++ defined(V8_TARGET_ARCH_LOONG64) || defined(V8_TARGET_ARCH_SW64) + // MIPS and LOONG64 uses a fixed 4 byte instruction set, using .long + // to prevent any unnecessary padding. + return kLong; +diff --git a/deps/v8/src/wasm/baseline/liftoff-assembler-defs.h b/deps/v8/src/wasm/baseline/liftoff-assembler-defs.h +index e4458b60..6329c1f4 100644 +--- a/deps/v8/src/wasm/baseline/liftoff-assembler-defs.h ++++ b/deps/v8/src/wasm/baseline/liftoff-assembler-defs.h +@@ -57,6 +57,20 @@ constexpr DoubleRegList kLiftoffAssemblerFpCacheRegs = { + 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}; + ++#elif V8_TARGET_ARCH_SW64 ++ ++// t7, t8 used as two scratch regs instead of s3, s4, so delete t7, t8; ++// t5, t6 same to at, t4 used as v1. ++constexpr RegList kLiftoffAssemblerGpCacheRegs = {a0, a1, a2, a3, a4, a5, ++ t0, t1, t2, t3, t4, t9, t10, s5, v0}; ++ ++// f30 used as kDoubleCompareReg; ++// f27, f28, f29 used as kScratchDoubleReg*; ++constexpr DoubleRegList kLiftoffAssemblerFpCacheRegs = {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}; ++ + #elif V8_TARGET_ARCH_ARM + + // r10: root, r11: fp, r12: ip, r13: sp, r14: lr, r15: pc. +diff --git a/deps/v8/src/wasm/baseline/liftoff-assembler.h b/deps/v8/src/wasm/baseline/liftoff-assembler.h +index 22c7f73a..d84a09ac 100644 +--- a/deps/v8/src/wasm/baseline/liftoff-assembler.h ++++ b/deps/v8/src/wasm/baseline/liftoff-assembler.h +@@ -1752,6 +1752,8 @@ bool CheckCompatibleStackSlotTypes(ValueKind a, ValueKind b); + #include "src/wasm/baseline/mips64/liftoff-assembler-mips64.h" + #elif V8_TARGET_ARCH_LOONG64 + #include "src/wasm/baseline/loong64/liftoff-assembler-loong64.h" ++#elif V8_TARGET_ARCH_SW64 ++#include "src/wasm/baseline/sw64/liftoff-assembler-sw64.h" + #elif V8_TARGET_ARCH_S390 + #include "src/wasm/baseline/s390/liftoff-assembler-s390.h" + #elif V8_TARGET_ARCH_RISCV64 +diff --git a/deps/v8/src/wasm/baseline/liftoff-compiler.cc b/deps/v8/src/wasm/baseline/liftoff-compiler.cc +index 508a91ec..88f5d637 100644 +--- a/deps/v8/src/wasm/baseline/liftoff-compiler.cc ++++ b/deps/v8/src/wasm/baseline/liftoff-compiler.cc +@@ -312,7 +312,7 @@ void CheckBailoutAllowed(LiftoffBailoutReason reason, const char* detail, + + // Some externally maintained architectures don't fully implement Liftoff yet. + #if V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_S390X || \ +- V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_PPC64 || V8_TARGET_ARCH_LOONG64 ++ V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_PPC64 || V8_TARGET_ARCH_LOONG64 || V8_TARGET_ARCH_SW64 + return; + #endif + +diff --git a/deps/v8/src/wasm/baseline/sw64/liftoff-assembler-sw64.h b/deps/v8/src/wasm/baseline/sw64/liftoff-assembler-sw64.h +new file mode 100644 +index 00000000..ea3fc134 +--- /dev/null ++++ b/deps/v8/src/wasm/baseline/sw64/liftoff-assembler-sw64.h +@@ -0,0 +1,3590 @@ ++// Copyright 2017 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef V8_WASM_BASELINE_SW64_LIFTOFF_ASSEMBLER_SW64_H_ ++#define V8_WASM_BASELINE_SW64_LIFTOFF_ASSEMBLER_SW64_H_ ++ ++#include "src/base/platform/wrappers.h" ++#include "src/codegen/machine-type.h" ++#include "src/heap/memory-chunk.h" ++#include "src/wasm/baseline/liftoff-assembler.h" ++#include "src/wasm/wasm-objects.h" ++ ++namespace v8 { ++namespace internal { ++namespace wasm { ++ ++namespace liftoff { ++ ++inline constexpr Condition ToCondition(LiftoffCondition liftoff_cond) { ++ switch (liftoff_cond) { ++ case kEqual: ++ return eq; ++ case kUnequal: ++ return ne; ++ case kSignedLessThan: ++ return lt; ++ case kSignedLessEqual: ++ return le; ++ case kSignedGreaterThan: ++ return gt; ++ case kSignedGreaterEqual: ++ return ge; ++ case kUnsignedLessThan: ++ return ult; ++ case kUnsignedLessEqual: ++ return ule; ++ case kUnsignedGreaterThan: ++ return ugt; ++ case kUnsignedGreaterEqual: ++ return uge; ++ } ++} ++ ++// Liftoff Frames. ++// ++// slot Frame ++// +--------------------+--------------------------- ++// n+4 | optional padding slot to keep the stack 16 byte aligned. ++// n+3 | parameter n | ++// ... | ... | ++// 4 | parameter 1 | or parameter 2 ++// 3 | parameter 0 | or parameter 1 ++// 2 | (result address) | or parameter 0 ++// -----+--------------------+--------------------------- ++// 1 | return addr (ra) | ++// 0 | previous frame (fp)| ++// -----+--------------------+ <-- frame ptr (fp) ++// -1 | StackFrame::WASM | ++// -2 | instance | ++// -3 | feedback vector| ++// -4 | tiering budget | ++// -----+--------------------+--------------------------- ++// -5 | slot 0 | ^ ++// -6 | slot 1 | | ++// | | Frame slots ++// | | | ++// | | v ++// | optional padding slot to keep the stack 16 byte aligned. ++// -----+--------------------+ <-- stack ptr (sp) ++// ++ ++constexpr int kInstanceOffset = 2 * kSystemPointerSize; ++constexpr int kFeedbackVectorOffset = 3 * kSystemPointerSize; ++constexpr int kTierupBudgetOffset = 4 * kSystemPointerSize; ++ ++inline MemOperand GetStackSlot(int offset) { return MemOperand(fp, -offset); } ++ ++inline MemOperand GetInstanceOperand() { return GetStackSlot(kInstanceOffset); } ++ ++template ++inline MemOperand GetMemOp(LiftoffAssembler* assm, Register addr, ++ Register offset, T offset_imm) { ++ if (is_int32(offset_imm)) { ++ int32_t offset_imm32 = static_cast(offset_imm); ++ if (offset == no_reg) return MemOperand(addr, offset_imm32); ++ assm->addl(addr, offset, kScratchReg); ++ return MemOperand(kScratchReg, offset_imm32); ++ } ++ // Offset immediate does not fit in 31 bits. ++ assm->li(kScratchReg, offset_imm); ++ assm->addl(kScratchReg, addr, kScratchReg); ++ if (offset != no_reg) { ++ assm->addl(kScratchReg, offset, kScratchReg); ++ } ++ return MemOperand(kScratchReg, 0); ++} ++ ++inline void Load(LiftoffAssembler* assm, LiftoffRegister dst, MemOperand src, ++ ValueKind kind) { ++ switch (kind) { ++ case kI32: ++ assm->Ldw(dst.gp(), src); ++ break; ++ case kI64: ++ case kRef: ++ case kOptRef: ++ case kRtt: ++ assm->Ldl(dst.gp(), src); ++ break; ++ case kF32: ++ assm->Flds(dst.fp(), src); ++ break; ++ case kF64: ++ assm->Fldd(dst.fp(), src); ++ break; ++ // case ValueType::kS128: ++ // assm->ld_b(dst.fp().toW(), src); ++ // break; ++ default: ++ UNREACHABLE(); ++ } ++} ++ ++inline void Store(LiftoffAssembler* assm, Register base, int32_t offset, ++ LiftoffRegister src, ValueKind kind) { ++ MemOperand dst(base, offset); ++ switch (kind) { ++ case kI32: ++ assm->Stw_u(src.gp(), dst); ++ break; ++ case kI64: ++ case kOptRef: ++ case kRef: ++ case kRtt: ++ assm->Stl_u(src.gp(), dst); ++ break; ++ case kF32: ++ assm->Fsts_u(src.fp(), dst, t11); ++ break; ++ case kF64: ++ assm->Fstd_u(src.fp(), dst, t11); ++ break; ++ // case ValueType::kS128: ++ // assm->st_b(src.fp().toW(), dst); ++ // break; ++ default: ++ UNREACHABLE(); ++ } ++} ++ ++inline void push(LiftoffAssembler* assm, LiftoffRegister reg, ValueKind kind) { ++ switch (kind) { ++ case kI32: ++ assm->subl(sp, kSystemPointerSize, sp); ++ assm->Stw(reg.gp(), MemOperand(sp, 0)); ++ break; ++ case kI64: ++ case kOptRef: ++ case kRef: ++ case kRtt: ++ assm->Push(reg.gp()); ++ break; ++ case kF32: ++ assm->subl(sp, kSystemPointerSize, sp); ++ assm->Fsts(reg.fp(), MemOperand(sp, 0)); ++ break; ++ case kF64: ++ assm->subl(sp, kSystemPointerSize, sp); ++ assm->Fstd(reg.fp(), MemOperand(sp, 0)); ++ break; ++ // case ValueType::kS128: ++ // assm->daddiu(sp, sp, -kSystemPointerSize * 2); ++ // assm->st_b(reg.fp().toW(), MemOperand(sp, 0)); ++ // break; ++ default: ++ UNREACHABLE(); ++ } ++} ++ ++} // namespace liftoff ++ ++int LiftoffAssembler::PrepareStackFrame() { ++ int offset = pc_offset(); ++ // When the frame size is bigger than 4KB, we need five instructions for ++ // stack checking, so we reserve space for this case. ++ addl(sp, 0, sp); ++ nop(); ++ nop(); ++ nop(); ++ nop(); ++ return offset; ++} ++ ++void LiftoffAssembler::PrepareTailCall(int num_callee_stack_params, ++ int stack_param_delta) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ ++ // Push the return address and frame pointer to complete the stack frame. ++ Ldl(scratch, MemOperand(fp, 8)); ++ Push(scratch); ++ Ldl(scratch, MemOperand(fp, 0)); ++ Push(scratch); ++ ++ // Shift the whole frame upwards. ++ int slot_count = num_callee_stack_params + 2; ++ for (int i = slot_count - 1; i >= 0; --i) { ++ Ldl(scratch, MemOperand(sp, i * 8)); ++ Stl(scratch, MemOperand(fp, (i - stack_param_delta) * 8)); ++ } ++ ++ // Set the new stack and frame pointer. ++ subl(fp, stack_param_delta * 8, sp); ++ Pop(ra, fp); ++} ++ ++void LiftoffAssembler::AlignFrameSize() {} ++ ++void LiftoffAssembler::PatchPrepareStackFrame( ++ int offset, SafepointTableBuilder* safepoint_table_builder) { ++ // The frame_size includes the frame marker and the instance slot. Both are ++ // pushed as part of frame construction, so we don't need to allocate memory ++ // for them anymore. ++ int frame_size = GetTotalFrameSize() - 2 * kSystemPointerSize; ++ ++ // We can't run out of space, just pass anything big enough to not cause the ++ // assembler to try to grow the buffer. ++ constexpr int kAvailableSpace = 256; ++ TurboAssembler patching_assembler( ++ nullptr, AssemblerOptions{}, CodeObjectRequired::kNo, ++ ExternalAssemblerBuffer(buffer_start_ + offset, kAvailableSpace)); ++ ++ if (V8_LIKELY(frame_size < 4 * KB)) { ++ // This is the standard case for small frames: just subtract from SP and be ++ // done with it. ++ patching_assembler.Addl(sp, sp, Operand(-frame_size)); ++ return; ++ } ++ ++ // The frame size is bigger than 4KB, so we might overflow the available stack ++ // space if we first allocate the frame and then do the stack check (we will ++ // need some remaining stack space for throwing the exception). That's why we ++ // check the available stack space before we allocate the frame. To do this we ++ // replace the {__ Addl(sp, sp, -frame_size)} with a jump to OOL code that ++ // does this "extended stack check". ++ // ++ // The OOL code can simply be generated here with the normal assembler, ++ // because all other code generation, including OOL code, has already finished ++ // when {PatchPrepareStackFrame} is called. The function prologue then jumps ++ // to the current {pc_offset()} to execute the OOL code for allocating the ++ // large frame. ++ // Emit the unconditional branch in the function prologue (from {offset} to ++ // {pc_offset()}). ++ ++ int imm32 = pc_offset() - offset - 3 * kInstrSize; ++ patching_assembler.BranchLong(imm32); ++ ++ // If the frame is bigger than the stack, we throw the stack overflow ++ // exception unconditionally. Thereby we can avoid the integer overflow ++ // check in the condition code. ++ RecordComment("OOL: stack check for large frame"); ++ Label continuation; ++ if (frame_size < FLAG_stack_size * 1024) { ++ Register stack_limit = kScratchReg; ++ Ldl(stack_limit, ++ FieldMemOperand(kWasmInstanceRegister, ++ WasmInstanceObject::kRealStackLimitAddressOffset)); ++ Ldl(stack_limit, MemOperand(stack_limit)); ++ Addl(stack_limit, stack_limit, Operand(frame_size)); ++ Branch(&continuation, uge, sp, Operand(stack_limit)); ++ } ++ ++ Call(wasm::WasmCode::kWasmStackOverflow, RelocInfo::WASM_STUB_CALL); ++ // The call will not return; just define an empty safepoint. ++ safepoint_table_builder->DefineSafepoint(this); ++ if (FLAG_debug_code) halt(); ++ ++ bind(&continuation); ++ ++ // Now allocate the stack space. Note that this might do more than just ++ // decrementing the SP; ++ Addl(sp, sp, Operand(-frame_size)); ++ ++ // Jump back to the start of the function, from {pc_offset()} to ++ // right after the reserved space for the {__ Addl(sp, sp, -framesize)} ++ // (which is a Branch now). ++ int func_start_offset = offset + 7 * kInstrSize; ++ imm32 = func_start_offset - pc_offset() - 3 * kInstrSize; ++ BranchLong(imm32); ++} ++ ++void LiftoffAssembler::FinishCode() {} ++ ++void LiftoffAssembler::AbortCompilation() {} ++ ++// static ++constexpr int LiftoffAssembler::StaticStackFrameSize() { ++ return liftoff::kTierupBudgetOffset; ++} ++ ++int LiftoffAssembler::SlotSizeForType(ValueKind kind) { ++ switch (kind) { ++ case kS128: ++ return value_kind_size(kind); ++ default: ++ return kStackSlotSize; ++ } ++} ++ ++bool LiftoffAssembler::NeedsAlignment(ValueKind kind) { ++ return kind == kS128 || is_reference(kind); ++} ++ ++void LiftoffAssembler::LoadConstant(LiftoffRegister reg, WasmValue value, ++ RelocInfo::Mode rmode) { ++ switch (value.type().kind()) { ++ case kI32: ++ TurboAssembler::li(reg.gp(), Operand(value.to_i32(), rmode)); ++ break; ++ case kI64: ++ TurboAssembler::li(reg.gp(), Operand(value.to_i64(), rmode)); ++ break; ++ case kF32: ++ TurboAssembler::Move(reg.fp(), value.to_f32_boxed().get_bits()); ++ break; ++ case kF64: ++ TurboAssembler::Move(reg.fp(), value.to_f64_boxed().get_bits()); ++ break; ++ default: ++ UNREACHABLE(); ++ } ++} ++ ++void LiftoffAssembler::LoadInstanceFromFrame(Register dst) { ++ Ldl(dst, liftoff::GetInstanceOperand()); ++} ++ ++void LiftoffAssembler::LoadFromInstance(Register dst, Register instance, ++ int offset, int size) { ++ DCHECK_LE(0, offset); ++ switch (size) { ++ case 1: ++ Ldb(dst, MemOperand(instance, offset)); ++ break; ++ case 4: ++ Ldw(dst, MemOperand(instance, offset)); ++ break; ++ case 8: ++ Ldl(dst, MemOperand(instance, offset)); ++ break; ++ default: ++ UNIMPLEMENTED(); ++ } ++} ++ ++void LiftoffAssembler::LoadTaggedPointerFromInstance(Register dst, ++ Register instance, ++ int32_t offset) { ++ STATIC_ASSERT(kTaggedSize == kSystemPointerSize); ++ Ldl(dst, MemOperand(instance, offset)); ++} ++ ++void LiftoffAssembler::SpillInstance(Register instance) { ++ Stl(instance, liftoff::GetInstanceOperand()); ++} ++ ++void LiftoffAssembler::ResetOSRTarget() {} ++ ++void LiftoffAssembler::LoadTaggedPointer(Register dst, Register src_addr, ++ Register offset_reg, ++ int32_t offset_imm, ++ LiftoffRegList pinned) { ++ STATIC_ASSERT(kTaggedSize == kInt64Size); ++ MemOperand src_op = liftoff::GetMemOp(this, src_addr, offset_reg, offset_imm); ++ Ldl(dst, src_op); ++} ++ ++void LiftoffAssembler::LoadFullPointer(Register dst, Register src_addr, ++ int32_t offset_imm) { ++ MemOperand src_op = liftoff::GetMemOp(this, src_addr, no_reg, offset_imm); ++ Ldl(dst, src_op); ++} ++ ++void LiftoffAssembler::StoreTaggedPointer(Register dst_addr, ++ Register offset_reg, ++ int32_t offset_imm, ++ LiftoffRegister src, ++ LiftoffRegList pinned, ++ SkipWriteBarrier skip_write_barrier) { ++ STATIC_ASSERT(kTaggedSize == kInt64Size); ++ Register scratch = pinned.set(GetUnusedRegister(kGpReg, pinned)).gp(); ++ MemOperand dst_op = liftoff::GetMemOp(this, dst_addr, offset_reg, offset_imm); ++ Stl(src.gp(), dst_op); ++ ++ if (skip_write_barrier || FLAG_disable_write_barriers) return; ++ ++ Label write_barrier; ++ Label exit; ++ CheckPageFlag(dst_addr, scratch, ++ MemoryChunk::kPointersFromHereAreInterestingMask, ne, ++ &write_barrier); ++ Branch(&exit); ++ bind(&write_barrier); ++ JumpIfSmi(src.gp(), &exit); ++ CheckPageFlag(src.gp(), scratch, ++ MemoryChunk::kPointersToHereAreInterestingMask, eq, ++ &exit); ++ Addl(scratch, dst_op.rm(), dst_op.offset()); ++ CallRecordWriteStubSaveRegisters( ++ dst_addr, scratch, RememberedSetAction::kEmit, SaveFPRegsMode::kSave, ++ StubCallMode::kCallWasmRuntimeStub); ++ bind(&exit); ++} ++ ++void LiftoffAssembler::Load(LiftoffRegister dst, Register src_addr, ++ Register offset_reg, uintptr_t offset_imm, ++ LoadType type, LiftoffRegList pinned, ++ uint32_t* protected_load_pc, bool is_load_mem, ++ bool i64_offset) { ++ MemOperand src_op = liftoff::GetMemOp(this, src_addr, offset_reg, offset_imm); ++ ++ if (protected_load_pc) *protected_load_pc = pc_offset(); ++ switch (type.value()) { ++ case LoadType::kI32Load8U: ++ case LoadType::kI64Load8U: ++ Ldbu(dst.gp(), src_op); ++ break; ++ case LoadType::kI32Load8S: ++ case LoadType::kI64Load8S: ++ Ldb(dst.gp(), src_op); ++ break; ++ case LoadType::kI32Load16U: ++ case LoadType::kI64Load16U: ++ TurboAssembler::Ldhu_u(dst.gp(), src_op); ++ break; ++ case LoadType::kI32Load16S: ++ case LoadType::kI64Load16S: ++ TurboAssembler::Ldh_u(dst.gp(), src_op); ++ break; ++ case LoadType::kI64Load32U: ++ TurboAssembler::Ldwu_u(dst.gp(), src_op); ++ break; ++ case LoadType::kI32Load: ++ case LoadType::kI64Load32S: ++ TurboAssembler::Ldw_u(dst.gp(), src_op); ++ break; ++ case LoadType::kI64Load: ++ TurboAssembler::Ldl_u(dst.gp(), src_op); ++ break; ++ case LoadType::kF32Load: ++ TurboAssembler::Flds_u(dst.fp(), src_op, t11); ++ break; ++ case LoadType::kF64Load: ++ TurboAssembler::Fldd_u(dst.fp(), src_op, t11); ++ break; ++ default: ++ UNREACHABLE(); ++ } ++} ++ ++void LiftoffAssembler::Store(Register dst_addr, Register offset_reg, ++ uintptr_t offset_imm, LiftoffRegister src, ++ StoreType type, LiftoffRegList pinned, ++ uint32_t* protected_store_pc, bool is_storere_mem) { ++ MemOperand dst_op = liftoff::GetMemOp(this, dst_addr, offset_reg, offset_imm); ++ ++ if (protected_store_pc) *protected_store_pc = pc_offset(); ++ switch (type.value()) { ++ case StoreType::kI32Store8: ++ case StoreType::kI64Store8: ++ Stb(src.gp(), dst_op); ++ break; ++ case StoreType::kI32Store16: ++ case StoreType::kI64Store16: ++ TurboAssembler::Sth_u(src.gp(), dst_op, t11); ++ break; ++ case StoreType::kI32Store: ++ case StoreType::kI64Store32: ++ TurboAssembler::Stw_u(src.gp(), dst_op); ++ break; ++ case StoreType::kI64Store: ++ TurboAssembler::Stl_u(src.gp(), dst_op); ++ break; ++ case StoreType::kF32Store: ++ TurboAssembler::Fsts_u(src.fp(), dst_op, t11); ++ break; ++ case StoreType::kF64Store: ++ TurboAssembler::Fstd_u(src.fp(), dst_op, t11); ++ break; ++ default: ++ UNREACHABLE(); ++ } ++} ++ ++// SKTODO ++void LiftoffAssembler::AtomicLoad(LiftoffRegister dst, Register src_addr, ++ Register offset_reg, uintptr_t offset_imm, ++ LoadType type, LiftoffRegList pinned ++) { ++ UseScratchRegisterScope temps(this); ++ MemOperand src_op = liftoff::GetMemOp(this, src_addr, offset_reg, offset_imm); ++ switch (type.value()) { ++ case LoadType::kI32Load8U: ++ case LoadType::kI64Load8U: { ++ Ldbu(dst.gp(), src_op); ++ // memb(); ++ return; ++ } ++ case LoadType::kI32Load16U: ++ case LoadType::kI64Load16U: { ++ Ldhu(dst.gp(), src_op); ++ // memb(); ++ return; ++ } ++ case LoadType::kI32Load: { ++ Ldw(dst.gp(), src_op); ++ // memb(); ++ return; ++ } ++ case LoadType::kI64Load32U: { ++ Ldwu(dst.gp(), src_op); ++ // memb(); ++ return; ++ } ++ case LoadType::kI64Load: { ++ Ldl(dst.gp(), src_op); ++ // memb(); ++ return; ++ } ++ default: ++ UNREACHABLE(); ++ } ++} ++ ++void LiftoffAssembler::AtomicStore(Register dst_addr, Register offset_reg, ++ uintptr_t offset_imm, LiftoffRegister src, ++ StoreType type, LiftoffRegList pinned) { ++ UseScratchRegisterScope temps(this); ++ MemOperand dst_op = liftoff::GetMemOp(this, dst_addr, offset_reg, offset_imm); ++ switch (type.value()) { ++ case StoreType::kI64Store8: ++ case StoreType::kI32Store8: { ++ // memb(); ++ Stb(src.gp(), dst_op); ++ return; ++ } ++ case StoreType::kI64Store16: ++ case StoreType::kI32Store16: { ++ // memb(); ++ Sth(src.gp(), dst_op); ++ return; ++ } ++ case StoreType::kI64Store32: ++ case StoreType::kI32Store: { ++ // memb(); ++ Stw(src.gp(), dst_op); ++ return; ++ } ++ case StoreType::kI64Store: { ++ // memb(); ++ Stl(src.gp(), dst_op); ++ return; ++ } ++ default: ++ UNREACHABLE(); ++ } ++} ++ ++#define ASSEMBLE_ATOMIC_BINOP(load_linked, store_conditional, bin_instr) \ ++ do { \ ++ Label binop; \ ++ bind(&binop); \ ++ load_linked(result.gp(), 0, temp0); \ ++ ldi(gp, 1, zero_reg); \ ++ wr_f(gp); \ ++ bin_instr(temp1, result.gp(), Operand(value.gp())); \ ++ Align(8); \ ++ store_conditional(temp1, 0, temp0); \ ++ rd_f(temp1); \ ++ BranchShort(&binop, eq, temp1, Operand(zero_reg)); \ ++ /*__ memb();*/ \ ++ } while (0) ++ ++#define ASSEMBLE_ATOMIC_BINOP_EXT(load_linked, store_conditional, size, \ ++ bin_instr, aligned) \ ++ do { \ ++ Label binop; \ ++ and_(temp0, aligned, temp3); \ ++ Subl(temp0, temp0, Operand(temp3)); \ ++ slll(temp3, 3, temp3); \ ++ bind(&binop); \ ++ load_linked(temp1, 0, temp0); \ ++ ldi(gp, 1, zero_reg); \ ++ wr_f(gp); \ ++ ExtractBits(result.gp(), temp1, temp3, size, false); \ ++ bin_instr(temp2, result.gp(), value.gp()); \ ++ InsertBits(temp1, temp2, temp3, size); \ ++ Align(8); \ ++ store_conditional(temp1, 0, temp0); \ ++ rd_f(temp1); \ ++ BranchShort(&binop, eq, temp1, Operand(zero_reg)); \ ++ } while (0) ++ ++#define ATOMIC_BINOP_CASE(name, inst32, inst64) \ ++ void LiftoffAssembler::Atomic##name( \ ++ Register dst_addr, Register offset_reg, uintptr_t offset_imm, \ ++ LiftoffRegister value, LiftoffRegister result, StoreType type) { \ ++ LiftoffRegList pinned = {dst_addr, offset_reg, value, result}; \ ++ Register temp0 = pinned.set(GetUnusedRegister(kGpReg, pinned)).gp(); \ ++ Register temp1 = pinned.set(GetUnusedRegister(kGpReg, pinned)).gp(); \ ++ Register temp2 = pinned.set(GetUnusedRegister(kGpReg, pinned)).gp(); \ ++ Register temp3 = pinned.set(GetUnusedRegister(kGpReg, pinned)).gp(); \ ++ MemOperand dst_op = \ ++ liftoff::GetMemOp(this, dst_addr, offset_reg, offset_imm); \ ++ Addl(temp0, dst_op.rm(), dst_op.offset()); \ ++ switch (type.value()) { \ ++ case StoreType::kI64Store8: \ ++ ASSEMBLE_ATOMIC_BINOP_EXT(lldl, lstl, 8, inst64, 7); \ ++ break; \ ++ case StoreType::kI32Store8: \ ++ ASSEMBLE_ATOMIC_BINOP_EXT(lldw, lstw, 8, inst32, 3); \ ++ break; \ ++ case StoreType::kI64Store16: \ ++ ASSEMBLE_ATOMIC_BINOP_EXT(lldl, lstl, 16, inst64, 7); \ ++ break; \ ++ case StoreType::kI32Store16: \ ++ ASSEMBLE_ATOMIC_BINOP_EXT(lldw, lstw, 16, inst32, 3); \ ++ break; \ ++ case StoreType::kI64Store32: \ ++ ASSEMBLE_ATOMIC_BINOP_EXT(lldl, lstl, 32, inst64, 7); \ ++ break; \ ++ case StoreType::kI32Store: \ ++ ASSEMBLE_ATOMIC_BINOP(lldw, lstw, inst32); \ ++ break; \ ++ case StoreType::kI64Store: \ ++ ASSEMBLE_ATOMIC_BINOP(lldl, lstl, inst64); \ ++ break; \ ++ default: \ ++ UNREACHABLE(); \ ++ } \ ++ } ++ ++ATOMIC_BINOP_CASE(Add, Addw, Addl) ++ATOMIC_BINOP_CASE(Sub, Subw, Subl) ++ATOMIC_BINOP_CASE(And, And, And) ++ATOMIC_BINOP_CASE(Or, Or, Or) ++ATOMIC_BINOP_CASE(Xor, Xor, Xor) ++#undef ASSEMBLE_ATOMIC_BINOP ++#undef ASSEMBLE_ATOMIC_BINOP_EXT ++#undef ATOMIC_BINOP_CASE ++ ++#define ASSEMBLE_ATOMIC_EXCHANGE_INTEGER(load_linked, store_conditional) \ ++ do { \ ++ Label exchange; \ ++ bind(&exchange); \ ++ load_linked(result.gp(), 0, temp0); \ ++ ldi(gp, 1, zero_reg); \ ++ wr_f(gp); \ ++ mov(temp1, value.gp()); \ ++ Align(8); \ ++ store_conditional(temp1, 0, temp0); \ ++ rd_f(temp1); \ ++ BranchShort(&exchange, eq, temp1, Operand(zero_reg)); \ ++ } while (0) ++ ++#define ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(load_linked, store_conditional, \ ++ size, aligned) \ ++ do { \ ++ Label exchange; \ ++ and_(temp0, aligned, temp1); \ ++ Subl(temp0, temp0, Operand(temp1)); \ ++ slll(temp1, 3, temp1); \ ++ bind(&exchange); \ ++ load_linked(temp2, 0, temp0); \ ++ ldi(gp, 1, zero_reg); \ ++ wr_f(gp); \ ++ ExtractBits(result.gp(), temp2, temp1, size, false); \ ++ InsertBits(temp2, value.gp(), temp1, size); \ ++ Align(8); \ ++ store_conditional(temp2, 0, temp0); \ ++ rd_f(temp2); \ ++ BranchShort(&exchange, eq, temp2, Operand(zero_reg)); \ ++ } while (0) ++ ++void LiftoffAssembler::AtomicExchange(Register dst_addr, Register offset_reg, ++ uintptr_t offset_imm, ++ LiftoffRegister value, ++ LiftoffRegister result, StoreType type) { ++ LiftoffRegList pinned = {dst_addr, offset_reg, value, result}; ++ Register temp0 = pinned.set(GetUnusedRegister(kGpReg, pinned)).gp(); ++ Register temp1 = pinned.set(GetUnusedRegister(kGpReg, pinned)).gp(); ++ Register temp2 = pinned.set(GetUnusedRegister(kGpReg, pinned)).gp(); ++ MemOperand dst_op = liftoff::GetMemOp(this, dst_addr, offset_reg, offset_imm); ++ Addl(temp0, dst_op.rm(), dst_op.offset()); ++ switch (type.value()) { ++ case StoreType::kI64Store8: ++ ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(lldl, lstl, 8, 7); ++ break; ++ case StoreType::kI32Store8: ++ ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(lldw, lstw, 8, 3); ++ break; ++ case StoreType::kI64Store16: ++ ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(lldl, lstl, 16, 7); ++ break; ++ case StoreType::kI32Store16: ++ ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(lldw, lstw, 16, 3); ++ break; ++ case StoreType::kI64Store32: ++ ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(lldl, lstl, 32, 7); ++ break; ++ case StoreType::kI32Store: ++ ASSEMBLE_ATOMIC_EXCHANGE_INTEGER(lldw, lstw); ++ break; ++ case StoreType::kI64Store: ++ ASSEMBLE_ATOMIC_EXCHANGE_INTEGER(lldl, lstl); ++ break; ++ default: ++ UNREACHABLE(); ++ } ++} ++#undef ASSEMBLE_ATOMIC_EXCHANGE_INTEGER ++#undef ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT ++ ++#define ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER(load_linked, \ ++ store_conditional) \ ++ do { \ ++ Label compareExchange; \ ++ Label exit; \ ++ bind(&compareExchange); \ ++ load_linked(result.gp(), 0, temp0); \ ++ cmpeq(result.gp(), expected.gp(), temp1); \ ++ wr_f(temp1); \ ++ mov(temp2, new_value.gp()); \ ++ Align(8); \ ++ store_conditional(temp2, 0, temp0); \ ++ rd_f(temp2); \ ++ beq(temp1, &exit); \ ++ beq(temp2, &compareExchange); \ ++ bind(&exit); \ ++ } while (0) ++ ++#define ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT( \ ++ load_linked, store_conditional, size, aligned) \ ++ do { \ ++ Label compareExchange; \ ++ Label exit; \ ++ and_(temp0, aligned, temp1); \ ++ Subl(temp0, temp0, Operand(temp1)); \ ++ slll(temp1, 3, temp1); \ ++ bind(&compareExchange); \ ++ load_linked(temp2, 0, temp0); \ ++ mov(result.gp(), temp2); \ ++ cmpeq(result.gp(), expected.gp(), gp); \ ++ ExtractBits(result.gp(), temp2, temp1, size, false); \ ++ ExtractBits(temp2, expected.gp(), zero_reg, size, false); \ ++ wr_f(gp); \ ++ InsertBits(temp2, new_value.gp(), temp1, size); \ ++ Align(8); \ ++ store_conditional(temp2, 0, temp0); \ ++ rd_f(temp2); \ ++ beq(gp, &exit); \ ++ beq(temp2, &compareExchange); \ ++ bind(&exit); \ ++ } while (0) ++ ++void LiftoffAssembler::AtomicCompareExchange( ++ Register dst_addr, Register offset_reg, uintptr_t offset_imm, ++ LiftoffRegister expected, LiftoffRegister new_value, LiftoffRegister result, ++ StoreType type) { ++ LiftoffRegList pinned = {dst_addr, offset_reg, expected, new_value, result}; ++ Register temp0 = pinned.set(GetUnusedRegister(kGpReg, pinned)).gp(); ++ Register temp1 = pinned.set(GetUnusedRegister(kGpReg, pinned)).gp(); ++ Register temp2 = pinned.set(GetUnusedRegister(kGpReg, pinned)).gp(); ++ MemOperand dst_op = liftoff::GetMemOp(this, dst_addr, offset_reg, offset_imm); ++ Addl(temp0, dst_op.rm(), dst_op.offset()); ++ switch (type.value()) { ++ case StoreType::kI64Store8: ++ ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT(lldl, lstl, 8, 7); ++ break; ++ case StoreType::kI32Store8: ++ ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT(lldw, lstw, 8, 3); ++ break; ++ case StoreType::kI64Store16: ++ ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT(lldl, lstl, 16, 7); ++ break; ++ case StoreType::kI32Store16: ++ ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT(lldw, lstw, 16, 3); ++ break; ++ case StoreType::kI64Store32: ++ ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT(lldl, lstl, 32, 7); ++ break; ++ case StoreType::kI32Store: ++ ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER(lldw, lstw); ++ break; ++ case StoreType::kI64Store: ++ ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER(lldl, lstl); ++ break; ++ default: ++ UNREACHABLE(); ++ } ++} ++#undef ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER ++#undef ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT ++ ++// SKTODO ++void LiftoffAssembler::AtomicFence() { memb(); } ++ ++void LiftoffAssembler::LoadCallerFrameSlot(LiftoffRegister dst, ++ uint32_t caller_slot_idx, ++ ValueKind kind) { ++ MemOperand src(fp, kSystemPointerSize * (caller_slot_idx + 1)); ++ liftoff::Load(this, dst, src, kind); ++} ++ ++void LiftoffAssembler::StoreCallerFrameSlot(LiftoffRegister src, ++ uint32_t caller_slot_idx, ++ ValueKind kind) { ++ int32_t offset = kSystemPointerSize * (caller_slot_idx + 1); ++ liftoff::Store(this, fp, offset, src, kind); ++} ++ ++void LiftoffAssembler::LoadReturnStackSlot(LiftoffRegister dst, int offset, ++ ValueKind kind) { ++ liftoff::Load(this, dst, MemOperand(sp, offset), kind); ++} ++ ++void LiftoffAssembler::MoveStackValue(uint32_t dst_offset, uint32_t src_offset, ++ ValueKind kind) { ++ DCHECK_NE(dst_offset, src_offset); ++ LiftoffRegister reg = GetUnusedRegister(reg_class_for(kind), {}); ++ Fill(reg, src_offset, kind); ++ Spill(dst_offset, reg, kind); ++} ++ ++void LiftoffAssembler::Move(Register dst, Register src, ValueKind kind) { ++ DCHECK_NE(dst, src); ++ // TODO(ksreten): Handle different sizes here. ++ TurboAssembler::Move(dst, src); ++} ++ ++void LiftoffAssembler::Move(DoubleRegister dst, DoubleRegister src, ++ ValueKind kind) { ++ DCHECK_NE(dst, src); ++ TurboAssembler::Move(dst, src); ++} ++ ++void LiftoffAssembler::Spill(int offset, LiftoffRegister reg, ValueKind kind) { ++ RecordUsedSpillOffset(offset); ++ MemOperand dst = liftoff::GetStackSlot(offset); ++ switch (kind) { ++ case kI32: ++ Stw(reg.gp(), dst); ++ break; ++ case kI64: ++ case kRef: ++ case kOptRef: ++ case kRtt: ++ Stl(reg.gp(), dst); ++ break; ++ case kF32: ++ Fsts(reg.fp(), dst); ++ break; ++ case kF64: ++ TurboAssembler::Fstd(reg.fp(), dst); ++ break; ++ // case ValueType::kS128: ++ // TurboAssembler::st_b(reg.fp().toW(), dst); ++ // break; ++ default: ++ UNREACHABLE(); ++ } ++} ++ ++void LiftoffAssembler::Spill(int offset, WasmValue value) { ++ RecordUsedSpillOffset(offset); ++ MemOperand dst = liftoff::GetStackSlot(offset); ++ switch (value.type().kind()) { ++ case kI32: { ++ LiftoffRegister tmp = GetUnusedRegister(kGpReg, {}); ++ TurboAssembler::li(tmp.gp(), Operand(value.to_i32())); ++ Stw(tmp.gp(), dst); ++ break; ++ } ++ case kI64: ++ case kRef: ++ case kOptRef: { ++ LiftoffRegister tmp = GetUnusedRegister(kGpReg, {}); ++ TurboAssembler::li(tmp.gp(), value.to_i64()); ++ Stl(tmp.gp(), dst); ++ break; ++ } ++ default: ++ // kWasmF32 and kWasmF64 are unreachable, since those ++ // constants are not tracked. ++ UNREACHABLE(); ++ } ++} ++ ++void LiftoffAssembler::Fill(LiftoffRegister reg, int offset, ValueKind kind) { ++ MemOperand src = liftoff::GetStackSlot(offset); ++ switch (kind) { ++ case kI32: ++ Ldw(reg.gp(), src); ++ break; ++ case kI64: ++ case kRef: ++ case kOptRef: ++ Ldl(reg.gp(), src); ++ break; ++ case kF32: ++ Flds(reg.fp(), src); ++ break; ++ case kF64: ++ TurboAssembler::Fldd(reg.fp(), src); ++ break; ++ default: ++ UNREACHABLE(); ++ } ++} ++ ++void LiftoffAssembler::FillI64Half(Register, int offset, RegPairHalf) { ++ UNREACHABLE(); ++} ++ ++void LiftoffAssembler::FillStackSlotsWithZero(int start, int size) { ++ DCHECK_LT(0, size); ++ RecordUsedSpillOffset(start + size); ++ ++ if (size <= 12 * kStackSlotSize) { ++ // Special straight-line code for up to 12 slots. Generates one ++ // instruction per slot (<= 12 instructions total). ++ uint32_t remainder = size; ++ for (; remainder >= kStackSlotSize; remainder -= kStackSlotSize) { ++ Stl(zero_reg, liftoff::GetStackSlot(start + remainder)); ++ } ++ DCHECK(remainder == 4 || remainder == 0); ++ if (remainder) { ++ Stw(zero_reg, liftoff::GetStackSlot(start + remainder)); ++ } ++ } else { ++ // General case for bigger counts (12 instructions). ++ // Use a0 for start address (inclusive), a1 for end address (exclusive). ++ Push(a1, a0); ++ Addl(a0, fp, Operand(-start - size)); ++ Addl(a1, fp, Operand(-start)); ++ ++ Label loop; ++ bind(&loop); ++ Stl(zero_reg, MemOperand(a0)); ++ addl(a0, kSystemPointerSize, a0); ++ BranchShort(&loop, ne, a0, Operand(a1)); ++ ++ Pop(a1, a0); ++ } ++} ++ ++void LiftoffAssembler::emit_i64_clz(LiftoffRegister dst, LiftoffRegister src) { ++ TurboAssembler::Ctlzl(dst.gp(), src.gp()); ++} ++ ++void LiftoffAssembler::emit_i64_ctz(LiftoffRegister dst, LiftoffRegister src) { ++ TurboAssembler::Cttzl(dst.gp(), src.gp()); ++} ++ ++bool LiftoffAssembler::emit_i64_popcnt(LiftoffRegister dst, ++ LiftoffRegister src) { ++ TurboAssembler::Ctpopl(dst.gp(), src.gp()); ++ return true; ++} ++ ++void LiftoffAssembler::IncrementSmi(LiftoffRegister dst, int offset) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ SmiUntag(scratch, MemOperand(dst.gp(), offset)); ++ Addl(scratch, scratch, Operand(1)); ++ SmiTag(scratch); ++ Stl(scratch, MemOperand(dst.gp(), offset)); ++} ++ ++void LiftoffAssembler::emit_i32_mul(Register dst, Register lhs, Register rhs) { ++ TurboAssembler::Mulw(dst, lhs, rhs); ++} ++ ++void LiftoffAssembler::emit_i32_divs(Register dst, Register lhs, Register rhs, ++ Label* trap_div_by_zero, ++ Label* trap_div_unrepresentable) { ++ TurboAssembler::Branch(trap_div_by_zero, eq, rhs, Operand(zero_reg)); ++ ++ // Check if lhs == kMinInt and rhs == -1, since this case is unrepresentable. ++ TurboAssembler::li(kScratchReg, 1); ++ TurboAssembler::li(kScratchReg2, 1); ++ TurboAssembler::LoadZeroOnCondition(kScratchReg, lhs, Operand(kMinInt), eq); ++ TurboAssembler::LoadZeroOnCondition(kScratchReg2, rhs, Operand(-1), eq); ++ addl(kScratchReg, kScratchReg2, kScratchReg); ++ TurboAssembler::Branch(trap_div_unrepresentable, eq, kScratchReg, ++ Operand(zero_reg)); ++ ++ // addw(lhs, 0, lhs); ++ // addw(rhs, 0, rhs); ++ ifmovd(lhs, kScratchDoubleReg1); ++ ifmovd(rhs, kScratchDoubleReg2); ++ fcvtld(kScratchDoubleReg1, kScratchDoubleReg1); ++ fcvtld(kScratchDoubleReg2, kScratchDoubleReg2); ++ fdivd(kScratchDoubleReg1, kScratchDoubleReg2, kScratchDoubleReg1); ++ fcvtdl_z(kScratchDoubleReg1, kScratchDoubleReg2); ++ fimovd(kScratchDoubleReg2, dst); ++ // TurboAssembler::Div(dst, lhs, rhs); ++} ++ ++void LiftoffAssembler::emit_i32_divu(Register dst, Register lhs, Register rhs, ++ Label* trap_div_by_zero) { ++ TurboAssembler::Branch(trap_div_by_zero, eq, rhs, Operand(zero_reg)); ++ zapnot(lhs, 0xf, lhs); ++ zapnot(rhs, 0xf, rhs); ++ ifmovd(lhs, kScratchDoubleReg1); ++ ifmovd(rhs, kScratchDoubleReg2); ++ fcvtld(kScratchDoubleReg1, kScratchDoubleReg1); ++ fcvtld(kScratchDoubleReg2, kScratchDoubleReg2); ++ fdivd(kScratchDoubleReg1, kScratchDoubleReg2, kScratchDoubleReg1); ++ fcvtdl_z(kScratchDoubleReg1, kScratchDoubleReg2); ++ fimovd(kScratchDoubleReg2, dst); ++ // TurboAssembler::Divu(dst, lhs, rhs); ++} ++ ++void LiftoffAssembler::emit_i32_rems(Register dst, Register lhs, Register rhs, ++ Label* trap_div_by_zero) { ++ TurboAssembler::Branch(trap_div_by_zero, eq, rhs, Operand(zero_reg)); ++ // addw(lhs, 0, lhs); ++ // addw(rhs, 0, rhs); ++ ifmovd(lhs, kScratchDoubleReg1); ++ ifmovd(rhs, kScratchDoubleReg2); ++ fcvtld(kScratchDoubleReg1, kScratchDoubleReg1); ++ fcvtld(kScratchDoubleReg2, kScratchDoubleReg2); ++ fdivd(kScratchDoubleReg1, kScratchDoubleReg2, kScratchDoubleReg1); ++ fcvtdl_z(kScratchDoubleReg1, kScratchDoubleReg2); ++ fimovd(kScratchDoubleReg2, kScratchReg); ++ mulw(kScratchReg, rhs, kScratchReg); ++ subw(lhs, kScratchReg, dst); ++ // TurboAssembler::Mod(dst, lhs, rhs); ++} ++ ++void LiftoffAssembler::emit_i32_remu(Register dst, Register lhs, Register rhs, ++ Label* trap_div_by_zero) { ++ TurboAssembler::Branch(trap_div_by_zero, eq, rhs, Operand(zero_reg)); ++ zapnot(lhs, 0xf, lhs); ++ zapnot(rhs, 0xf, rhs); ++ ifmovd(lhs, kScratchDoubleReg1); ++ ifmovd(rhs, kScratchDoubleReg2); ++ fcvtld(kScratchDoubleReg1, kScratchDoubleReg1); ++ fcvtld(kScratchDoubleReg2, kScratchDoubleReg2); ++ fdivd(kScratchDoubleReg1, kScratchDoubleReg2, kScratchDoubleReg1); ++ fcvtdl_z(kScratchDoubleReg1, kScratchDoubleReg2); ++ fimovd(kScratchDoubleReg2, kScratchReg); ++ mulw(kScratchReg, rhs, kScratchReg); ++ subw(lhs, kScratchReg, dst); ++ // TurboAssembler::Modu(dst, lhs, rhs); ++} ++ ++#define I32_BINOP(name, instruction) \ ++ void LiftoffAssembler::emit_i32_##name(Register dst, Register lhs, \ ++ Register rhs) { \ ++ instruction(lhs, rhs, dst); \ ++ } ++ ++// clang-format off ++I32_BINOP(add, addw) ++I32_BINOP(sub, subw) ++I32_BINOP(and, and_) ++I32_BINOP(or, or_) ++I32_BINOP(xor, xor_) ++// clang-format on ++ ++#undef I32_BINOP ++ ++#define I32_BINOP_I(name, instruction) \ ++ void LiftoffAssembler::emit_i32_##name##i(Register dst, Register lhs, \ ++ int32_t imm) { \ ++ instruction(dst, lhs, Operand(imm)); \ ++ } ++ ++// clang-format off ++I32_BINOP_I(add, Addw) ++I32_BINOP_I(sub, Subw) ++I32_BINOP_I(and, And) ++I32_BINOP_I(or, Or) ++I32_BINOP_I(xor, Xor) ++// clang-format on ++ ++#undef I32_BINOP_I ++ ++void LiftoffAssembler::emit_i32_clz(Register dst, Register src) { ++ TurboAssembler::Ctlzw(dst, src); ++} ++ ++void LiftoffAssembler::emit_i32_ctz(Register dst, Register src) { ++ TurboAssembler::Cttzw(dst, src); ++} ++ ++bool LiftoffAssembler::emit_i32_popcnt(Register dst, Register src) { ++ TurboAssembler::Ctpopw(dst, src); ++ return true; ++} ++ ++#define I32_SHIFTOP(name, instruction) \ ++ void LiftoffAssembler::emit_i32_##name(Register dst, Register src, \ ++ Register amount) { \ ++ instruction(dst, src, amount); \ ++ } ++#define I32_SHIFTOP_I(name, instruction) \ ++ I32_SHIFTOP(name, instruction) \ ++ void LiftoffAssembler::emit_i32_##name##i(Register dst, Register src, \ ++ int amount) { \ ++ instruction(dst, src, (amount & 31)); \ ++ } ++ ++I32_SHIFTOP_I(shl, Sllw) ++I32_SHIFTOP_I(sar, Sraw) ++I32_SHIFTOP_I(shr, Srlw) ++ ++#undef I32_SHIFTOP ++#undef I32_SHIFTOP_I ++ ++void LiftoffAssembler::emit_i64_addi(LiftoffRegister dst, LiftoffRegister lhs, ++ int64_t imm) { ++ TurboAssembler::Addl(dst.gp(), lhs.gp(), Operand(imm)); ++} ++ ++void LiftoffAssembler::emit_i64_mul(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ TurboAssembler::Mull(dst.gp(), lhs.gp(), rhs.gp()); ++} ++ ++bool LiftoffAssembler::emit_i64_divs(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs, ++ Label* trap_div_by_zero, ++ Label* trap_div_unrepresentable) { ++ // TODO ++ return false; ++} ++ ++bool LiftoffAssembler::emit_i64_divu(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs, ++ Label* trap_div_by_zero) { ++ // TODO ++ return false; ++ // TurboAssembler::Branch(trap_div_by_zero, eq, rhs.gp(), Operand(zero_reg)); ++ // TurboAssembler::Ddivu(dst.gp(), lhs.gp(), rhs.gp()); ++ // return true; ++} ++ ++bool LiftoffAssembler::emit_i64_rems(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs, ++ Label* trap_div_by_zero) { ++ // TODO ++ return false; ++ // TurboAssembler::Branch(trap_div_by_zero, eq, rhs.gp(), Operand(zero_reg)); ++ // TurboAssembler::Dmod(dst.gp(), lhs.gp(), rhs.gp()); ++ // return true; ++} ++ ++bool LiftoffAssembler::emit_i64_remu(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs, ++ Label* trap_div_by_zero) { ++ // TODO ++ return false; ++} ++ ++#define I64_BINOP(name, instruction) \ ++ void LiftoffAssembler::emit_i64_##name( \ ++ LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs) { \ ++ instruction(lhs.gp(), rhs.gp(), dst.gp()); \ ++ } ++ ++// clang-format off ++I64_BINOP(add, addl) ++I64_BINOP(sub, subl) ++I64_BINOP(and, and_) ++I64_BINOP(or, or_) ++I64_BINOP(xor, xor_) ++// clang-format on ++ ++#undef I64_BINOP ++ ++#define I64_BINOP_I(name, instruction) \ ++ void LiftoffAssembler::emit_i64_##name##i( \ ++ LiftoffRegister dst, LiftoffRegister lhs, int32_t imm) { \ ++ instruction(dst.gp(), lhs.gp(), Operand(imm)); \ ++ } ++ ++// clang-format off ++I64_BINOP_I(and, And) ++I64_BINOP_I(or, Or) ++I64_BINOP_I(xor, Xor) ++// clang-format on ++ ++#undef I64_BINOP_I ++ ++#define I64_SHIFTOP(name, instruction) \ ++ void LiftoffAssembler::emit_i64_##name( \ ++ LiftoffRegister dst, LiftoffRegister src, Register amount) { \ ++ instruction(src.gp(), amount, dst.gp()); \ ++ } ++#define I64_SHIFTOP_I(name, instruction) \ ++ I64_SHIFTOP(name, instruction) \ ++ void LiftoffAssembler::emit_i64_##name##i(LiftoffRegister dst, \ ++ LiftoffRegister src, int amount) { \ ++ instruction(src.gp(), (amount & 63), dst.gp()); \ ++ } ++ ++I64_SHIFTOP_I(shl, slll) ++I64_SHIFTOP_I(sar, sral) ++I64_SHIFTOP_I(shr, srll) ++ ++#undef I64_SHIFTOP ++#undef I64_SHIFTOP_I ++ ++void LiftoffAssembler::emit_u32_to_uintptr(Register dst, Register src) { ++ zapnot(src, 0xf, dst); ++} ++ ++void LiftoffAssembler::emit_f32_min(DoubleRegister dst, DoubleRegister lhs, ++ DoubleRegister rhs) { ++ Label ool, done; ++ TurboAssembler::Float32Min(dst, lhs, rhs, &ool); ++ Branch(&done); ++ ++ bind(&ool); ++ TurboAssembler::Float32MinOutOfLine(dst, lhs, rhs); ++ bind(&done); ++} ++ ++void LiftoffAssembler::emit_f32_max(DoubleRegister dst, DoubleRegister lhs, ++ DoubleRegister rhs) { ++ Label ool, done; ++ TurboAssembler::Float32Max(dst, lhs, rhs, &ool); ++ Branch(&done); ++ ++ bind(&ool); ++ TurboAssembler::Float32MaxOutOfLine(dst, lhs, rhs); ++ bind(&done); ++} ++ ++// SKTODO ++void LiftoffAssembler::emit_f32_copysign(DoubleRegister dst, DoubleRegister lhs, ++ DoubleRegister rhs) { ++ bailout(kComplexOperation, "f32_copysign"); ++} ++ ++void LiftoffAssembler::emit_f64_min(DoubleRegister dst, DoubleRegister lhs, ++ DoubleRegister rhs) { ++ Label ool, done; ++ TurboAssembler::Float64Min(dst, lhs, rhs, &ool); ++ Branch(&done); ++ ++ bind(&ool); ++ TurboAssembler::Float64MinOutOfLine(dst, lhs, rhs); ++ bind(&done); ++} ++ ++void LiftoffAssembler::emit_f64_max(DoubleRegister dst, DoubleRegister lhs, ++ DoubleRegister rhs) { ++ Label ool, done; ++ TurboAssembler::Float64Max(dst, lhs, rhs, &ool); ++ Branch(&done); ++ ++ bind(&ool); ++ TurboAssembler::Float64MaxOutOfLine(dst, lhs, rhs); ++ bind(&done); ++} ++ ++// SKTODO ++void LiftoffAssembler::emit_f64_copysign(DoubleRegister dst, DoubleRegister lhs, ++ DoubleRegister rhs) { ++ bailout(kComplexOperation, "f64_copysign"); ++} ++ ++#define FP_BINOP(name, instruction) \ ++ void LiftoffAssembler::emit_##name(DoubleRegister dst, DoubleRegister lhs, \ ++ DoubleRegister rhs) { \ ++ instruction(lhs, rhs, dst); \ ++ } ++#define FP_UNOP_X(name, instruction) \ ++ void LiftoffAssembler::emit_##name(DoubleRegister dst, DoubleRegister src) { \ ++ instruction(src, dst); \ ++ } ++ ++#define FP_UNOP(name, instruction) \ ++ void LiftoffAssembler::emit_##name(DoubleRegister dst, DoubleRegister src) { \ ++ TurboAssembler::instruction(dst, src); \ ++ } ++ ++#define FP_UNOP_RETURN_TRUE(name, instruction) \ ++ bool LiftoffAssembler::emit_##name(DoubleRegister dst, DoubleRegister src) { \ ++ instruction(dst, src); \ ++ return true; \ ++ } ++ ++FP_BINOP(f32_add, fadds) ++FP_BINOP(f32_sub, fsubs) ++FP_BINOP(f32_mul, fmuls) ++FP_BINOP(f32_div, fdivs) ++FP_UNOP(f32_abs, Fabs) ++FP_UNOP(f32_neg, Fneg) ++FP_UNOP_RETURN_TRUE(f32_ceil, Ceils) ++FP_UNOP_RETURN_TRUE(f32_floor, Floors) ++FP_UNOP_RETURN_TRUE(f32_trunc, Truncs) ++FP_UNOP_RETURN_TRUE(f32_nearest_int, Rounds) ++FP_UNOP_X(f32_sqrt, fsqrts) ++FP_BINOP(f64_add, faddd) ++FP_BINOP(f64_sub, fsubd) ++FP_BINOP(f64_mul, fmuld) ++FP_BINOP(f64_div, fdivd) ++FP_UNOP(f64_abs, Fabs) ++FP_UNOP(f64_neg, Fneg) ++FP_UNOP_RETURN_TRUE(f64_ceil, Ceild) ++FP_UNOP_RETURN_TRUE(f64_floor, Floord) ++FP_UNOP_RETURN_TRUE(f64_trunc, Truncd) ++FP_UNOP_RETURN_TRUE(f64_nearest_int, Roundd) ++FP_UNOP_X(f64_sqrt, fsqrtd) ++ ++#undef FP_BINOP ++#undef FP_UNOP ++#undef FP_UNOP_RETURN_TRUE ++ ++bool LiftoffAssembler::emit_type_conversion(WasmOpcode opcode, ++ LiftoffRegister dst, ++ LiftoffRegister src, Label* trap) { ++ switch (opcode) { ++ case kExprI32ConvertI64: ++ addw(src.gp(), zero_reg, dst.gp()); ++ return true; ++ case kExprI32SConvertF32: { ++ LiftoffRegister rounded = GetUnusedRegister(kFpReg, LiftoffRegList{src}); ++ LiftoffRegister converted_back = ++ GetUnusedRegister(kFpReg, LiftoffRegList{src, rounded}); ++ ++ // Real conversion. ++ TurboAssembler::Truncs(rounded.fp(), src.fp()); ++ ftruncsw(rounded.fp(), kScratchDoubleReg); ++ fimovs(kScratchDoubleReg, dst.gp()); ++ // Avoid INT32_MAX as an overflow indicator and use INT32_MIN instead, ++ // because INT32_MIN allows easier out-of-bounds detection. ++ TurboAssembler::Addw(kScratchReg, dst.gp(), 1); ++ TurboAssembler::Cmplt(kScratchReg2, kScratchReg, dst.gp()); ++ TurboAssembler::Selne(dst.gp(), kScratchReg, kScratchReg2); ++ ++ // Checking if trap. ++ ifmovs(dst.gp(), kScratchDoubleReg); ++ fcvtws(kScratchDoubleReg, converted_back.fp()); ++ TurboAssembler::CompareF(EQ, rounded.fp(), converted_back.fp()); ++ TurboAssembler::BranchFalseF(trap); ++ return true; ++ } ++ case kExprI32UConvertF32: { ++ LiftoffRegister rounded = GetUnusedRegister(kFpReg, LiftoffRegList{src}); ++ LiftoffRegister converted_back = ++ GetUnusedRegister(kFpReg, LiftoffRegList{src, rounded}); ++ ++ // Real conversion. ++ TurboAssembler::Truncs(rounded.fp(), src.fp()); ++ TurboAssembler::Trunc_uw_s(dst.gp(), rounded.fp(), kScratchDoubleReg); ++ // Avoid UINT32_MAX as an overflow indicator and use 0 instead, ++ // because 0 allows easier out-of-bounds detection. ++ TurboAssembler::Addw(kScratchReg, dst.gp(), 1); ++ TurboAssembler::Seleq(dst.gp(), zero_reg, kScratchReg); ++ ++ // Checking if trap. ++ TurboAssembler::Cvt_d_uw(converted_back.fp(), dst.gp()); ++ fcvtds_(converted_back.fp(), converted_back.fp()); ++ TurboAssembler::CompareF(EQ, rounded.fp(), converted_back.fp()); ++ TurboAssembler::BranchFalseF(trap); ++ return true; ++ } ++ case kExprI32SConvertF64: { ++ LiftoffRegister rounded = GetUnusedRegister(kFpReg, LiftoffRegList{src}); ++ LiftoffRegister converted_back = ++ GetUnusedRegister(kFpReg, LiftoffRegList{src, rounded}); ++ ++ // Real conversion. ++ TurboAssembler::Truncd(rounded.fp(), src.fp()); ++ ftruncdw(rounded.fp(), kScratchDoubleReg); ++ fimovs(kScratchDoubleReg, dst.gp()); ++ ++ // Checking if trap. ++ fcvtwd(kScratchDoubleReg, converted_back.fp()); ++ TurboAssembler::CompareF(EQ, rounded.fp(), converted_back.fp()); ++ TurboAssembler::BranchFalseF(trap); ++ return true; ++ } ++ case kExprI32UConvertF64: { ++ LiftoffRegister rounded = GetUnusedRegister(kFpReg, LiftoffRegList{src}); ++ LiftoffRegister converted_back = ++ GetUnusedRegister(kFpReg, LiftoffRegList{src, rounded}); ++ ++ // Real conversion. ++ TurboAssembler::Truncd(rounded.fp(), src.fp()); ++ TurboAssembler::Trunc_uw_d(dst.gp(), rounded.fp(), kScratchDoubleReg); ++ ++ // Checking if trap. ++ TurboAssembler::Cvt_d_uw(converted_back.fp(), dst.gp()); ++ TurboAssembler::CompareF(EQ, rounded.fp(), converted_back.fp()); ++ TurboAssembler::BranchFalseF(trap); ++ return true; ++ } ++ case kExprI32ReinterpretF32: ++ fimovs(src.fp(), dst.gp()); ++ return true; ++ case kExprI64SConvertI32: ++ addw(src.gp(), 0, dst.gp()); ++ return true; ++ case kExprI64UConvertI32: ++ zapnot(src.gp(), 0xf, dst.gp()); ++ return true; ++ case kExprI64SConvertF32: { ++ LiftoffRegister rounded = GetUnusedRegister(kFpReg, LiftoffRegList{src}); ++ LiftoffRegister converted_back = ++ GetUnusedRegister(kFpReg, LiftoffRegList{src, rounded}); ++ ++ // Real conversion. ++ TurboAssembler::Truncs(rounded.fp(), src.fp()); ++ ftruncsl(rounded.fp(), kScratchDoubleReg); ++ fimovd(kScratchDoubleReg, dst.gp()); ++ // Avoid INT64_MAX as an overflow indicator and use INT64_MIN instead, ++ // because INT64_MIN allows easier out-of-bounds detection. ++ TurboAssembler::Addl(kScratchReg, dst.gp(), 1); ++ TurboAssembler::Cmplt(kScratchReg2, kScratchReg, dst.gp()); ++ TurboAssembler::Selne(dst.gp(), kScratchReg, kScratchReg2); ++ ++ // Checking if trap. ++ ifmovd(dst.gp(), kScratchDoubleReg); ++ fcvtls(kScratchDoubleReg, converted_back.fp()); ++ TurboAssembler::CompareF(EQ, rounded.fp(), converted_back.fp()); ++ TurboAssembler::BranchFalseF(trap); ++ return true; ++ } ++ case kExprI64UConvertF32: { ++ // Real conversion. ++ TurboAssembler::Trunc_ul_s(dst.gp(), src.fp(), kScratchDoubleReg, ++ kScratchReg); ++ ++ // Checking if trap. ++ TurboAssembler::Branch(trap, eq, kScratchReg, Operand(zero_reg)); ++ return true; ++ } ++ case kExprI64SConvertF64: { ++ LiftoffRegister rounded = GetUnusedRegister(kFpReg, LiftoffRegList{src}); ++ LiftoffRegister converted_back = ++ GetUnusedRegister(kFpReg, LiftoffRegList{src, rounded}); ++ ++ // Real conversion. ++ TurboAssembler::Truncd(rounded.fp(), src.fp()); ++ ftruncdl(rounded.fp(), kScratchDoubleReg); ++ fimovd(kScratchDoubleReg, dst.gp()); ++ // Avoid INT64_MAX as an overflow indicator and use INT64_MIN instead, ++ // because INT64_MIN allows easier out-of-bounds detection. ++ TurboAssembler::Addl(kScratchReg, dst.gp(), 1); ++ TurboAssembler::Cmplt(kScratchReg2, kScratchReg, dst.gp()); ++ TurboAssembler::Selne(dst.gp(), kScratchReg, kScratchReg2); ++ ++ // Checking if trap. ++ ifmovd(dst.gp(), kScratchDoubleReg); ++ fcvtld(kScratchDoubleReg, converted_back.fp()); ++ TurboAssembler::CompareF(EQ, rounded.fp(), converted_back.fp()); ++ TurboAssembler::BranchFalseF(trap); ++ return true; ++ } ++ case kExprI64UConvertF64: { ++ // Real conversion. ++ TurboAssembler::Trunc_ul_d(dst.gp(), src.fp(), kScratchDoubleReg, ++ kScratchReg); ++ ++ // Checking if trap. ++ TurboAssembler::Branch(trap, eq, kScratchReg, Operand(zero_reg)); ++ return true; ++ } ++ case kExprI64ReinterpretF64: ++ fimovd(src.fp(), dst.gp()); ++ return true; ++ case kExprF32SConvertI32: { ++ LiftoffRegister scratch = GetUnusedRegister(kFpReg, LiftoffRegList{dst}); ++ ifmovs(src.gp(), scratch.fp()); ++ fcvtws(scratch.fp(), dst.fp()); ++ return true; ++ } ++ case kExprF32UConvertI32: ++ TurboAssembler::Cvt_s_uw(dst.fp(), src.gp()); ++ return true; ++ case kExprF32ConvertF64: ++ fcvtds(src.fp(), dst.fp()); ++ return true; ++ case kExprF32ReinterpretI32: ++ ifmovs(src.gp(), dst.fp()); ++ return true; ++ case kExprF64SConvertI32: { ++ LiftoffRegister scratch = GetUnusedRegister(kFpReg, LiftoffRegList{dst}); ++ ifmovs(src.gp(), scratch.fp()); ++ fcvtwd(scratch.fp(), dst.fp()); ++ return true; ++ } ++ case kExprF64UConvertI32: ++ TurboAssembler::Cvt_d_uw(dst.fp(), src.gp()); ++ return true; ++ case kExprF64ConvertF32: ++ fcvtsd(src.fp(), dst.fp()); ++ return true; ++ case kExprF64ReinterpretI64: ++ ifmovd(src.gp(), dst.fp()); ++ return true; ++ case kExprI32SConvertSatF32: ++ bailout(kNonTrappingFloatToInt, "kExprI32SConvertSatF32"); ++ return true; ++ case kExprI32UConvertSatF32: ++ bailout(kNonTrappingFloatToInt, "kExprI32UConvertSatF32"); ++ return true; ++ case kExprI32SConvertSatF64: ++ bailout(kNonTrappingFloatToInt, "kExprI32SConvertSatF64"); ++ return true; ++ case kExprI32UConvertSatF64: ++ bailout(kNonTrappingFloatToInt, "kExprI32UConvertSatF64"); ++ return true; ++ case kExprI64SConvertSatF32: ++ bailout(kNonTrappingFloatToInt, "kExprI64SConvertSatF32"); ++ return true; ++ case kExprI64UConvertSatF32: ++ bailout(kNonTrappingFloatToInt, "kExprI64UConvertSatF32"); ++ return true; ++ case kExprI64SConvertSatF64: ++ bailout(kNonTrappingFloatToInt, "kExprI64SConvertSatF64"); ++ return true; ++ case kExprI64UConvertSatF64: ++ bailout(kNonTrappingFloatToInt, "kExprI64UConvertSatF64"); ++ return true; ++ default: ++ return false; ++ } ++} ++ ++void LiftoffAssembler::emit_i32_signextend_i8(Register dst, Register src) { ++ bailout(kComplexOperation, "i32_signextend_i8"); ++} ++ ++void LiftoffAssembler::emit_i32_signextend_i16(Register dst, Register src) { ++ bailout(kComplexOperation, "i32_signextend_i16"); ++} ++ ++void LiftoffAssembler::emit_i64_signextend_i8(LiftoffRegister dst, ++ LiftoffRegister src) { ++ bailout(kComplexOperation, "i64_signextend_i8"); ++} ++ ++void LiftoffAssembler::emit_i64_signextend_i16(LiftoffRegister dst, ++ LiftoffRegister src) { ++ bailout(kComplexOperation, "i64_signextend_i16"); ++} ++ ++void LiftoffAssembler::emit_i64_signextend_i32(LiftoffRegister dst, ++ LiftoffRegister src) { ++ bailout(kComplexOperation, "i64_signextend_i32"); ++} ++ ++void LiftoffAssembler::emit_jump(Label* label) { ++ TurboAssembler::Branch(label); ++} ++ ++void LiftoffAssembler::emit_jump(Register target) { ++ TurboAssembler::Jump(target); ++} ++ ++void LiftoffAssembler::emit_cond_jump(LiftoffCondition liftoff_cond, ++ Label* label, ValueKind kind, ++ Register lhs, Register rhs) { ++ Condition cond = liftoff::ToCondition(liftoff_cond); ++ if (rhs == no_reg) { ++ DCHECK(kind == kI32 || kind == kI64); ++ TurboAssembler::Branch(label, cond, lhs, Operand(zero_reg)); ++ } else { ++ DCHECK((kind == kI32 || kind == kI64) || ++ (is_reference(kind) && ++ (liftoff_cond == kEqual || liftoff_cond == kUnequal))); ++ TurboAssembler::Branch(label, cond, lhs, Operand(rhs)); ++ } ++} ++ ++void LiftoffAssembler::emit_i32_cond_jumpi(LiftoffCondition liftoff_cond, ++ Label* label, Register lhs, ++ int32_t imm) { ++ Condition cond = liftoff::ToCondition(liftoff_cond); ++ TurboAssembler::Branch(label, cond, lhs, Operand(imm)); ++} ++ ++void LiftoffAssembler::emit_i32_subi_jump_negative(Register value, ++ int subtrahend, ++ Label* result_negative) { ++ TurboAssembler::Subl(value, value, Operand(subtrahend)); ++ TurboAssembler::Branch(result_negative, less, value, Operand(zero_reg)); ++} ++ ++void LiftoffAssembler::emit_i32_eqz(Register dst, Register src) { ++ cmpeq(src, 0, dst); ++} ++ ++void LiftoffAssembler::emit_i32_set_cond(LiftoffCondition liftoff_cond, ++ Register dst, Register lhs, ++ Register rhs) { ++ Condition cond = liftoff::ToCondition(liftoff_cond); ++ Register tmp = dst; ++ if (dst == lhs || dst == rhs) { ++ tmp = GetUnusedRegister(kGpReg, LiftoffRegList{lhs, rhs}).gp(); ++ } ++ // Write 1 as result. ++ TurboAssembler::li(tmp, 1); ++ ++ // If negative condition is true, write 0 as result. ++ Condition neg_cond = NegateCondition(cond); ++ TurboAssembler::LoadZeroOnCondition(tmp, lhs, Operand(rhs), neg_cond); ++ ++ // If tmp != dst, result will be moved. ++ TurboAssembler::Move(dst, tmp); ++} ++ ++void LiftoffAssembler::emit_i64_eqz(Register dst, LiftoffRegister src) { ++ cmpeq(src.gp(), 0, dst); ++} ++ ++void LiftoffAssembler::emit_i64_set_cond(LiftoffCondition liftoff_cond, ++ Register dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ Condition cond = liftoff::ToCondition(liftoff_cond); ++ Register tmp = dst; ++ if (dst == lhs.gp() || dst == rhs.gp()) { ++ tmp = GetUnusedRegister(kGpReg, LiftoffRegList{lhs, rhs}).gp(); ++ } ++ // Write 1 as result. ++ TurboAssembler::li(tmp, 1); ++ ++ // If negative condition is true, write 0 as result. ++ Condition neg_cond = NegateCondition(cond); ++ TurboAssembler::LoadZeroOnCondition(tmp, lhs.gp(), Operand(rhs.gp()), ++ neg_cond); ++ ++ // If tmp != dst, result will be moved. ++ TurboAssembler::Move(dst, tmp); ++} ++ ++namespace liftoff { ++ ++inline FPUCondition ConditionToConditionCmpFPU(LiftoffCondition condition, ++ bool* predicate) { ++ switch (condition) { ++ case kEqual: ++ *predicate = true; ++ return EQ; ++ case kUnequal: ++ *predicate = false; ++ return EQ; ++ case kUnsignedLessThan: ++ *predicate = true; ++ return OLT; ++ case kUnsignedGreaterEqual: ++ *predicate = false; ++ return OLT; ++ case kUnsignedLessEqual: ++ *predicate = true; ++ return OLE; ++ case kUnsignedGreaterThan: ++ *predicate = false; ++ return OLE; ++ default: ++ *predicate = true; ++ break; ++ } ++ UNREACHABLE(); ++} ++// SKTODO ++#if 0 ++inline void EmitAnyTrue(LiftoffAssembler* assm, LiftoffRegister dst, ++ LiftoffRegister src) { ++ Label all_false; ++ assm->BranchSWSA(&all_false, SWSA_BRANCH_V, all_zero, src.fp().toW()); ++ assm->li(dst.gp(), 0l); ++ assm->li(dst.gp(), 1); ++ assm->bind(&all_false); ++} ++ ++inline void EmitAllTrue(LiftoffAssembler* assm, LiftoffRegister dst, ++ LiftoffRegister src, SWSABranchDF swsa_branch_df) { ++ Label all_true; ++ assm->BranchSWSA(&all_true, swsa_branch_df, all_not_zero, src.fp().toW()); ++ assm->li(dst.gp(), 1); ++ assm->li(dst.gp(), 0l); ++ assm->bind(&all_true); ++} ++#endif ++} // namespace liftoff ++ ++void LiftoffAssembler::emit_f32_set_cond(LiftoffCondition liftoff_cond, ++ Register dst, DoubleRegister lhs, ++ DoubleRegister rhs) { ++ Condition cond = liftoff::ToCondition(liftoff_cond); ++ Label not_nan, cont; ++ TurboAssembler::CompareIsNanF(lhs, rhs); ++ TurboAssembler::BranchFalseF(¬_nan); ++ // If one of the operands is NaN, return 1 for f32.ne, else 0. ++ if (cond == ne) { ++ TurboAssembler::li(dst, 1); ++ } else { ++ TurboAssembler::Move(dst, zero_reg); ++ } ++ TurboAssembler::Branch(&cont); ++ ++ bind(¬_nan); ++ ++ TurboAssembler::li(dst, 1); ++ bool predicate; ++ FPUCondition fcond = liftoff::ConditionToConditionCmpFPU(liftoff_cond, &predicate); ++ TurboAssembler::CompareF(fcond, lhs, rhs); ++ if (predicate) { ++ TurboAssembler::LoadZeroIfNotFPUCondition(dst); ++ } else { ++ TurboAssembler::LoadZeroIfFPUCondition(dst); ++ } ++ ++ bind(&cont); ++} ++ ++void LiftoffAssembler::emit_f64_set_cond(LiftoffCondition liftoff_cond, ++ Register dst, DoubleRegister lhs, ++ DoubleRegister rhs) { ++ Condition cond = liftoff::ToCondition(liftoff_cond); ++ Label not_nan, cont; ++ TurboAssembler::CompareIsNanF(lhs, rhs); ++ TurboAssembler::BranchFalseF(¬_nan); ++ // If one of the operands is NaN, return 1 for f64.ne, else 0. ++ if (cond == ne) { ++ TurboAssembler::li(dst, 1); ++ } else { ++ TurboAssembler::Move(dst, zero_reg); ++ } ++ TurboAssembler::Branch(&cont); ++ ++ bind(¬_nan); ++ ++ TurboAssembler::li(dst, 1); ++ bool predicate; ++ FPUCondition fcond = liftoff::ConditionToConditionCmpFPU(liftoff_cond, &predicate); ++ TurboAssembler::CompareF(fcond, lhs, rhs); ++ if (predicate) { ++ TurboAssembler::LoadZeroIfNotFPUCondition(dst); ++ } else { ++ TurboAssembler::LoadZeroIfFPUCondition(dst); ++ } ++ ++ bind(&cont); ++} ++ ++bool LiftoffAssembler::emit_select(LiftoffRegister dst, Register condition, ++ LiftoffRegister true_value, ++ LiftoffRegister false_value, ++ ValueKind kind) { ++ return false; ++} ++ ++void LiftoffAssembler::emit_smi_check(Register obj, Label* target, ++ SmiCheckMode mode) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ And(scratch, obj, Operand(kSmiTagMask)); ++ Condition condition = mode == kJumpOnSmi ? eq : ne; ++ Branch(target, condition, scratch, Operand(zero_reg)); ++} ++ ++// SKTODO ++void LiftoffAssembler::LoadTransform(LiftoffRegister dst, Register src_addr, ++ Register offset_reg, uintptr_t offset_imm, ++ LoadType type, ++ LoadTransformationKind transform, ++ uint32_t* protected_load_pc) { ++ UNREACHABLE(); ++#if 0 ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ MemOperand src_op = ++ liftoff::GetMemOp(this, src_addr, offset_reg, offset_imm); ++ SWSARegister dst_swsa = dst.fp().toW(); ++ *protected_load_pc = pc_offset(); ++ MachineType memtype = type.mem_type(); ++ ++ if (transform == LoadTransformationKind::kExtend) { ++ Ld(scratch, src_op); ++ if (memtype == MachineType::Int8()) { ++ fill_d(dst_swsa, scratch); ++ clti_s_b(kSimd128ScratchReg, dst_swsa, 0); ++ ilvr_b(dst_swsa, kSimd128ScratchReg, dst_swsa); ++ } else if (memtype == MachineType::Uint8()) { ++ xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero); ++ fill_d(dst_swsa, scratch); ++ ilvr_b(dst_swsa, kSimd128RegZero, dst_swsa); ++ } else if (memtype == MachineType::Int16()) { ++ fill_d(dst_swsa, scratch); ++ clti_s_h(kSimd128ScratchReg, dst_swsa, 0); ++ ilvr_h(dst_swsa, kSimd128ScratchReg, dst_swsa); ++ } else if (memtype == MachineType::Uint16()) { ++ xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero); ++ fill_d(dst_swsa, scratch); ++ ilvr_h(dst_swsa, kSimd128RegZero, dst_swsa); ++ } else if (memtype == MachineType::Int32()) { ++ fill_d(dst_swsa, scratch); ++ clti_s_w(kSimd128ScratchReg, dst_swsa, 0); ++ ilvr_w(dst_swsa, kSimd128ScratchReg, dst_swsa); ++ } else if (memtype == MachineType::Uint32()) { ++ xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero); ++ fill_d(dst_swsa, scratch); ++ ilvr_w(dst_swsa, kSimd128RegZero, dst_swsa); ++ } ++ } else if (transform == LoadTransformationKind::kZeroExtend) { ++ xor_v(dst_swsa, dst_swsa, dst_swsa); ++ if (memtype == MachineType::Int32()) { ++ Lwu(scratch, src_op); ++ insert_w(dst_swsa, 0, scratch); ++ } else { ++ DCHECK_EQ(MachineType::Int64(), memtype); ++ Ld(scratch, src_op); ++ insert_d(dst_swsa, 0, scratch); ++ } ++ } else { ++ DCHECK_EQ(LoadTransformationKind::kSplat, transform); ++ if (memtype == MachineType::Int8()) { ++ Lb(scratch, src_op); ++ fill_b(dst_swsa, scratch); ++ } else if (memtype == MachineType::Int16()) { ++ Lh(scratch, src_op); ++ fill_h(dst_swsa, scratch); ++ } else if (memtype == MachineType::Int32()) { ++ Lw(scratch, src_op); ++ fill_w(dst_swsa, scratch); ++ } else if (memtype == MachineType::Int64()) { ++ Ld(scratch, src_op); ++ fill_d(dst_swsa, scratch); ++ } ++ } ++#endif ++} ++ ++void LiftoffAssembler::LoadLane(LiftoffRegister dst, LiftoffRegister src, ++ Register addr, Register offset_reg, ++ uintptr_t offset_imm, LoadType type, ++ uint8_t laneidx, uint32_t* protected_load_pc) { ++ UNREACHABLE(); ++} ++ ++void LiftoffAssembler::StoreLane(Register dst, Register offset, ++ uintptr_t offset_imm, LiftoffRegister src, ++ StoreType type, uint8_t lane, ++ uint32_t* protected_store_pc) { ++ UNREACHABLE(); ++} ++ ++void LiftoffAssembler::emit_i8x16_shuffle(LiftoffRegister dst, ++ LiftoffRegister lhs, ++ LiftoffRegister rhs, ++ const uint8_t shuffle[16], ++ bool is_swizzle) { ++ UNREACHABLE(); ++#if 0 ++ SWSARegister dst_swsa = dst.fp().toW(); ++ SWSARegister lhs_swsa = lhs.fp().toW(); ++ SWSARegister rhs_swsa = rhs.fp().toW(); ++ ++ uint64_t control_hi = 0; ++ uint64_t control_low = 0; ++ for (int i = 7; i >= 0; i--) { ++ control_hi <<= 8; ++ control_hi |= shuffle[i + 8]; ++ control_low <<= 8; ++ control_low |= shuffle[i]; ++ } ++ ++ if (dst_swsa == lhs_swsa) { ++ move_v(kSimd128ScratchReg, lhs_swsa); ++ lhs_swsa = kSimd128ScratchReg; ++ } else if (dst_swsa == rhs_swsa) { ++ move_v(kSimd128ScratchReg, rhs_swsa); ++ rhs_swsa = kSimd128ScratchReg; ++ } ++ ++ li(kScratchReg, control_low); ++ insert_d(dst_swsa, 0, kScratchReg); ++ li(kScratchReg, control_hi); ++ insert_d(dst_swsa, 1, kScratchReg); ++ vshf_b(dst_swsa, rhs_swsa, lhs_swsa); ++#endif ++} ++ ++void LiftoffAssembler::emit_i8x16_swizzle(LiftoffRegister dst, ++ LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++#if 0 ++ SWSARegister dst_swsa = dst.fp().toW(); ++ SWSARegister lhs_swsa = lhs.fp().toW(); ++ SWSARegister rhs_swsa = rhs.fp().toW(); ++ ++ if (dst == lhs) { ++ move_v(kSimd128ScratchReg, lhs_swsa); ++ lhs_swsa = kSimd128ScratchReg; ++ } ++ xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero); ++ move_v(dst_swsa, rhs_swsa); ++ vshf_b(dst_swsa, kSimd128RegZero, lhs_swsa); ++#endif ++} ++ ++void LiftoffAssembler::emit_i8x16_splat(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++ // fill_b(dst.fp().toW(), src.gp()); ++} ++ ++void LiftoffAssembler::emit_i16x8_splat(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++ // fill_h(dst.fp().toW(), src.gp()); ++} ++ ++void LiftoffAssembler::emit_i32x4_splat(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++ // fill_w(dst.fp().toW(), src.gp()); ++} ++ ++void LiftoffAssembler::emit_i64x2_splat(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++ // fill_d(dst.fp().toW(), src.gp()); ++} ++ ++void LiftoffAssembler::emit_f32x4_splat(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++ // TurboAssembler::FmoveLow(kScratchReg, src.fp()); ++ // fill_w(dst.fp().toW(), kScratchReg); ++} ++ ++void LiftoffAssembler::emit_f64x2_splat(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++ // TurboAssembler::Move(kScratchReg, src.fp()); ++ // fill_d(dst.fp().toW(), kScratchReg); ++} ++ ++#define SIMD_BINOP(name1, name2, type) \ ++ void LiftoffAssembler::emit_##name1##_extmul_low_##name2( \ ++ LiftoffRegister dst, LiftoffRegister src1, LiftoffRegister src2) { \ ++ UNREACHABLE(); \ ++ } \ ++ void LiftoffAssembler::emit_##name1##_extmul_high_##name2( \ ++ LiftoffRegister dst, LiftoffRegister src1, LiftoffRegister src2) { \ ++ UNREACHABLE(); \ ++ } ++ ++SIMD_BINOP(i16x8, i8x16_s, SWSAS8) ++SIMD_BINOP(i16x8, i8x16_u, SWSAU8) ++ ++SIMD_BINOP(i32x4, i16x8_s, SWSAS16) ++SIMD_BINOP(i32x4, i16x8_u, SWSAU16) ++ ++SIMD_BINOP(i64x2, i32x4_s, SWSAS32) ++SIMD_BINOP(i64x2, i32x4_u, SWSAU32) ++ ++#undef SIMD_BINOP ++ ++#define SIMD_BINOP(name1, name2, type) \ ++ void LiftoffAssembler::emit_##name1##_extadd_pairwise_##name2( \ ++ LiftoffRegister dst, LiftoffRegister src) { \ ++ UNREACHABLE(); \ ++ } ++ ++SIMD_BINOP(i16x8, i8x16_s, SWSAS8) ++SIMD_BINOP(i16x8, i8x16_u, SWSAU8) ++SIMD_BINOP(i32x4, i16x8_s, SWSAS16) ++SIMD_BINOP(i32x4, i16x8_u, SWSAU16) ++#undef SIMD_BINOP ++ ++void LiftoffAssembler::emit_i16x8_q15mulr_sat_s(LiftoffRegister dst, ++ LiftoffRegister src1, ++ LiftoffRegister src2) { ++ UNREACHABLE(); ++ // mulr_q_h(dst.fp().toW(), src1.fp().toW(), src2.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i8x16_eq(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // ceq_b(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i8x16_ne(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // ceq_b(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++ // nor_v(dst.fp().toW(), dst.fp().toW(), dst.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i8x16_gt_s(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // clt_s_b(dst.fp().toW(), rhs.fp().toW(), lhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i8x16_gt_u(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // clt_u_b(dst.fp().toW(), rhs.fp().toW(), lhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i8x16_ge_s(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // cle_s_b(dst.fp().toW(), rhs.fp().toW(), lhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i8x16_ge_u(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // cle_u_b(dst.fp().toW(), rhs.fp().toW(), lhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i16x8_eq(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // ceq_h(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i16x8_ne(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // ceq_h(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++ // nor_v(dst.fp().toW(), dst.fp().toW(), dst.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i16x8_gt_s(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // clt_s_h(dst.fp().toW(), rhs.fp().toW(), lhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i16x8_gt_u(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // clt_u_h(dst.fp().toW(), rhs.fp().toW(), lhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i16x8_ge_s(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // cle_s_h(dst.fp().toW(), rhs.fp().toW(), lhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i16x8_ge_u(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // cle_u_h(dst.fp().toW(), rhs.fp().toW(), lhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i32x4_eq(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // ceq_w(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i32x4_ne(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // ceq_w(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++ // nor_v(dst.fp().toW(), dst.fp().toW(), dst.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i32x4_gt_s(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // clt_s_w(dst.fp().toW(), rhs.fp().toW(), lhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i32x4_gt_u(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // clt_u_w(dst.fp().toW(), rhs.fp().toW(), lhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i32x4_ge_s(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // cle_s_w(dst.fp().toW(), rhs.fp().toW(), lhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i32x4_ge_u(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // cle_u_w(dst.fp().toW(), rhs.fp().toW(), lhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_f32x4_eq(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // fceq_w(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_f32x4_ne(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // fcune_w(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_f32x4_lt(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // fclt_w(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_f32x4_le(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // fcle_w(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i64x2_eq(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // ceq_d(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i64x2_ne(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // ceq_d(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++ // nor_v(dst.fp().toW(), dst.fp().toW(), dst.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i64x2_abs(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++ // xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero); ++ // add_a_d(dst.fp().toW(), src.fp().toW(), kSimd128RegZero); ++} ++ ++void LiftoffAssembler::emit_f64x2_eq(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // fceq_d(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_f64x2_ne(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // fcune_d(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_f64x2_lt(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // fclt_d(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_f64x2_le(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // fcle_d(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_s128_const(LiftoffRegister dst, ++ const uint8_t imms[16]) { ++ UNREACHABLE(); ++#if 0 ++ SWSARegister dst_swsa = dst.fp().toW(); ++ uint64_t vals[2]; ++ memcpy(vals, imms, sizeof(vals)); ++ li(kScratchReg, vals[0]); ++ insert_d(dst_swsa, 0, kScratchReg); ++ li(kScratchReg, vals[1]); ++ insert_d(dst_swsa, 1, kScratchReg); ++#endif ++} ++ ++void LiftoffAssembler::emit_s128_not(LiftoffRegister dst, LiftoffRegister src) { ++ UNREACHABLE(); ++ // nor_v(dst.fp().toW(), src.fp().toW(), src.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_s128_and(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // and_v(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_s128_or(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // or_v(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_s128_xor(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // xor_v(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_s128_and_not(LiftoffRegister dst, ++ LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // nor_v(kSimd128ScratchReg, rhs.fp().toW(), rhs.fp().toW()); ++ // and_v(dst.fp().toW(), kSimd128ScratchReg, lhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_s128_select(LiftoffRegister dst, ++ LiftoffRegister src1, ++ LiftoffRegister src2, ++ LiftoffRegister mask) { ++ UNREACHABLE(); ++#if 0 ++ if (dst == mask) { ++ bsel_v(dst.fp().toW(), src2.fp().toW(), src1.fp().toW()); ++ } else { ++ xor_v(kSimd128ScratchReg, src1.fp().toW(), src2.fp().toW()); ++ and_v(kSimd128ScratchReg, kSimd128ScratchReg, mask.fp().toW()); ++ xor_v(dst.fp().toW(), kSimd128ScratchReg, src2.fp().toW()); ++ } ++#endif ++} ++ ++void LiftoffAssembler::emit_i8x16_neg(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++ // xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero); ++ // subv_b(dst.fp().toW(), kSimd128RegZero, src.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_v128_anytrue(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++ // liftoff::EmitAnyTrue(this, dst, src); ++} ++ ++void LiftoffAssembler::emit_i8x16_alltrue(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++ // liftoff::EmitAllTrue(this, dst, src, SWSA_BRANCH_B); ++} ++ ++void LiftoffAssembler::emit_i8x16_bitmask(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++#if 0 ++ SWSARegister scratch0 = kSimd128RegZero; ++ SWSARegister scratch1 = kSimd128ScratchReg; ++ srli_b(scratch0, src.fp().toW(), 7); ++ srli_h(scratch1, scratch0, 7); ++ or_v(scratch0, scratch0, scratch1); ++ srli_w(scratch1, scratch0, 14); ++ or_v(scratch0, scratch0, scratch1); ++ srli_d(scratch1, scratch0, 28); ++ or_v(scratch0, scratch0, scratch1); ++ shf_w(scratch1, scratch0, 0x0E); ++ ilvev_b(scratch0, scratch1, scratch0); ++ copy_u_h(dst.gp(), scratch0, 0); ++#endif ++} ++ ++void LiftoffAssembler::emit_i8x16_shl(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // fill_b(kSimd128ScratchReg, rhs.gp()); ++ // sll_b(dst.fp().toW(), lhs.fp().toW(), kSimd128ScratchReg); ++} ++ ++void LiftoffAssembler::emit_i8x16_shli(LiftoffRegister dst, LiftoffRegister lhs, ++ int32_t rhs) { ++ UNREACHABLE(); ++ // slli_b(dst.fp().toW(), lhs.fp().toW(), rhs & 7); ++} ++ ++void LiftoffAssembler::emit_i8x16_shr_s(LiftoffRegister dst, ++ LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // fill_b(kSimd128ScratchReg, rhs.gp()); ++ // sra_b(dst.fp().toW(), lhs.fp().toW(), kSimd128ScratchReg); ++} ++ ++void LiftoffAssembler::emit_i8x16_shri_s(LiftoffRegister dst, ++ LiftoffRegister lhs, int32_t rhs) { ++ UNREACHABLE(); ++ // srai_b(dst.fp().toW(), lhs.fp().toW(), rhs & 7); ++} ++ ++void LiftoffAssembler::emit_i8x16_shr_u(LiftoffRegister dst, ++ LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // fill_b(kSimd128ScratchReg, rhs.gp()); ++ // srl_b(dst.fp().toW(), lhs.fp().toW(), kSimd128ScratchReg); ++} ++ ++void LiftoffAssembler::emit_i8x16_shri_u(LiftoffRegister dst, ++ LiftoffRegister lhs, int32_t rhs) { ++ UNREACHABLE(); ++ // srli_b(dst.fp().toW(), lhs.fp().toW(), rhs & 7); ++} ++ ++void LiftoffAssembler::emit_i8x16_add(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // addv_b(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i8x16_add_sat_s(LiftoffRegister dst, ++ LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // adds_s_b(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i8x16_add_sat_u(LiftoffRegister dst, ++ LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // adds_u_b(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i8x16_sub(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // subv_b(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i8x16_sub_sat_s(LiftoffRegister dst, ++ LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // subs_s_b(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i8x16_sub_sat_u(LiftoffRegister dst, ++ LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // subs_u_b(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i8x16_min_s(LiftoffRegister dst, ++ LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // min_s_b(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i8x16_min_u(LiftoffRegister dst, ++ LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // min_u_b(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i8x16_max_s(LiftoffRegister dst, ++ LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // max_s_b(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i8x16_max_u(LiftoffRegister dst, ++ LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // max_u_b(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i8x16_popcnt(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++ // pcnt_b(dst.fp().toW(), src.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i16x8_neg(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++ // xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero); ++ // subv_h(dst.fp().toW(), kSimd128RegZero, src.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i16x8_alltrue(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++ // liftoff::EmitAllTrue(this, dst, src, SWSA_BRANCH_H); ++} ++ ++void LiftoffAssembler::emit_i16x8_bitmask(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++#if 0 ++ SWSARegister scratch0 = kSimd128RegZero; ++ SWSARegister scratch1 = kSimd128ScratchReg; ++ srli_h(scratch0, src.fp().toW(), 15); ++ srli_w(scratch1, scratch0, 15); ++ or_v(scratch0, scratch0, scratch1); ++ srli_d(scratch1, scratch0, 30); ++ or_v(scratch0, scratch0, scratch1); ++ shf_w(scratch1, scratch0, 0x0E); ++ slli_d(scratch1, scratch1, 4); ++ or_v(scratch0, scratch0, scratch1); ++ copy_u_b(dst.gp(), scratch0, 0); ++#endif ++} ++ ++void LiftoffAssembler::emit_i16x8_shl(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // fill_h(kSimd128ScratchReg, rhs.gp()); ++ // sll_h(dst.fp().toW(), lhs.fp().toW(), kSimd128ScratchReg); ++} ++ ++void LiftoffAssembler::emit_i16x8_shli(LiftoffRegister dst, LiftoffRegister lhs, ++ int32_t rhs) { ++ UNREACHABLE(); ++ // slli_h(dst.fp().toW(), lhs.fp().toW(), rhs & 15); ++} ++ ++void LiftoffAssembler::emit_i16x8_shr_s(LiftoffRegister dst, ++ LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // fill_h(kSimd128ScratchReg, rhs.gp()); ++ // sra_h(dst.fp().toW(), lhs.fp().toW(), kSimd128ScratchReg); ++} ++ ++void LiftoffAssembler::emit_i16x8_shri_s(LiftoffRegister dst, ++ LiftoffRegister lhs, int32_t rhs) { ++ UNREACHABLE(); ++ // srai_h(dst.fp().toW(), lhs.fp().toW(), rhs & 15); ++} ++ ++void LiftoffAssembler::emit_i16x8_shr_u(LiftoffRegister dst, ++ LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // fill_h(kSimd128ScratchReg, rhs.gp()); ++ // srl_h(dst.fp().toW(), lhs.fp().toW(), kSimd128ScratchReg); ++} ++ ++void LiftoffAssembler::emit_i16x8_shri_u(LiftoffRegister dst, ++ LiftoffRegister lhs, int32_t rhs) { ++ UNREACHABLE(); ++ // srli_h(dst.fp().toW(), lhs.fp().toW(), rhs & 15); ++} ++ ++void LiftoffAssembler::emit_i16x8_add(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // addv_h(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i16x8_add_sat_s(LiftoffRegister dst, ++ LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // adds_s_h(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i16x8_add_sat_u(LiftoffRegister dst, ++ LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // adds_u_h(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i16x8_sub(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // subv_h(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i16x8_sub_sat_s(LiftoffRegister dst, ++ LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // subs_s_h(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i16x8_sub_sat_u(LiftoffRegister dst, ++ LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // subs_u_h(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i16x8_mul(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // mulv_h(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i16x8_min_s(LiftoffRegister dst, ++ LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // min_s_h(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i16x8_min_u(LiftoffRegister dst, ++ LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // min_u_h(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i16x8_max_s(LiftoffRegister dst, ++ LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // max_s_h(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i16x8_max_u(LiftoffRegister dst, ++ LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // max_u_h(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i32x4_neg(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++ // xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero); ++ // subv_w(dst.fp().toW(), kSimd128RegZero, src.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i32x4_alltrue(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++ // liftoff::EmitAllTrue(this, dst, src, SWSA_BRANCH_W); ++} ++ ++void LiftoffAssembler::emit_i32x4_bitmask(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++#if 0 ++ SWSARegister scratch0 = kSimd128RegZero; ++ SWSARegister scratch1 = kSimd128ScratchReg; ++ srli_w(scratch0, src.fp().toW(), 31); ++ srli_d(scratch1, scratch0, 31); ++ or_v(scratch0, scratch0, scratch1); ++ shf_w(scratch1, scratch0, 0x0E); ++ slli_d(scratch1, scratch1, 2); ++ or_v(scratch0, scratch0, scratch1); ++ copy_u_b(dst.gp(), scratch0, 0); ++#endif ++} ++ ++void LiftoffAssembler::emit_i32x4_shl(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // fill_w(kSimd128ScratchReg, rhs.gp()); ++ // sll_w(dst.fp().toW(), lhs.fp().toW(), kSimd128ScratchReg); ++} ++ ++void LiftoffAssembler::emit_i32x4_shli(LiftoffRegister dst, LiftoffRegister lhs, ++ int32_t rhs) { ++ UNREACHABLE(); ++ // slli_w(dst.fp().toW(), lhs.fp().toW(), rhs & 31); ++} ++ ++void LiftoffAssembler::emit_i32x4_shr_s(LiftoffRegister dst, ++ LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // fill_w(kSimd128ScratchReg, rhs.gp()); ++ // sra_w(dst.fp().toW(), lhs.fp().toW(), kSimd128ScratchReg); ++} ++ ++void LiftoffAssembler::emit_i32x4_shri_s(LiftoffRegister dst, ++ LiftoffRegister lhs, int32_t rhs) { ++ UNREACHABLE(); ++ // srai_w(dst.fp().toW(), lhs.fp().toW(), rhs & 31); ++} ++ ++void LiftoffAssembler::emit_i32x4_shr_u(LiftoffRegister dst, ++ LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // fill_w(kSimd128ScratchReg, rhs.gp()); ++ // srl_w(dst.fp().toW(), lhs.fp().toW(), kSimd128ScratchReg); ++} ++ ++void LiftoffAssembler::emit_i32x4_shri_u(LiftoffRegister dst, ++ LiftoffRegister lhs, int32_t rhs) { ++ UNREACHABLE(); ++ // srli_w(dst.fp().toW(), lhs.fp().toW(), rhs & 31); ++} ++ ++void LiftoffAssembler::emit_i32x4_add(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // addv_w(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i32x4_sub(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // subv_w(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i32x4_mul(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // mulv_w(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i32x4_min_s(LiftoffRegister dst, ++ LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // min_s_w(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i32x4_min_u(LiftoffRegister dst, ++ LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // min_u_w(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i32x4_max_s(LiftoffRegister dst, ++ LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // max_s_w(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i32x4_max_u(LiftoffRegister dst, ++ LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // max_u_w(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i32x4_dot_i16x8_s(LiftoffRegister dst, ++ LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++} ++ ++void LiftoffAssembler::emit_i64x2_neg(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++ // xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero); ++ // subv_d(dst.fp().toW(), kSimd128RegZero, src.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i64x2_alltrue(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++ // liftoff::EmitAllTrue(this, dst, src, SWSA_BRANCH_D); ++} ++ ++void LiftoffAssembler::emit_i64x2_bitmask(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++ // srli_d(kSimd128RegZero, src.fp().toW(), 63); ++ // shf_w(kSimd128ScratchReg, kSimd128RegZero, 0x02); ++ // slli_d(kSimd128ScratchReg, kSimd128ScratchReg, 1); ++ // or_v(kSimd128RegZero, kSimd128RegZero, kSimd128ScratchReg); ++ // copy_u_b(dst.gp(), kSimd128RegZero, 0); ++} ++ ++void LiftoffAssembler::emit_i64x2_shl(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // fill_d(kSimd128ScratchReg, rhs.gp()); ++ // sll_d(dst.fp().toW(), lhs.fp().toW(), kSimd128ScratchReg); ++} ++ ++void LiftoffAssembler::emit_i64x2_shli(LiftoffRegister dst, LiftoffRegister lhs, ++ int32_t rhs) { ++ UNREACHABLE(); ++ // slli_d(dst.fp().toW(), lhs.fp().toW(), rhs & 63); ++} ++ ++void LiftoffAssembler::emit_i64x2_shr_s(LiftoffRegister dst, ++ LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // fill_d(kSimd128ScratchReg, rhs.gp()); ++ // sra_d(dst.fp().toW(), lhs.fp().toW(), kSimd128ScratchReg); ++} ++ ++void LiftoffAssembler::emit_i64x2_shri_s(LiftoffRegister dst, ++ LiftoffRegister lhs, int32_t rhs) { ++ UNREACHABLE(); ++ // srai_d(dst.fp().toW(), lhs.fp().toW(), rhs & 63); ++} ++ ++void LiftoffAssembler::emit_i64x2_shr_u(LiftoffRegister dst, ++ LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // fill_d(kSimd128ScratchReg, rhs.gp()); ++ // srl_d(dst.fp().toW(), lhs.fp().toW(), kSimd128ScratchReg); ++} ++ ++void LiftoffAssembler::emit_i64x2_shri_u(LiftoffRegister dst, ++ LiftoffRegister lhs, int32_t rhs) { ++ UNREACHABLE(); ++ // srli_d(dst.fp().toW(), lhs.fp().toW(), rhs & 63); ++} ++ ++void LiftoffAssembler::emit_i64x2_add(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // addv_d(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i64x2_sub(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // subv_d(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i64x2_mul(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // mulv_d(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i64x2_gt_s(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // clt_s_d(dst.fp().toW(), rhs.fp().toW(), lhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i64x2_ge_s(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // cle_s_d(dst.fp().toW(), rhs.fp().toW(), lhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_f32x4_abs(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++ // bclri_w(dst.fp().toW(), src.fp().toW(), 31); ++} ++ ++void LiftoffAssembler::emit_f32x4_neg(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++ // bnegi_w(dst.fp().toW(), src.fp().toW(), 31); ++} ++ ++void LiftoffAssembler::emit_f32x4_sqrt(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++} ++ ++bool LiftoffAssembler::emit_f32x4_ceil(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++} ++ ++bool LiftoffAssembler::emit_f32x4_floor(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++} ++ ++bool LiftoffAssembler::emit_f32x4_trunc(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++} ++ ++bool LiftoffAssembler::emit_f32x4_nearest_int(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++} ++ ++void LiftoffAssembler::emit_f32x4_add(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // fadd_w(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_f32x4_sub(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // fsub_w(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_f32x4_mul(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // fmul_w(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_f32x4_div(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // fdiv_w(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_f32x4_min(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++#if 0 ++ SWSARegister dst_swsa = dst.fp().toW(); ++ SWSARegister lhs_swsa = lhs.fp().toW(); ++ SWSARegister rhs_swsa = rhs.fp().toW(); ++ SWSARegister scratch0 = kSimd128RegZero; ++ SWSARegister scratch1 = kSimd128ScratchReg; ++ // If inputs are -0.0. and +0.0, then write -0.0 to scratch1. ++ // scratch1 = (lhs == rhs) ? (lhs | rhs) : (rhs | rhs). ++ fseq_w(scratch0, lhs_swsa, rhs_swsa); ++ bsel_v(scratch0, rhs_swsa, lhs_swsa); ++ or_v(scratch1, scratch0, rhs_swsa); ++ // scratch0 = isNaN(scratch1) ? scratch1: lhs. ++ fseq_w(scratch0, scratch1, scratch1); ++ bsel_v(scratch0, scratch1, lhs_swsa); ++ // dst = (scratch1 <= scratch0) ? scratch1 : scratch0. ++ fsle_w(dst_swsa, scratch1, scratch0); ++ bsel_v(dst_swsa, scratch0, scratch1); ++#endif ++} ++ ++void LiftoffAssembler::emit_f32x4_max(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++#if 0 ++ SWSARegister dst_swsa = dst.fp().toW(); ++ SWSARegister lhs_swsa = lhs.fp().toW(); ++ SWSARegister rhs_swsa = rhs.fp().toW(); ++ SWSARegister scratch0 = kSimd128RegZero; ++ SWSARegister scratch1 = kSimd128ScratchReg; ++ // If inputs are -0.0. and +0.0, then write +0.0 to scratch1. ++ // scratch1 = (lhs == rhs) ? (lhs | rhs) : (rhs | rhs). ++ fseq_w(scratch0, lhs_swsa, rhs_swsa); ++ bsel_v(scratch0, rhs_swsa, lhs_swsa); ++ and_v(scratch1, scratch0, rhs_swsa); ++ // scratch0 = isNaN(scratch1) ? scratch1: lhs. ++ fseq_w(scratch0, scratch1, scratch1); ++ bsel_v(scratch0, scratch1, lhs_swsa); ++ // dst = (scratch0 <= scratch1) ? scratch1 : scratch0. ++ fsle_w(dst_swsa, scratch0, scratch1); ++ bsel_v(dst_swsa, scratch0, scratch1); ++#endif ++} ++ ++void LiftoffAssembler::emit_f32x4_pmin(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++} ++ ++void LiftoffAssembler::emit_f32x4_pmax(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++} ++ ++void LiftoffAssembler::emit_f64x2_abs(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++ // bclri_d(dst.fp().toW(), src.fp().toW(), 63); ++} ++ ++void LiftoffAssembler::emit_f64x2_neg(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++ // bnegi_d(dst.fp().toW(), src.fp().toW(), 63); ++} ++ ++void LiftoffAssembler::emit_f64x2_sqrt(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++} ++ ++bool LiftoffAssembler::emit_f64x2_ceil(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++} ++ ++bool LiftoffAssembler::emit_f64x2_floor(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++} ++ ++bool LiftoffAssembler::emit_f64x2_trunc(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++} ++ ++bool LiftoffAssembler::emit_f64x2_nearest_int(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++} ++ ++void LiftoffAssembler::emit_f64x2_add(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // fadd_d(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_f64x2_sub(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // fsub_d(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_f64x2_mul(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // fmul_d(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_f64x2_div(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // fdiv_d(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_f64x2_min(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++#if 0 ++ SWSARegister dst_swsa = dst.fp().toW(); ++ SWSARegister lhs_swsa = lhs.fp().toW(); ++ SWSARegister rhs_swsa = rhs.fp().toW(); ++ SWSARegister scratch0 = kSimd128RegZero; ++ SWSARegister scratch1 = kSimd128ScratchReg; ++ // If inputs are -0.0. and +0.0, then write -0.0 to scratch1. ++ // scratch1 = (lhs == rhs) ? (lhs | rhs) : (rhs | rhs). ++ fseq_d(scratch0, lhs_swsa, rhs_swsa); ++ bsel_v(scratch0, rhs_swsa, lhs_swsa); ++ or_v(scratch1, scratch0, rhs_swsa); ++ // scratch0 = isNaN(scratch1) ? scratch1: lhs. ++ fseq_d(scratch0, scratch1, scratch1); ++ bsel_v(scratch0, scratch1, lhs_swsa); ++ // dst = (scratch1 <= scratch0) ? scratch1 : scratch0. ++ fsle_d(dst_swsa, scratch1, scratch0); ++ bsel_v(dst_swsa, scratch0, scratch1); ++#endif ++} ++ ++void LiftoffAssembler::emit_f64x2_max(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++#if 0 ++ SWSARegister dst_swsa = dst.fp().toW(); ++ SWSARegister lhs_swsa = lhs.fp().toW(); ++ SWSARegister rhs_swsa = rhs.fp().toW(); ++ SWSARegister scratch0 = kSimd128RegZero; ++ SWSARegister scratch1 = kSimd128ScratchReg; ++ // If inputs are -0.0. and +0.0, then write +0.0 to scratch1. ++ // scratch1 = (lhs == rhs) ? (lhs | rhs) : (rhs | rhs). ++ fseq_d(scratch0, lhs_swsa, rhs_swsa); ++ bsel_v(scratch0, rhs_swsa, lhs_swsa); ++ and_v(scratch1, scratch0, rhs_swsa); ++ // scratch0 = isNaN(scratch1) ? scratch1: lhs. ++ fseq_d(scratch0, scratch1, scratch1); ++ bsel_v(scratch0, scratch1, lhs_swsa); ++ // dst = (scratch0 <= scratch1) ? scratch1 : scratch0. ++ fsle_d(dst_swsa, scratch0, scratch1); ++ bsel_v(dst_swsa, scratch0, scratch1); ++ // Canonicalize the result. ++ fmax_d(dst_swsa, dst_swsa, dst_swsa); ++#endif ++} ++ ++void LiftoffAssembler::emit_f64x2_pmin(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++} ++ ++void LiftoffAssembler::emit_f64x2_pmax(LiftoffRegister dst, LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++} ++ ++void LiftoffAssembler::emit_f64x2_convert_low_i32x4_s(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++} ++ ++void LiftoffAssembler::emit_f64x2_convert_low_i32x4_u(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++} ++ ++void LiftoffAssembler::emit_f64x2_promote_low_f32x4(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++} ++ ++void LiftoffAssembler::emit_i32x4_sconvert_f32x4(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++ // ftrunc_s_w(dst.fp().toW(), src.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i32x4_uconvert_f32x4(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++ // ftrunc_u_w(dst.fp().toW(), src.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i32x4_trunc_sat_f64x2_s_zero(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++} ++ ++void LiftoffAssembler::emit_i32x4_trunc_sat_f64x2_u_zero(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++} ++ ++void LiftoffAssembler::emit_f32x4_sconvert_i32x4(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++ // ffint_s_w(dst.fp().toW(), src.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_f32x4_uconvert_i32x4(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++ // ffint_u_w(dst.fp().toW(), src.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_f32x4_demote_f64x2_zero(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++} ++ ++void LiftoffAssembler::emit_i8x16_sconvert_i16x8(LiftoffRegister dst, ++ LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // sat_s_h(kSimd128ScratchReg, lhs.fp().toW(), 7); ++ // sat_s_h(dst.fp().toW(), lhs.fp().toW(), 7); ++ // pckev_b(dst.fp().toW(), dst.fp().toW(), kSimd128ScratchReg); ++} ++ ++void LiftoffAssembler::emit_i8x16_uconvert_i16x8(LiftoffRegister dst, ++ LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++#if 0 ++ xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero); ++ max_s_h(kSimd128ScratchReg, kSimd128RegZero, lhs.fp().toW()); ++ sat_u_h(kSimd128ScratchReg, kSimd128ScratchReg, 7); ++ max_s_h(dst.fp().toW(), kSimd128RegZero, rhs.fp().toW()); ++ sat_u_h(dst.fp().toW(), dst.fp().toW(), 7); ++ pckev_b(dst.fp().toW(), dst.fp().toW(), kSimd128ScratchReg); ++#endif ++} ++ ++void LiftoffAssembler::emit_i16x8_sconvert_i32x4(LiftoffRegister dst, ++ LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // sat_s_w(kSimd128ScratchReg, lhs.fp().toW(), 15); ++ // sat_s_w(dst.fp().toW(), lhs.fp().toW(), 15); ++ // pckev_h(dst.fp().toW(), dst.fp().toW(), kSimd128ScratchReg); ++} ++ ++void LiftoffAssembler::emit_i16x8_uconvert_i32x4(LiftoffRegister dst, ++ LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++#if 0 ++ xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero); ++ max_s_w(kSimd128ScratchReg, kSimd128RegZero, lhs.fp().toW()); ++ sat_u_w(kSimd128ScratchReg, kSimd128ScratchReg, 15); ++ max_s_w(dst.fp().toW(), kSimd128RegZero, rhs.fp().toW()); ++ sat_u_w(dst.fp().toW(), dst.fp().toW(), 15); ++ pckev_h(dst.fp().toW(), dst.fp().toW(), kSimd128ScratchReg); ++#endif ++} ++ ++void LiftoffAssembler::emit_i16x8_sconvert_i8x16_low(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++ // ilvr_b(kSimd128ScratchReg, src.fp().toW(), src.fp().toW()); ++ // slli_h(dst.fp().toW(), kSimd128ScratchReg, 8); ++ // srai_h(dst.fp().toW(), dst.fp().toW(), 8); ++} ++ ++void LiftoffAssembler::emit_i16x8_sconvert_i8x16_high(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++ // ilvl_b(kSimd128ScratchReg, src.fp().toW(), src.fp().toW()); ++ // slli_h(dst.fp().toW(), kSimd128ScratchReg, 8); ++ // srai_h(dst.fp().toW(), dst.fp().toW(), 8); ++} ++ ++void LiftoffAssembler::emit_i16x8_uconvert_i8x16_low(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++ // xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero); ++ // ilvr_b(dst.fp().toW(), kSimd128RegZero, src.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i16x8_uconvert_i8x16_high(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++ // xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero); ++ // ilvl_b(dst.fp().toW(), kSimd128RegZero, src.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i32x4_sconvert_i16x8_low(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++ // ilvr_h(kSimd128ScratchReg, src.fp().toW(), src.fp().toW()); ++ // slli_w(dst.fp().toW(), kSimd128ScratchReg, 16); ++ // srai_w(dst.fp().toW(), dst.fp().toW(), 16); ++} ++ ++void LiftoffAssembler::emit_i32x4_sconvert_i16x8_high(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++ // ilvl_h(kSimd128ScratchReg, src.fp().toW(), src.fp().toW()); ++ // slli_w(dst.fp().toW(), kSimd128ScratchReg, 16); ++ // srai_w(dst.fp().toW(), dst.fp().toW(), 16); ++} ++ ++void LiftoffAssembler::emit_i32x4_uconvert_i16x8_low(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++ // xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero); ++ // ilvr_h(dst.fp().toW(), kSimd128RegZero, src.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i32x4_uconvert_i16x8_high(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++ // xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero); ++ // ilvl_h(dst.fp().toW(), kSimd128RegZero, src.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i64x2_sconvert_i32x4_low(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++ // ilvr_w(kSimd128ScratchReg, src.fp().toW(), src.fp().toW()); ++ // slli_d(dst.fp().toW(), kSimd128ScratchReg, 32); ++ // srai_d(dst.fp().toW(), dst.fp().toW(), 32); ++} ++ ++void LiftoffAssembler::emit_i64x2_sconvert_i32x4_high(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++ // ilvl_w(kSimd128ScratchReg, src.fp().toW(), src.fp().toW()); ++ // slli_d(dst.fp().toW(), kSimd128ScratchReg, 32); ++ // srai_d(dst.fp().toW(), dst.fp().toW(), 32); ++} ++ ++void LiftoffAssembler::emit_i64x2_uconvert_i32x4_low(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++ // xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero); ++ // ilvr_w(dst.fp().toW(), kSimd128RegZero, src.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i64x2_uconvert_i32x4_high(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++ // xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero); ++ // ilvl_w(dst.fp().toW(), kSimd128RegZero, src.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i8x16_rounding_average_u(LiftoffRegister dst, ++ LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // aver_u_b(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i16x8_rounding_average_u(LiftoffRegister dst, ++ LiftoffRegister lhs, ++ LiftoffRegister rhs) { ++ UNREACHABLE(); ++ // aver_u_h(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); ++} ++ ++void LiftoffAssembler::emit_i8x16_abs(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++ // xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero); ++ // asub_s_b(dst.fp().toW(), src.fp().toW(), kSimd128RegZero); ++} ++ ++void LiftoffAssembler::emit_i16x8_abs(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++ // xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero); ++ // asub_s_h(dst.fp().toW(), src.fp().toW(), kSimd128RegZero); ++} ++ ++void LiftoffAssembler::emit_i32x4_abs(LiftoffRegister dst, ++ LiftoffRegister src) { ++ UNREACHABLE(); ++ // xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero); ++ // asub_s_w(dst.fp().toW(), src.fp().toW(), kSimd128RegZero); ++} ++ ++void LiftoffAssembler::emit_i8x16_extract_lane_s(LiftoffRegister dst, ++ LiftoffRegister lhs, ++ uint8_t imm_lane_idx) { ++ UNREACHABLE(); ++ // copy_s_b(dst.gp(), lhs.fp().toW(), imm_lane_idx); ++} ++ ++void LiftoffAssembler::emit_i8x16_extract_lane_u(LiftoffRegister dst, ++ LiftoffRegister lhs, ++ uint8_t imm_lane_idx) { ++ UNREACHABLE(); ++ // copy_u_b(dst.gp(), lhs.fp().toW(), imm_lane_idx); ++} ++ ++void LiftoffAssembler::emit_i16x8_extract_lane_s(LiftoffRegister dst, ++ LiftoffRegister lhs, ++ uint8_t imm_lane_idx) { ++ UNREACHABLE(); ++ // copy_s_h(dst.gp(), lhs.fp().toW(), imm_lane_idx); ++} ++ ++void LiftoffAssembler::emit_i16x8_extract_lane_u(LiftoffRegister dst, ++ LiftoffRegister lhs, ++ uint8_t imm_lane_idx) { ++ UNREACHABLE(); ++ // copy_u_h(dst.gp(), lhs.fp().toW(), imm_lane_idx); ++} ++ ++void LiftoffAssembler::emit_i32x4_extract_lane(LiftoffRegister dst, ++ LiftoffRegister lhs, ++ uint8_t imm_lane_idx) { ++ UNREACHABLE(); ++ // copy_s_w(dst.gp(), lhs.fp().toW(), imm_lane_idx); ++} ++ ++void LiftoffAssembler::emit_i64x2_extract_lane(LiftoffRegister dst, ++ LiftoffRegister lhs, ++ uint8_t imm_lane_idx) { ++ UNREACHABLE(); ++ // copy_s_d(dst.gp(), lhs.fp().toW(), imm_lane_idx); ++} ++ ++void LiftoffAssembler::emit_f32x4_extract_lane(LiftoffRegister dst, ++ LiftoffRegister lhs, ++ uint8_t imm_lane_idx) { ++ UNREACHABLE(); ++ // copy_u_w(kScratchReg, lhs.fp().toW(), imm_lane_idx); ++ // TurboAssembler::FmoveLow(dst.fp(), kScratchReg); ++} ++ ++void LiftoffAssembler::emit_f64x2_extract_lane(LiftoffRegister dst, ++ LiftoffRegister lhs, ++ uint8_t imm_lane_idx) { ++ UNREACHABLE(); ++ // copy_s_d(kScratchReg, lhs.fp().toW(), imm_lane_idx); ++ // TurboAssembler::Move(dst.fp(), kScratchReg); ++} ++ ++void LiftoffAssembler::emit_i8x16_replace_lane(LiftoffRegister dst, ++ LiftoffRegister src1, ++ LiftoffRegister src2, ++ uint8_t imm_lane_idx) { ++ UNREACHABLE(); ++#if 0 ++ if (dst != src1) { ++ move_v(dst.fp().toW(), src1.fp().toW()); ++ } ++ insert_b(dst.fp().toW(), imm_lane_idx, src2.gp()); ++#endif ++} ++ ++void LiftoffAssembler::emit_i16x8_replace_lane(LiftoffRegister dst, ++ LiftoffRegister src1, ++ LiftoffRegister src2, ++ uint8_t imm_lane_idx) { ++ UNREACHABLE(); ++#if 0 ++ if (dst != src1) { ++ move_v(dst.fp().toW(), src1.fp().toW()); ++ } ++ insert_h(dst.fp().toW(), imm_lane_idx, src2.gp()); ++#endif ++} ++ ++void LiftoffAssembler::emit_i32x4_replace_lane(LiftoffRegister dst, ++ LiftoffRegister src1, ++ LiftoffRegister src2, ++ uint8_t imm_lane_idx) { ++ UNREACHABLE(); ++#if 0 ++ if (dst != src1) { ++ move_v(dst.fp().toW(), src1.fp().toW()); ++ } ++ insert_w(dst.fp().toW(), imm_lane_idx, src2.gp()); ++#endif ++} ++ ++void LiftoffAssembler::emit_i64x2_replace_lane(LiftoffRegister dst, ++ LiftoffRegister src1, ++ LiftoffRegister src2, ++ uint8_t imm_lane_idx) { ++ UNREACHABLE(); ++#if 0 ++ if (dst != src1) { ++ move_v(dst.fp().toW(), src1.fp().toW()); ++ } ++ insert_d(dst.fp().toW(), imm_lane_idx, src2.gp()); ++#endif ++} ++ ++void LiftoffAssembler::emit_f32x4_replace_lane(LiftoffRegister dst, ++ LiftoffRegister src1, ++ LiftoffRegister src2, ++ uint8_t imm_lane_idx) { ++ UNREACHABLE(); ++#if 0 ++ TurboAssembler::FmoveLow(kScratchReg, src2.fp()); ++ if (dst != src1) { ++ move_v(dst.fp().toW(), src1.fp().toW()); ++ } ++ insert_w(dst.fp().toW(), imm_lane_idx, kScratchReg); ++#endif ++} ++ ++void LiftoffAssembler::emit_f64x2_replace_lane(LiftoffRegister dst, ++ LiftoffRegister src1, ++ LiftoffRegister src2, ++ uint8_t imm_lane_idx) { ++ UNREACHABLE(); ++#if 0 ++ TurboAssembler::Move(kScratchReg, src2.fp()); ++ if (dst != src1) { ++ move_v(dst.fp().toW(), src1.fp().toW()); ++ } ++ insert_d(dst.fp().toW(), imm_lane_idx, kScratchReg); ++#endif ++} ++ ++void LiftoffAssembler::StackCheck(Label* ool_code, Register limit_address) { ++ TurboAssembler::Ldl_u(limit_address, MemOperand(limit_address)); ++ TurboAssembler::Branch(ool_code, ule, sp, Operand(limit_address)); ++} ++ ++void LiftoffAssembler::CallTrapCallbackForTesting() { ++ PrepareCallCFunction(0, GetUnusedRegister(kGpReg, {}).gp()); ++ CallCFunction(ExternalReference::wasm_call_trap_callback_for_testing(), 0); ++} ++ ++void LiftoffAssembler::AssertUnreachable(AbortReason reason) { ++ if (FLAG_debug_code) Abort(reason); ++} ++ ++void LiftoffAssembler::PushRegisters(LiftoffRegList regs) { ++ LiftoffRegList gp_regs = regs & kGpCacheRegList; ++ unsigned num_gp_regs = gp_regs.GetNumRegsSet(); ++ if (num_gp_regs) { ++ unsigned offset = num_gp_regs * kSystemPointerSize; ++ subl(sp, offset, sp); ++ while (!gp_regs.is_empty()) { ++ LiftoffRegister reg = gp_regs.GetFirstRegSet(); ++ offset -= kSystemPointerSize; ++ Stl(reg.gp(), MemOperand(sp, offset)); ++ gp_regs.clear(reg); ++ } ++ DCHECK_EQ(offset, 0); ++ } ++ LiftoffRegList fp_regs = regs & kFpCacheRegList; ++ unsigned num_fp_regs = fp_regs.GetNumRegsSet(); ++ if (num_fp_regs) { ++ unsigned slot_size = IsEnabled(SW64_SIMD) ? 16 : 8; ++ subl(sp, (num_fp_regs * slot_size), sp); ++ unsigned offset = 0; ++ while (!fp_regs.is_empty()) { ++ LiftoffRegister reg = fp_regs.GetFirstRegSet(); ++ TurboAssembler::Fstd(reg.fp(), MemOperand(sp, offset)); ++ fp_regs.clear(reg); ++ offset += slot_size; ++ } ++ DCHECK_EQ(offset, num_fp_regs * slot_size); ++ } ++} ++ ++void LiftoffAssembler::PopRegisters(LiftoffRegList regs) { ++ LiftoffRegList fp_regs = regs & kFpCacheRegList; ++ unsigned fp_offset = 0; ++ while (!fp_regs.is_empty()) { ++ LiftoffRegister reg = fp_regs.GetFirstRegSet(); ++ TurboAssembler::Fldd(reg.fp(), MemOperand(sp, fp_offset)); ++ fp_regs.clear(reg); ++ fp_offset += sizeof(double); ++ } ++ if (fp_offset) addl(sp, fp_offset, sp); ++ LiftoffRegList gp_regs = regs & kGpCacheRegList; ++ unsigned gp_offset = 0; ++ while (!gp_regs.is_empty()) { ++ LiftoffRegister reg = gp_regs.GetLastRegSet(); ++ Ldl(reg.gp(), MemOperand(sp, gp_offset)); ++ gp_regs.clear(reg); ++ gp_offset += kSystemPointerSize; ++ } ++ addl(sp, gp_offset, sp); ++} ++ ++void LiftoffAssembler::RecordSpillsInSafepoint( ++ SafepointTableBuilder::Safepoint& safepoint, LiftoffRegList all_spills, ++ LiftoffRegList ref_spills, int spill_offset) { ++ int spill_space_size = 0; ++ while (!all_spills.is_empty()) { ++ LiftoffRegister reg = all_spills.GetFirstRegSet(); ++ if (ref_spills.has(reg)) { ++ safepoint.DefineTaggedStackSlot(spill_offset); ++ } ++ all_spills.clear(reg); ++ ++spill_offset; ++ spill_space_size += kSystemPointerSize; ++ } ++ // Record the number of additional spill slots. ++ RecordOolSpillSpaceSize(spill_space_size); ++} ++ ++void LiftoffAssembler::DropStackSlotsAndRet(uint32_t num_stack_slots) { ++ DCHECK_LT(num_stack_slots, ++ (1 << 16) / kSystemPointerSize); // 16 bit immediate ++ TurboAssembler::DropAndRet(static_cast(num_stack_slots)); ++} ++ ++void LiftoffAssembler::CallC(const ValueKindSig* sig, ++ const LiftoffRegister* args, ++ const LiftoffRegister* rets, ++ ValueKind out_argument_kind, int stack_bytes, ++ ExternalReference ext_ref) { ++ Addl(sp, sp, -stack_bytes); ++ ++ int arg_bytes = 0; ++ for (ValueKind param_kind : sig->parameters()) { ++ liftoff::Store(this, sp, arg_bytes, *args++, param_kind); ++ arg_bytes += value_kind_size(param_kind); ++ } ++ DCHECK_LE(arg_bytes, stack_bytes); ++ ++ // Pass a pointer to the buffer with the arguments to the C function. ++ // On sw64, the first argument is passed in {a0}. ++ constexpr Register kFirstArgReg = a0; ++ mov(kFirstArgReg, sp); ++ ++ // Now call the C function. ++ constexpr int kNumCCallArgs = 1; ++ PrepareCallCFunction(kNumCCallArgs, kScratchReg); ++ CallCFunction(ext_ref, kNumCCallArgs); ++ ++ // Move return value to the right register. ++ const LiftoffRegister* next_result_reg = rets; ++ if (sig->return_count() > 0) { ++ DCHECK_EQ(1, sig->return_count()); ++ constexpr Register kReturnReg = v0; ++ if (kReturnReg != next_result_reg->gp()) { ++ Move(*next_result_reg, LiftoffRegister(kReturnReg), sig->GetReturn(0)); ++ } ++ ++next_result_reg; ++ } ++ ++ // Load potential output value from the buffer on the stack. ++ if (out_argument_kind != kVoid) { ++ liftoff::Load(this, *next_result_reg, MemOperand(sp, 0), out_argument_kind); ++ } ++ ++ Addl(sp, sp, stack_bytes); ++} ++ ++void LiftoffAssembler::CallNativeWasmCode(Address addr) { ++ Call(addr, RelocInfo::WASM_CALL); ++} ++ ++void LiftoffAssembler::TailCallNativeWasmCode(Address addr) { ++ Jump(addr, RelocInfo::WASM_CALL); ++} ++ ++void LiftoffAssembler::CallIndirect(const ValueKindSig* sig, ++ compiler::CallDescriptor* call_descriptor, ++ Register target) { ++ if (target == no_reg) { ++ pop(kScratchReg); ++ Call(kScratchReg); ++ } else { ++ Call(target); ++ } ++} ++ ++void LiftoffAssembler::TailCallIndirect(Register target) { ++ if (target == no_reg) { ++ Pop(kScratchReg); ++ Jump(kScratchReg); ++ } else { ++ Jump(target); ++ } ++} ++ ++void LiftoffAssembler::CallRuntimeStub(WasmCode::RuntimeStubId sid) { ++ // A direct call to a wasm runtime stub defined in this module. ++ // Just encode the stub index. This will be patched at relocation. ++ Call(static_cast
(sid), RelocInfo::WASM_STUB_CALL); ++} ++ ++void LiftoffAssembler::AllocateStackSlot(Register addr, uint32_t size) { ++ Addl(sp, sp, -size); ++ TurboAssembler::Move(addr, sp); ++} ++ ++void LiftoffAssembler::DeallocateStackSlot(uint32_t size) { ++ Addl(sp, sp, size); ++} ++ ++void LiftoffAssembler::MaybeOSR() {} ++ ++void LiftoffAssembler::emit_set_if_nan(Register dst, FPURegister src, ++ ValueKind kind) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ Label not_nan; ++ if (kind == kF32) { ++ CompareIsNanF(src, src); ++ } else { ++ DCHECK_EQ(kind, kF64); ++ CompareIsNanF(src, src); ++ } ++ li(scratch, 1); ++ BranchFalseShortF(¬_nan); ++ Stw(dst, MemOperand(dst)); ++ bind(¬_nan); ++} ++ ++void LiftoffAssembler::emit_s128_set_if_nan(Register dst, LiftoffRegister src, ++ Register tmp_gp, ++ LiftoffRegister tmp_s128, ++ ValueKind lane_kind) { ++ UNIMPLEMENTED(); ++} ++ ++void LiftoffStackSlots::Construct(int param_slots) { ++ DCHECK_LT(0, slots_.size()); ++ SortInPushOrder(); ++ int last_stack_slot = param_slots; ++ for (auto& slot : slots_) { ++ const int stack_slot = slot.dst_slot_; ++ int stack_decrement = (last_stack_slot - stack_slot) * kSystemPointerSize; ++ DCHECK_LT(0, stack_decrement); ++ last_stack_slot = stack_slot; ++ const LiftoffAssembler::VarState& src = slot.src_; ++ switch (src.loc()) { ++ case LiftoffAssembler::VarState::kStack: ++ if (src.kind() != kS128) { ++ asm_->AllocateStackSpace(stack_decrement - kSystemPointerSize); ++ asm_->Ldl(kScratchReg, liftoff::GetStackSlot(slot.src_offset_)); ++ asm_->Push(kScratchReg); ++ } else { ++ asm_->AllocateStackSpace(stack_decrement - kSimd128Size); ++ asm_->Ldl(kScratchReg, liftoff::GetStackSlot(slot.src_offset_ - 8)); ++ asm_->Push(kScratchReg); ++ asm_->Ldl(kScratchReg, liftoff::GetStackSlot(slot.src_offset_)); ++ asm_->Push(kScratchReg); ++ } ++ break; ++ case LiftoffAssembler::VarState::kRegister: { ++ int pushed_bytes = SlotSizeInBytes(slot); ++ asm_->AllocateStackSpace(stack_decrement - pushed_bytes); ++ liftoff::push(asm_, src.reg(), src.kind()); ++ break; ++ } ++ case LiftoffAssembler::VarState::kIntConst: { ++ asm_->AllocateStackSpace(stack_decrement - kSystemPointerSize); ++ asm_->li(kScratchReg, Operand(src.i32_const())); ++ asm_->Push(kScratchReg); ++ break; ++ } ++ } ++ } ++} ++ ++} // namespace wasm ++} // namespace internal ++} // namespace v8 ++ ++#endif // V8_WASM_BASELINE_SW64_LIFTOFF_ASSEMBLER_SW64_H_ +diff --git a/deps/v8/src/wasm/jump-table-assembler.cc b/deps/v8/src/wasm/jump-table-assembler.cc +index ef79c55f..8bd3934f 100644 +--- a/deps/v8/src/wasm/jump-table-assembler.cc ++++ b/deps/v8/src/wasm/jump-table-assembler.cc +@@ -312,6 +312,41 @@ void JumpTableAssembler::NopBytes(int bytes) { + } + } + ++#elif V8_TARGET_ARCH_SW64 ++void JumpTableAssembler::EmitLazyCompileJumpSlot(uint32_t func_index, ++ Address lazy_compile_target) { ++ int start = pc_offset(); ++ li(kWasmCompileLazyFuncIndexRegister, func_index); // max. 2 instr ++ // Jump produces max. 4 instructions for 32-bit platform ++ // and max. 6 instructions for 64-bit platform. ++ Jump(lazy_compile_target, RelocInfo::NO_INFO); ++ int nop_bytes = start + kLazyCompileTableSlotSize - pc_offset(); ++ DCHECK_EQ(nop_bytes % kInstrSize, 0); ++ for (int i = 0; i < nop_bytes; i += kInstrSize) nop(); ++} ++ ++bool JumpTableAssembler::EmitJumpSlot(Address target) { ++ PatchAndJump(target); ++ return true; ++} ++ ++void JumpTableAssembler::EmitFarJumpSlot(Address target) { ++ JumpToOffHeapInstructionStream(target); ++} ++ ++// static ++void JumpTableAssembler::PatchFarJumpSlot(Address slot, Address target) { ++ UNREACHABLE(); ++} ++ ++void JumpTableAssembler::NopBytes(int bytes) { ++ DCHECK_LE(0, bytes); ++ DCHECK_EQ(0, bytes % kInstrSize); ++ for (; bytes > 0; bytes -= kInstrSize) { ++ nop(); ++ } ++} ++ + #elif V8_TARGET_ARCH_PPC64 + void JumpTableAssembler::EmitLazyCompileJumpSlot(uint32_t func_index, + Address lazy_compile_target) { +diff --git a/deps/v8/src/wasm/jump-table-assembler.h b/deps/v8/src/wasm/jump-table-assembler.h +index f2accac5..aa48ab7f 100644 +--- a/deps/v8/src/wasm/jump-table-assembler.h ++++ b/deps/v8/src/wasm/jump-table-assembler.h +@@ -229,6 +229,11 @@ class V8_EXPORT_PRIVATE JumpTableAssembler : public MacroAssembler { + static constexpr int kJumpTableSlotSize = 8 * kInstrSize; + static constexpr int kFarJumpTableSlotSize = 4 * kInstrSize; + static constexpr int kLazyCompileTableSlotSize = 8 * kInstrSize; ++#elif V8_TARGET_ARCH_SW64 ++ static constexpr int kJumpTableLineSize = 10 * kInstrSize; ++ static constexpr int kJumpTableSlotSize = 10 * kInstrSize; ++ static constexpr int kFarJumpTableSlotSize = 5 * kInstrSize; ++ static constexpr int kLazyCompileTableSlotSize = 10 * kInstrSize; + #else + #error Unknown architecture. + #endif +diff --git a/deps/v8/src/wasm/wasm-debug.cc b/deps/v8/src/wasm/wasm-debug.cc +index 4750d9fa..ba8785c7 100644 +--- a/deps/v8/src/wasm/wasm-debug.cc ++++ b/deps/v8/src/wasm/wasm-debug.cc +@@ -631,7 +631,11 @@ class DebugInfoImpl { + debug_break_fp + + WasmDebugBreakFrameConstants::GetPushedFpRegisterOffset(code); + if (value->type == kWasmF32) { ++#ifdef V8_TARGET_ARCH_SW64 ++ return WasmValue((float)(ReadUnalignedValue(spilled_addr))); ++#else + return WasmValue(ReadUnalignedValue(spilled_addr)); ++#endif + } else if (value->type == kWasmF64) { + return WasmValue(ReadUnalignedValue(spilled_addr)); + } else if (value->type == kWasmS128) { +diff --git a/deps/v8/src/wasm/wasm-linkage.h b/deps/v8/src/wasm/wasm-linkage.h +index 88115462..c54606c5 100644 +--- a/deps/v8/src/wasm/wasm-linkage.h ++++ b/deps/v8/src/wasm/wasm-linkage.h +@@ -89,6 +89,15 @@ constexpr Register kGpReturnRegisters[] = {a0, a1}; + constexpr DoubleRegister kFpParamRegisters[] = {f0, f1, f2, f3, f4, f5, f6, f7}; + constexpr DoubleRegister kFpReturnRegisters[] = {f0, f1}; + ++#elif V8_TARGET_ARCH_SW64 ++// =========================================================================== ++// == sw64 =================================================================== ++// =========================================================================== ++constexpr Register kGpParamRegisters[] = {a0, a1, a2, a3, a4, a5}; ++constexpr Register kGpReturnRegisters[] = {v0, t4}; // t4 used as v1 ++constexpr DoubleRegister kFpParamRegisters[] = {f16, f17, f18, f19, f20, f21}; ++constexpr DoubleRegister kFpReturnRegisters[] = {f0, f1}; ++ + #elif V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_PPC64 + // =========================================================================== + // == ppc & ppc64 ============================================================ +diff --git a/tools/v8_gypfiles/toolchain.gypi b/tools/v8_gypfiles/toolchain.gypi +index 61c59e4e..1d817062 100644 +--- a/tools/v8_gypfiles/toolchain.gypi ++++ b/tools/v8_gypfiles/toolchain.gypi +@@ -945,6 +945,18 @@ + }], #'_toolset=="host" + ], + }], # v8_target_arch=="mips64el" ++ ['v8_target_arch=="sw_64"', { ++ 'defines': [], ++ 'conditions': [], ++ 'target_conditions': [ ++ ['_toolset=="target"', { ++ 'defines': ['V8_TARGET_ARCH_SW64', 'SW64', ], ++ }], ++ ['_toolset=="host"', { ++ 'defines': ['V8_TARGET_ARCH_SW64', 'SW64', ], ++ }], ++ ], ++ }], # v8_target_arch=="sw64" + ['v8_target_arch=="x64"', { + 'defines': [ + 'V8_TARGET_ARCH_X64', +diff --git a/tools/v8_gypfiles/v8.gyp b/tools/v8_gypfiles/v8.gyp +index ba8b161f..1df58523 100644 +--- a/tools/v8_gypfiles/v8.gyp ++++ b/tools/v8_gypfiles/v8.gyp +@@ -302,6 +302,11 @@ + '<(V8_ROOT)/src/builtins/mips64/builtins-mips64.cc', + ], + }], ++ ['v8_target_arch=="sw_64"', { ++ 'sources': [ ++ '<(V8_ROOT)/src/builtins/sw64/builtins-sw64.cc', ++ ], ++ }], + ['v8_target_arch=="ppc"', { + 'sources': [ + '<(V8_ROOT)/src/builtins/ppc/builtins-ppc.cc', +@@ -887,6 +892,11 @@ + ' - 1:18.20.2-2 +- add sw64 support + * Sat May 11 2024 yangxianzhao - 1:18.20.2-1 - update to 18.20.2-1 -- Gitee