Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/BUILD.gn =================================================================== --- qtwebengine-everywhere-src-5.15.3.orig/src/3rdparty/chromium/v8/BUILD.gn +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/BUILD.gn @@ -714,7 +714,7 @@ config("toolchain") { # Mips64el/mipsel simulators. if (target_is_simulator && - (v8_current_cpu == "mipsel" || v8_current_cpu == "mips64el")) { + (v8_current_cpu == "mipsel" || v8_current_cpu == "mips64el" || v8_current_cpu == "riscv64")) { defines += [ "_MIPS_TARGET_SIMULATOR" ] } @@ -817,6 +817,21 @@ config("toolchain") { } } + # Under simulator build, compiler will not provide __riscv_xlen. Define here + if (v8_current_cpu == "riscv") { + defines += [ "V8_TARGET_ARCH_RISCV" ] + defines += [ "__riscv_xlen=32" ] + } + + if (v8_current_cpu == "riscv64") { + defines += [ "V8_TARGET_ARCH_RISCV64" ] + defines += [ "__riscv_xlen=64" ] + #FIXME: Temporarily use MIPS macro for the building. + defines += [ + "CAN_USE_FPU_INSTRUCTIONS", + ] + } + if (v8_current_cpu == "x86") { defines += [ "V8_TARGET_ARCH_IA32" ] if (is_win) { @@ -905,7 +920,7 @@ config("toolchain") { } if (v8_current_cpu == "x64" || v8_current_cpu == "arm64" || - v8_current_cpu == "mips64el") { + v8_current_cpu == "mips64el" || v8_current_cpu == "riscv64") { cflags += [ "-Wshorten-64-to-32" ] } } @@ -1888,6 +1903,16 @@ v8_source_set("v8_initializers") { ### gcmole(arch:s390) ### "src/builtins/s390/builtins-s390.cc", ] + } else if (v8_current_cpu == "riscv64") { + sources += [ + ### gcmole(arch:riscv64) ### + "src/builtins/riscv64/builtins-riscv64.cc", + ] + } else if (v8_current_cpu == "riscv") { + sources += [ + ### gcmole(arch:riscv) ### + "src/builtins/riscv/builtins-riscv.cc", + ] } if (!v8_enable_i18n_support) { @@ -3762,6 +3787,60 @@ v8_source_set("v8_base_without_compiler" "src/regexp/s390/regexp-macro-assembler-s390.h", "src/wasm/baseline/s390/liftoff-assembler-s390.h", ] + } else if (v8_current_cpu == "riscv64") { + sources += [ ### gcmole(arch:riscv64) ### + "src/codegen/riscv64/assembler-riscv64-inl.h", + "src/codegen/riscv64/assembler-riscv64.cc", + "src/codegen/riscv64/assembler-riscv64.h", + "src/codegen/riscv64/constants-riscv64.cc", + "src/codegen/riscv64/constants-riscv64.h", + "src/codegen/riscv64/cpu-riscv64.cc", + "src/codegen/riscv64/interface-descriptors-riscv64.cc", + "src/codegen/riscv64/macro-assembler-riscv64.cc", + "src/codegen/riscv64/macro-assembler-riscv64.h", + "src/codegen/riscv64/register-riscv64.h", + "src/compiler/backend/riscv64/code-generator-riscv64.cc", + "src/compiler/backend/riscv64/instruction-codes-riscv64.h", + "src/compiler/backend/riscv64/instruction-scheduler-riscv64.cc", + "src/compiler/backend/riscv64/instruction-selector-riscv64.cc", + "src/debug/riscv64/debug-riscv64.cc", + "src/deoptimizer/riscv64/deoptimizer-riscv64.cc", + "src/diagnostics/riscv64/disasm-riscv64.cc", + "src/execution/riscv64/frame-constants-riscv64.cc", + "src/execution/riscv64/frame-constants-riscv64.h", + "src/execution/riscv64/simulator-riscv64.cc", + "src/execution/riscv64/simulator-riscv64.h", + "src/regexp/riscv64/regexp-macro-assembler-riscv64.cc", + "src/regexp/riscv64/regexp-macro-assembler-riscv64.h", + "src/wasm/baseline/riscv64/liftoff-assembler-riscv64.h", + ] + } else if (v8_current_cpu == "riscv") { + sources += [ ### gcmole(arch:riscv) ### + "src/codegen/riscv/assembler-riscv-inl.h", + "src/codegen/riscv/assembler-riscv.cc", + "src/codegen/riscv/assembler-riscv.h", + "src/codegen/riscv/constants-riscv.cc", + "src/codegen/riscv/constants-riscv.h", + "src/codegen/riscv/cpu-riscv.cc", + "src/codegen/riscv/interface-descriptors-riscv.cc", + "src/codegen/riscv/macro-assembler-riscv.cc", + "src/codegen/riscv/macro-assembler-riscv.h", + "src/codegen/riscv/register-riscv.h", + "src/compiler/backend/riscv/code-generator-riscv.cc", + "src/compiler/backend/riscv/instruction-codes-riscv.h", + "src/compiler/backend/riscv/instruction-scheduler-riscv.cc", + "src/compiler/backend/riscv/instruction-selector-riscv.cc", + "src/debug/riscv/debug-riscv.cc", + "src/deoptimizer/riscv/deoptimizer-riscv.cc", + "src/diagnostics/riscv/disasm-riscv.cc", + "src/execution/riscv/frame-constants-riscv.cc", + "src/execution/riscv/frame-constants-riscv.h", + "src/execution/riscv/simulator-riscv.cc", + "src/execution/riscv/simulator-riscv.h", + "src/regexp/riscv/regexp-macro-assembler-riscv.cc", + "src/regexp/riscv/regexp-macro-assembler-riscv.h", + "src/wasm/baseline/riscv/liftoff-assembler-riscv.h", + ] } configs = [ @@ -3862,7 +3941,8 @@ v8_source_set("v8_base_without_compiler" if (v8_current_cpu == "mips" || v8_current_cpu == "mipsel" || v8_current_cpu == "mips64" || v8_current_cpu == "mips64el" || v8_current_cpu == "ppc" || v8_current_cpu == "ppc64" || - v8_current_cpu == "s390" || v8_current_cpu == "s390x") { + v8_current_cpu == "s390" || v8_current_cpu == "s390x" || + v8_current_cpu == "riscv64" || v8_current_cpu == "riscv") { libs += [ "atomic" ] } @@ -4197,7 +4277,7 @@ v8_component("v8_libbase") { } if (is_ubsan && (v8_current_cpu == "x86" || v8_current_cpu == "arm" || - v8_current_cpu == "mips")) { + v8_current_cpu == "mips" || v8_current_cpu == "riscv")) { # Special UBSan 32-bit requirement. sources += [ "src/base/ubsan.cc" ] } @@ -4328,6 +4408,8 @@ v8_source_set("v8_cppgc_shared") { sources += [ "src/heap/base/asm/mips/push_registers_asm.cc" ] } else if (current_cpu == "mips64el") { sources += [ "src/heap/base/asm/mips64/push_registers_asm.cc" ] + } else if (current_cpu == "riscv64") { + sources += [ "src/heap/base/asm/riscv64/push_registers_asm.cc" ] } } else if (is_win) { if (current_cpu == "x64") { Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/gni/snapshot_toolchain.gni =================================================================== --- qtwebengine-everywhere-src-5.15.3.orig/src/3rdparty/chromium/v8/gni/snapshot_toolchain.gni +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/gni/snapshot_toolchain.gni @@ -79,7 +79,8 @@ if (v8_snapshot_toolchain == "") { if (v8_current_cpu == "x64" || v8_current_cpu == "x86") { _cpus = v8_current_cpu - } else if (v8_current_cpu == "arm64" || v8_current_cpu == "mips64el") { + } else if (v8_current_cpu == "arm64" || v8_current_cpu == "mips64el" + || v8_current_cpu == "riscv64") { if (is_win && v8_current_cpu == "arm64") { # set _cpus to blank for Windows ARM64 so host_toolchain could be # selected as snapshot toolchain later. Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/infra/mb/mb_config.pyl =================================================================== --- qtwebengine-everywhere-src-5.15.3.orig/src/3rdparty/chromium/v8/infra/mb/mb_config.pyl +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/infra/mb/mb_config.pyl @@ -33,6 +33,12 @@ 'ppc64.debug.sim': 'default_debug_ppc64_sim', 'ppc64.optdebug.sim': 'default_optdebug_ppc64_sim', 'ppc64.release.sim': 'default_release_ppc64_sim', + 'riscv64.debug': 'default_debug_riscv64', + 'riscv64.optdebug': 'default_optdebug_riscv64', + 'riscv64.release': 'default_release_riscv64', + 'riscv64.debug.sim': 'default_debug_riscv64_sim', + 'riscv64.optdebug.sim': 'default_optdebug_riscv64_sim', + 'riscv64.release.sim': 'default_release_riscv64_sim', 's390x.debug': 'default_debug_s390x', 's390x.optdebug': 'default_optdebug_s390x', 's390x.release': 'default_release_s390x', @@ -175,6 +181,8 @@ # IBM. 'V8 Linux - ppc64 - sim': 'release_simulate_ppc64', 'V8 Linux - s390x - sim': 'release_simulate_s390x', + # RISC-V + 'V8 Linux - riscv64 - sim': 'release_simulate_riscv64', }, 'client.v8.branches': { 'V8 Linux - previous branch': 'release_x86', @@ -207,6 +215,9 @@ 'V8 s390x - sim - previous branch': 'release_simulate_s390x', 'V8 s390x - sim - beta branch': 'release_simulate_s390x', 'V8 s390x - sim - stable branch': 'release_simulate_s390x', + 'V8 riscv64 - sim - previous branch': 'release_simulate_riscv64', + 'V8 riscv64 - sim - beta branch': 'release_simulate_riscv64', + 'V8 riscv64 - sim - stable branch': 'release_simulate_riscv64', }, 'tryserver.v8': { 'v8_android_arm_compile_rel': 'release_android_arm', @@ -323,6 +334,18 @@ 'debug', 'simulate_mips64el', 'v8_enable_slow_dchecks'], 'default_release_mips64el': [ 'release', 'simulate_mips64el'], + 'default_debug_riscv64': [ + 'debug', 'riscv64', 'gcc', 'v8_enable_slow_dchecks', 'v8_full_debug'], + 'default_optdebug_riscv64': [ + 'debug', 'riscv64', 'gcc', 'v8_enable_slow_dchecks'], + 'default_release_riscv64': [ + 'release', 'riscv64', 'gcc'], + 'default_debug_riscv64_sim': [ + 'debug', 'simulate_riscv64', 'v8_enable_slow_dchecks', 'v8_full_debug'], + 'default_optdebug_riscv64_sim': [ + 'debug', 'simulate_riscv64', 'v8_enable_slow_dchecks'], + 'default_release_riscv64_sim': [ + 'release', 'simulate_riscv64'], 'default_debug_ppc64': [ 'debug', 'ppc64', 'gcc', 'v8_enable_slow_dchecks', 'v8_full_debug'], 'default_optdebug_ppc64': [ @@ -410,6 +433,8 @@ 'release_bot', 'simulate_mips64el'], 'release_simulate_ppc64': [ 'release_bot', 'simulate_ppc64'], + 'release_simulate_riscv64': [ + 'release_bot', 'simulate_riscv64'], 'release_simulate_s390x': [ 'release_bot', 'simulate_s390x'], @@ -812,6 +837,10 @@ 'gn_args': 'target_cpu="x64" v8_target_cpu="ppc64"', }, + 'simulate_riscv64': { + 'gn_args': 'target_cpu="x64" v8_target_cpu="riscv64"', + }, + 'simulate_s390x': { 'gn_args': 'target_cpu="x64" v8_target_cpu="s390x"', }, @@ -918,6 +947,10 @@ 'gn_args': 'target_cpu="ppc64" use_custom_libcxx=false', }, + 'riscv64': { + 'gn_args': 'target_cpu="riscv64" use_custom_libcxx=false', + }, + 'x64': { 'gn_args': 'target_cpu="x64"', }, Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/base/build_config.h =================================================================== --- qtwebengine-everywhere-src-5.15.3.orig/src/3rdparty/chromium/v8/src/base/build_config.h +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/base/build_config.h @@ -46,6 +46,16 @@ #else #define V8_HOST_ARCH_32_BIT 1 #endif +#elif defined(__riscv) || defined(__riscv__) +#if __riscv_xlen == 64 +#define V8_HOST_ARCH_RISCV64 1 +#define V8_HOST_ARCH_64_BIT 1 +#elif __riscv_xlen == 32 +#define V8_HOST_ARCH_RISCV 1 +#define V8_HOST_ARCH_32_BIT 1 +#else +#error "Cannot detect Riscv's bitwidth" +#endif #else #error "Host architecture was not detected as supported by v8" #endif @@ -77,7 +87,8 @@ // environment as presented by the compiler. #if !V8_TARGET_ARCH_X64 && !V8_TARGET_ARCH_IA32 && !V8_TARGET_ARCH_ARM && \ !V8_TARGET_ARCH_ARM64 && !V8_TARGET_ARCH_MIPS && !V8_TARGET_ARCH_MIPS64 && \ - !V8_TARGET_ARCH_PPC && !V8_TARGET_ARCH_PPC64 && !V8_TARGET_ARCH_S390 + !V8_TARGET_ARCH_PPC && !V8_TARGET_ARCH_PPC64 && !V8_TARGET_ARCH_S390 && \ + !V8_TARGET_ARCH_RISCV64 && !V8_TARGET_ARCH_RISCV #if defined(_M_X64) || defined(__x86_64__) #define V8_TARGET_ARCH_X64 1 #elif defined(_M_IX86) || defined(__i386__) @@ -94,6 +105,12 @@ #define V8_TARGET_ARCH_PPC64 1 #elif defined(_ARCH_PPC) #define V8_TARGET_ARCH_PPC 1 +#elif defined(__riscv) || defined(__riscv__) +#if __riscv_xlen == 64 +#define V8_TARGET_ARCH_RISCV64 1 +#elif __riscv_xlen == 32 +#define V8_TARGET_ARCH_RISCV 1 +#endif #else #error Target architecture was not detected as supported by v8 #endif @@ -128,6 +145,10 @@ #else #define V8_TARGET_ARCH_32_BIT 1 #endif +#elif V8_TARGET_ARCH_RISCV64 +#define V8_TARGET_ARCH_64_BIT 1 +#elif V8_TARGET_ARCH_RISCV +#define V8_TARGET_ARCH_32_BIT 1 #else #error Unknown target architecture pointer size #endif @@ -156,6 +177,13 @@ #if (V8_TARGET_ARCH_MIPS64 && !(V8_HOST_ARCH_X64 || V8_HOST_ARCH_MIPS64)) #error Target architecture mips64 is only supported on mips64 and x64 host #endif +#if (V8_TARGET_ARCH_RISCV64 && !(V8_HOST_ARCH_X64 || V8_HOST_ARCH_RISCV64)) +#error Target architecture riscv64 is only supported on riscv64 and x64 host +#endif +#if (V8_TARGET_ARCH_RISCV && \ + !(V8_HOST_ARCH_X64 || V8_HOST_ARCH_RISCV || V8_HOST_ARCH_RISCV64)) +#error Target architecture riscv (32) is only supported on riscv(32), riscv64, and x64 host +#endif // Determine architecture endianness. #if V8_TARGET_ARCH_IA32 @@ -190,6 +218,8 @@ #else #define V8_TARGET_BIG_ENDIAN 1 #endif +#elif V8_TARGET_ARCH_RISCV64 || V8_TARGET_ARCH_RISCV +#define V8_TARGET_LITTLE_ENDIAN 1 #else #error Unknown target architecture endianness #endif Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/base/compiler-specific.h =================================================================== --- qtwebengine-everywhere-src-5.15.3.orig/src/3rdparty/chromium/v8/src/base/compiler-specific.h +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/base/compiler-specific.h @@ -96,9 +96,10 @@ // do not support adding noexcept to default members. // Disabled on MSVC because constructors of standard containers are not noexcept // there. -#if ((!defined(V8_CC_GNU) && !defined(V8_CC_MSVC) && \ - !defined(V8_TARGET_ARCH_MIPS) && !defined(V8_TARGET_ARCH_MIPS64) && \ - !defined(V8_TARGET_ARCH_PPC) && !defined(V8_TARGET_ARCH_PPC64)) || \ +#if ((!defined(V8_CC_GNU) && !defined(V8_CC_MSVC) && \ + !defined(V8_TARGET_ARCH_MIPS) && !defined(V8_TARGET_ARCH_MIPS64) && \ + !defined(V8_TARGET_ARCH_PPC) && !defined(V8_TARGET_ARCH_PPC64) && \ + !defined(V8_TARGET_ARCH_RISCV64) && !defined(V8_TARGET_ARCH_RISCV)) || \ (defined(__clang__) && __cplusplus > 201300L)) #define V8_NOEXCEPT noexcept #else Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/base/macros.h =================================================================== --- qtwebengine-everywhere-src-5.15.3.orig/src/3rdparty/chromium/v8/src/base/macros.h +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/base/macros.h @@ -11,6 +11,8 @@ #include "src/base/compiler-specific.h" #include "src/base/logging.h" +#define DEBUG_PRINTF(...) if (FLAG_debug_riscv) { printf(__VA_ARGS__); } + // No-op macro which is used to work around MSVC's funky VA_ARGS support. #define EXPAND(x) x Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/base/platform/platform-posix.cc =================================================================== --- qtwebengine-everywhere-src-5.15.3.orig/src/3rdparty/chromium/v8/src/base/platform/platform-posix.cc +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/base/platform/platform-posix.cc @@ -324,6 +324,12 @@ 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_RISCV64 + // FIXME(RISCV): We need more information from the kernel to correctly mask + // this address for RISC-V. + raw_addr &= uint64_t{0xFFFFFF0000}; +#elif V8_TARGET_ARCH_RISCV + #error RISCV archiecture not supported #else raw_addr &= 0x3FFFF000; @@ -512,6 +518,8 @@ void OS::DebugBreak() { #elif V8_HOST_ARCH_S390 // Software breakpoint instruction is 0x0001 asm volatile(".word 0x0001"); +#elif V8_HOST_ARCH_RISCV64 || V8_HOST_ARCH_RISCV + asm("ebreak"); #else #error Unsupported host architecture. #endif Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/builtins/builtins-sharedarraybuffer-gen.cc =================================================================== --- qtwebengine-everywhere-src-5.15.3.orig/src/3rdparty/chromium/v8/src/builtins/builtins-sharedarraybuffer-gen.cc +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/builtins/builtins-sharedarraybuffer-gen.cc @@ -379,7 +379,9 @@ TF_BUILTIN(AtomicsExchange, SharedArrayB // 2. Let i be ? ValidateAtomicAccess(typedArray, index). TNode index_word = ValidateAtomicAccess(array, index, context); -#if V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 +#if V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_RISCV64 || \ + V8_TARGET_ARCH_RISCV + // FIXME(RISCV): Review this special case once atomics are added USE(array_buffer); TNode index_number = ChangeUintPtrToTagged(index_word); Return(CallRuntime(Runtime::kAtomicsExchange, context, array, index_number, @@ -481,7 +483,8 @@ TF_BUILTIN(AtomicsExchange, SharedArrayB // This shouldn't happen, we've already validated the type. BIND(&other); Unreachable(); -#endif // V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 +#endif // V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || + // V8_TARGET_ARCH_RISCV64 || V8_TARGET_ARCH_RISCV BIND(&detached); { @@ -510,7 +513,9 @@ TF_BUILTIN(AtomicsCompareExchange, Share TNode index_word = ValidateAtomicAccess(array, index, context); #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_PPC || V8_TARGET_ARCH_S390 || V8_TARGET_ARCH_S390X || \ + V8_TARGET_ARCH_RISCV64 || V8_TARGET_ARCH_RISCV + // FIXME(RISCV): Review this special case once atomics are added USE(array_buffer); TNode index_number = ChangeUintPtrToTagged(index_word); Return(CallRuntime(Runtime::kAtomicsCompareExchange, context, array, @@ -632,6 +637,7 @@ TF_BUILTIN(AtomicsCompareExchange, Share 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_RISCV BIND(&detached); { @@ -679,7 +685,9 @@ void SharedArrayBufferBuiltinsAssembler: TNode index_word = ValidateAtomicAccess(array, index, context); #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_PPC || V8_TARGET_ARCH_S390 || V8_TARGET_ARCH_S390X || \ + V8_TARGET_ARCH_RISCV64 || V8_TARGET_ARCH_RISCV + // FIXME(RISCV): Review this special case once atomics are added USE(array_buffer); TNode index_number = ChangeUintPtrToTagged(index_word); Return(CallRuntime(runtime_function, context, array, index_number, value)); @@ -783,6 +791,7 @@ void SharedArrayBufferBuiltinsAssembler: 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_RISCV BIND(&detached); ThrowTypeError(context, MessageTemplate::kDetachedOperation, method_name); Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/builtins/builtins.cc =================================================================== --- qtwebengine-everywhere-src-5.15.3.orig/src/3rdparty/chromium/v8/src/builtins/builtins.cc +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/builtins/builtins.cc @@ -490,14 +490,17 @@ bool Builtins::CodeObjectIsExecutable(in case Builtins::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_RISCV64 || \ + V8_TARGET_ARCH_RISCV + // FIXME(RISCV): Is this correct for RISC-V? // 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. return true; #else return false; -#endif // V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 +#endif // V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || + // V8_TARGET_ARCH_RISCV64 || V8_TARGET_ARCH_RISCV } } Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/builtins/riscv64/builtins-riscv64.cc =================================================================== --- /dev/null +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/builtins/riscv64/builtins-riscv64.cc @@ -0,0 +1,3197 @@ +// 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_RISCV64 + +#include "src/api/api-arguments.h" +#include "src/codegen/code-factory.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/riscv64/constants-riscv64.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" +#include "src/wasm/wasm-linkage.h" +#include "src/wasm/wasm-objects.h" + +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 ------------- + // -- a1 : target function (preserved for callee) + // -- a3 : new target (preserved for callee) + // ----------------------------------- + { + FrameScope scope(masm, StackFrame::INTERNAL); + // Push a copy of the function onto the stack. + // Push a copy of the target function and the new target. + __ Push(a1, a3, a1); + + __ CallRuntime(function_id, 1); + // Restore target function and new target. + __ Pop(a1, a3); + } + + static_assert(kJavaScriptCallCodeStartRegister == a2, "ABI mismatch"); + __ Add64(a2, a0, Operand(Code::kHeaderSize - kHeapObjectTag)); + __ Jump(a2); +} + +namespace { + +enum StackLimitKind { kInterruptStackLimit, kRealStackLimit }; + +void LoadStackLimit(MacroAssembler* masm, Register destination, + StackLimitKind kind) { + DCHECK(masm->root_array_available()); + Isolate* isolate = masm->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)); + __ Ld(destination, MemOperand(kRootRegister, static_cast(offset))); +} + +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); + + // The receiver for the builtin/api call. + __ PushRoot(RootIndex::kTheHoleValue); + + // Set up pointer to last argument. + __ Add64(t2, fp, Operand(StandardFrameConstants::kCallerSPOffset)); + + // Copy arguments and receiver to the expression stack. + Label loop, entry; + __ Move(t4, a0); + // ----------- S t a t e ------------- + // -- a0: number of arguments (untagged) + // -- a3: new target + // -- t2: pointer to last argument + // -- t4: counter + // -- sp[0*kPointerSize]: the hole (receiver) + // -- sp[1*kPointerSize]: number of arguments (tagged) + // -- sp[2*kPointerSize]: context + // ----------------------------------- + __ Branch(&entry); + __ bind(&loop); + __ CalcScaledAddress(t0, t2, t4, kPointerSizeLog2); + __ Ld(t1, MemOperand(t0)); + __ push(t1); + __ bind(&entry); + __ Add64(t4, t4, Operand(-1)); + __ Branch(&loop, greater_equal, t4, Operand(zero_reg)); + + // Call the function. + // a0: number of arguments (untagged) + // a1: constructor function + // a3: new target + __ InvokeFunctionWithNewTarget(a1, a3, a0, CALL_FUNCTION); + + // Restore context from the frame. + __ Ld(cp, MemOperand(fp, ConstructFrameConstants::kContextOffset)); + // Restore smi-tagged arguments count from the frame. + __ Ld(a1, MemOperand(fp, ConstructFrameConstants::kLengthOffset)); + // Leave construct frame. + } + + // Remove caller arguments from the stack and return. + __ SmiScale(a4, a1, kPointerSizeLog2); + __ Add64(sp, sp, a4); + __ Add64(sp, sp, kPointerSize); + __ Ret(); +} + +static void Generate_StackOverflowCheck(MacroAssembler* masm, Register num_args, + Register scratch1, Register scratch2, + Label* stack_overflow) { + // 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(masm, scratch1, StackLimitKind::kRealStackLimit); + // Make scratch1 the space we have left. The stack might already be overflowed + // here which will cause scratch1 to become negative. + __ Sub64(scratch1, sp, scratch1); + // Check if the arguments will overflow the stack. + __ Sll64(scratch2, num_args, kPointerSizeLog2); + // Signed comparison. + __ Branch(stack_overflow, le, scratch1, Operand(scratch2)); +} + +} // 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::CONSTRUCT); + Label post_instantiation_deopt_entry, not_create_implicit_receiver; + + // Preserve the incoming parameters on the stack. + __ SmiTag(a0); + __ Push(cp, a0, a1); + __ PushRoot(RootIndex::kTheHoleValue); + __ 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 + // ----------------------------------- + + __ Ld(t2, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset)); + __ Lwu(t2, FieldMemOperand(t2, SharedFunctionInfo::kFlagsOffset)); + __ DecodeField(t2); + __ JumpIfIsInRange(t2, kDefaultDerivedConstructor, kDerivedConstructor, + ¬_create_implicit_receiver); + + // If not derived class constructor: Allocate the new receiver object. + __ IncrementCounter(masm->isolate()->counters()->constructed_objects(), 1, + t2, t4); + __ 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(a0, RootIndex::kTheHoleValue); + + // ----------- S t a t e ------------- + // -- a0: 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. We need two copies + // because we may have to return the original one and the calling + // conventions dictate that the called function pops the receiver. + __ Push(a0, a0); + + // ----------- S t a t e ------------- + // -- r3: 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. + __ Ld(a1, MemOperand(fp, ConstructFrameConstants::kConstructorOffset)); + __ Ld(a0, MemOperand(fp, ConstructFrameConstants::kLengthOffset)); + __ SmiUntag(a0); + + // Set up pointer to last argument. + __ Add64(t2, fp, Operand(StandardFrameConstants::kCallerSPOffset)); + + Label enough_stack_space, stack_overflow; + Generate_StackOverflowCheck(masm, a0, t0, t1, &stack_overflow); + __ Branch(&enough_stack_space); + + __ bind(&stack_overflow); + // Restore the context from the frame. + __ Ld(cp, MemOperand(fp, ConstructFrameConstants::kContextOffset)); + __ CallRuntime(Runtime::kThrowStackOverflow); + // Unreachable code. + __ break_(0xCC); + + __ bind(&enough_stack_space); + + // Copy arguments and receiver to the expression stack. + Label loop, entry; + __ Move(t4, a0); + // ----------- S t a t e ------------- + // -- a0: number of arguments (untagged) + // -- a3: new target + // -- t2: pointer to last argument + // -- t4: counter + // -- sp[0*kPointerSize]: implicit receiver + // -- sp[1*kPointerSize]: implicit receiver + // -- sp[2*kPointerSize]: padding + // -- a1 and sp[3*kPointerSize]: constructor function + // -- sp[4*kPointerSize]: number of arguments (tagged) + // -- sp[5*kPointerSize]: context + // ----------------------------------- + __ Branch(&entry); + __ bind(&loop); + __ CalcScaledAddress(t0, t2, t4, kPointerSizeLog2); + __ Ld(t1, MemOperand(t0)); + __ push(t1); + __ bind(&entry); + __ Add64(t4, t4, Operand(-1)); + __ Branch(&loop, greater_equal, t4, Operand(zero_reg)); + + // Call the function. + __ InvokeFunctionWithNewTarget(a1, a3, a0, CALL_FUNCTION); + + // ----------- S t a t e ------------- + // -- a0: 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()); + + // Restore the context from the frame. + __ Ld(cp, MemOperand(fp, ConstructFrameConstants::kContextOffset)); + + // If the result is an object (in the ECMA sense), we should get rid + // of the receiver and use the result; see ECMA-262 section 13.2.2-7 + // on page 74. + Label use_receiver, do_throw, leave_frame; + + // If the result is undefined, we jump out to using the implicit receiver. + __ JumpIfRoot(a0, RootIndex::kUndefinedValue, &use_receiver); + + // Otherwise we do a smi check and fall through to check if the return value + // is a valid receiver. + + // If the result is a smi, it is *not* an object in the ECMA sense. + __ JumpIfSmi(a0, &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(a0, t2, t2); + STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE); + __ Branch(&leave_frame, greater_equal, t2, Operand(FIRST_JS_RECEIVER_TYPE)); + __ Branch(&use_receiver); + + __ bind(&do_throw); + __ CallRuntime(Runtime::kThrowConstructorReturnedNonObject); + + // Throw away the result of the constructor invocation and use the + // on-stack receiver as the result. + __ bind(&use_receiver); + __ Ld(a0, MemOperand(sp, 0 * kPointerSize)); + __ JumpIfRoot(a0, RootIndex::kTheHoleValue, &do_throw); + + __ bind(&leave_frame); + // Restore smi-tagged arguments count from the frame. + __ Ld(a1, MemOperand(fp, ConstructFrameConstants::kLengthOffset)); + // Leave construct frame. + } + // Remove caller arguments from the stack and return. + __ SmiScale(a4, a1, kPointerSizeLog2); + __ Add64(sp, sp, a4); + __ Add64(sp, sp, kPointerSize); + __ Ret(); +} + +void Builtins::Generate_JSBuiltinsConstructStub(MacroAssembler* masm) { + Generate_JSBuiltinsConstructStubHelper(masm); +} + +static void GetSharedFunctionInfoBytecode(MacroAssembler* masm, + Register sfi_data, + Register scratch1) { + Label done; + + __ GetObjectType(sfi_data, scratch1, scratch1); + __ Branch(&done, ne, scratch1, Operand(INTERPRETER_DATA_TYPE)); + __ Ld(sfi_data, + FieldMemOperand(sfi_data, InterpreterData::kBytecodeArrayOffset)); + + __ bind(&done); +} + +// static +void Builtins::Generate_ResumeGeneratorTrampoline(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- a0 : the value to pass to the generator + // -- a1 : the JSGeneratorObject to resume + // -- ra : return address + // ----------------------------------- + __ AssertGeneratorObject(a1); + + // Store input value into generator object. + __ Sd(a0, FieldMemOperand(a1, JSGeneratorObject::kInputOrDebugPosOffset)); + __ RecordWriteField(a1, JSGeneratorObject::kInputOrDebugPosOffset, a0, a3, + kRAHasNotBeenSaved, kDontSaveFPRegs); + + // Load suspended function and context. + __ Ld(a4, FieldMemOperand(a1, JSGeneratorObject::kFunctionOffset)); + __ Ld(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); + __ Lb(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); + __ Ld(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(masm, kScratchReg, StackLimitKind::kRealStackLimit); + __ Branch(&stack_overflow, Uless, sp, Operand(kScratchReg)); + + // Push receiver. + __ Ld(a5, FieldMemOperand(a1, JSGeneratorObject::kReceiverOffset)); + __ Push(a5); + + // ----------- S t a t e ------------- + // -- a1 : the JSGeneratorObject to resume + // -- a4 : generator function + // -- cp : generator context + // -- ra : return address + // -- sp[0] : generator receiver + // ----------------------------------- + + // Push holes for arguments to generator function. Since the parser forced + // context allocation for any variables in generators, the actual argument + // values have already been copied into the context and these dummy values + // will never be used. + __ Ld(a3, FieldMemOperand(a4, JSFunction::kSharedFunctionInfoOffset)); + __ Lhu(a3, + FieldMemOperand(a3, SharedFunctionInfo::kFormalParameterCountOffset)); + __ Ld(t1, + FieldMemOperand(a1, JSGeneratorObject::kParametersAndRegistersOffset)); + { + Label done_loop, loop; + __ Move(t2, zero_reg); + __ bind(&loop); + __ Sub64(a3, a3, Operand(1)); + __ Branch(&done_loop, lt, a3, Operand(zero_reg)); + __ CalcScaledAddress(kScratchReg, t1, t2, kPointerSizeLog2); + __ Ld(kScratchReg, FieldMemOperand(kScratchReg, FixedArray::kHeaderSize)); + __ Push(kScratchReg); + __ Add64(t2, t2, Operand(1)); + __ Branch(&loop); + __ bind(&done_loop); + } + + // Underlying function needs to have bytecode available. + if (FLAG_debug_code) { + __ Ld(a3, FieldMemOperand(a4, JSFunction::kSharedFunctionInfoOffset)); + __ Ld(a3, FieldMemOperand(a3, SharedFunctionInfo::kFunctionDataOffset)); + GetSharedFunctionInfoBytecode(masm, a3, a0); + __ GetObjectType(a3, a3, a3); + __ Assert(eq, AbortReason::kMissingBytecodeArray, a3, + Operand(BYTECODE_ARRAY_TYPE)); + } + + // Resume (Ignition/TurboFan) generator object. + { + __ Ld(a0, FieldMemOperand(a4, JSFunction::kSharedFunctionInfoOffset)); + __ Lhu(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"); + __ Ld(a2, FieldMemOperand(a1, JSFunction::kCodeOffset)); + __ Add64(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); + } + __ Ld(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); + } + __ Ld(a4, FieldMemOperand(a1, JSGeneratorObject::kFunctionOffset)); + __ Branch(&stepping_prepared); + + __ bind(&stack_overflow); + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ CallRuntime(Runtime::kThrowStackOverflow); + __ break_(0xCC); // 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(masm, scratch1, StackLimitKind::kRealStackLimit); + // Make a2 the space we have left. The stack might already be overflowed + // here which will cause r2 to become negative. + __ Sub64(scratch1, sp, scratch1); + // Check if the arguments will overflow the stack. + __ Sll64(scratch2, argc, kPointerSizeLog2); + __ 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, + Builtins::Name 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 + + // Save callee saved registers on the stack. + __ MultiPush(kCalleeSaved | ra.bit()); + + // Save callee-saved FPU registers. + __ MultiPushFPU(kCalleeSavedFPU); + // Set up the reserved register for 0.0. + __ LoadFPRImmediate(kDoubleRegZero, 0.0); + + // Initialize the root register. + // C calling convention. The first argument is passed in a0. + __ Move(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(s3, Operand(StackFrame::TypeToMarker(type))); + ExternalReference c_entry_fp = ExternalReference::Create( + IsolateAddressId::kCEntryFPAddress, masm->isolate()); + __ li(s4, c_entry_fp); + __ Ld(s4, MemOperand(s4)); + __ Push(s1, s2, s3, s4); + // Set up frame pointer for the frame to be pushed. + __ Add64(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); + __ Ld(s2, MemOperand(s1)); + __ Branch(&non_outermost_js, ne, s2, Operand(zero_reg)); + __ Sd(fp, MemOperand(s1)); + __ li(s3, Operand(StackFrame::OUTERMOST_JSENTRY_FRAME)); + Label cont; + __ Branch(&cont); + __ 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. + __ Branch(&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())); + __ Sd(a0, MemOperand(s1)); // We come back from 'invoke'. result is in a0. + __ LoadRoot(a0, RootIndex::kException); + __ Branch(&exit); + + // 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()->builtin_handle(entry_trampoline); + __ Call(trampoline_code, RelocInfo::CODE_TARGET); + + // Unlink this frame from the handler chain. + __ PopStackHandler(); + + __ bind(&exit); // a0 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); + __ Sd(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())); + __ Sd(a5, MemOperand(a4)); + + // Reset the stack to the callee saved registers. + __ Add64(sp, sp, -EntryFrameConstants::kCallerFPOffset); + + // Restore callee-saved fpu registers. + __ MultiPopFPU(kCalleeSavedFPU); + + // Restore callee saved registers from the stack. + __ MultiPop(kCalleeSaved | ra.bit()); + // Return. + __ Jump(ra); +} + +} // namespace + +void Builtins::Generate_JSEntry(MacroAssembler* masm) { + Generate_JSEntryVariant(masm, StackFrame::ENTRY, + Builtins::kJSEntryTrampoline); +} + +void Builtins::Generate_JSConstructEntry(MacroAssembler* masm) { + Generate_JSEntryVariant(masm, StackFrame::CONSTRUCT_ENTRY, + Builtins::kJSConstructEntryTrampoline); +} + +void Builtins::Generate_JSRunMicrotasksEntry(MacroAssembler* masm) { + Generate_JSEntryVariant(masm, StackFrame::ENTRY, + Builtins::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); + __ Ld(cp, MemOperand(cp)); + + // Push the function and the receiver onto the stack. + __ Push(a2, a3); + + // Check if we have enough stack space to push all arguments. + // Clobbers a0 and a3. + Generate_CheckStackOverflow(masm, a4, a0, a3); + + // Setup new.target, function and argc. + __ Move(a3, a1); + __ Move(a1, a2); + __ Move(a0, a4); + + // a0: argc + // a1: function + // a3: new.target + // a5: argv + + // Copy arguments to the stack in a loop. + // a3: argc + // a5: argv, i.e. points to first arg + Label loop, entry; + __ CalcScaledAddress(s1, a5, a4, kPointerSizeLog2); + __ Branch(&entry); + // s1 points past last arg. + __ bind(&loop); + __ Ld(s2, MemOperand(a5)); // Read next parameter. + __ Add64(a5, a5, kPointerSize); + __ Ld(s2, MemOperand(s2)); // Dereference handle. + __ push(s2); // Push parameter. + __ bind(&entry); + __ Branch(&loop, ne, a5, Operand(s1)); + + // a0: argc + // a1: function + // a3: new.target + + // Initialize all JavaScript callee-saved registers, since they will be seen + // by the garbage collector as part of handlers. + __ LoadRoot(a4, RootIndex::kUndefinedValue); + __ Move(a5, a4); + __ Move(s1, a4); + __ Move(s2, a4); + __ Move(s3, a4); + __ Move(s4, a4); + __ Move(s5, a4); + // s6 holds the root address. Do not clobber. + // s7 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 + __ Move(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) { + // Store code entry in the closure. + __ Sd(optimized_code, FieldMemOperand(closure, JSFunction::kCodeOffset)); + __ Move(scratch1, optimized_code); // Write barrier clobbers scratch1 below. + __ RecordWriteField(closure, JSFunction::kCodeOffset, scratch1, scratch2, + kRAHasNotBeenSaved, kDontSaveFPRegs, OMIT_REMEMBERED_SET, + OMIT_SMI_CHECK); +} + +static void LeaveInterpreterFrame(MacroAssembler* masm, Register scratch) { + Register args_count = scratch; + + // Get the arguments + receiver count. + __ Ld(args_count, + MemOperand(fp, InterpreterFrameConstants::kBytecodeArrayFromFp)); + __ Lw(t0, FieldMemOperand(args_count, BytecodeArray::kParameterSizeOffset)); + + // Leave the frame (also dropping the register file). + __ LeaveFrame(StackFrame::INTERPRETED); + + // Drop receiver + arguments. + __ Add64(sp, sp, args_count); +} + +// Tail-call |function_id| if |smi_entry| == |marker| +static void TailCallRuntimeIfMarkerEquals(MacroAssembler* masm, + Register smi_entry, + OptimizationMarker marker, + Runtime::FunctionId function_id) { + Label no_match; + __ Branch(&no_match, ne, smi_entry, Operand(Smi::FromEnum(marker))); + 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 ------------- + // -- 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; + + // Check if the optimized code is marked for deopt. If it is, call the + // runtime to clear it. + Label found_deoptimized_code; + __ Ld(a5, + FieldMemOperand(optimized_code_entry, Code::kCodeDataContainerOffset)); + __ Lw(a5, FieldMemOperand(a5, CodeDataContainer::kKindSpecificFlagsOffset)); + __ And(a5, a5, Operand(1 << Code::kMarkedForDeoptimizationBit)); + __ Branch(&found_deoptimized_code, ne, a5, 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"); + __ Add64(a2, optimized_code_entry, + Operand(Code::kHeaderSize - kHeapObjectTag)); + __ Jump(a2); + + // Optimized code slot contains deoptimized code, evict it and re-enter the + // closure's code. + __ bind(&found_deoptimized_code); + GenerateTailCallToReturnedCode(masm, Runtime::kEvictOptimizedCodeSlot); +} + +static void MaybeOptimizeCode(MacroAssembler* masm, Register feedback_vector, + Register optimization_marker) { + // ----------- S t a t e ------------- + // -- 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) + // -- optimization_marker : a Smi containing a non-zero optimization marker. + // ----------------------------------- + DCHECK(!AreAliased(feedback_vector, a1, a3, optimization_marker)); + + // TODO(v8:8394): The logging of first execution will break if + // feedback vectors are not allocated. We need to find a different way of + // logging these events if required. + TailCallRuntimeIfMarkerEquals(masm, optimization_marker, + OptimizationMarker::kLogFirstExecution, + Runtime::kFunctionFirstExecution); + TailCallRuntimeIfMarkerEquals(masm, optimization_marker, + OptimizationMarker::kCompileOptimized, + Runtime::kCompileOptimized_NotConcurrent); + TailCallRuntimeIfMarkerEquals(masm, optimization_marker, + OptimizationMarker::kCompileOptimizedConcurrent, + Runtime::kCompileOptimized_Concurrent); + + // Otherwise, the marker is InOptimizationQueue, so fall through hoping + // that an interrupt will eventually update the slot with optimized code. + if (FLAG_debug_code) { + __ Assert(eq, AbortReason::kExpectedOptimizationSentinel, + optimization_marker, + Operand(Smi::FromEnum(OptimizationMarker::kInOptimizationQueue))); + } +} + +// 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, Ugreater, 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. + __ Add64(bytecode_offset, bytecode_offset, Operand(1)); + __ Add64(scratch2, bytecode_array, bytecode_offset); + __ Lbu(bytecode, MemOperand(scratch2)); + __ Add64(bytecode_size_table, bytecode_size_table, + Operand(kIntSize * interpreter::Bytecodes::kBytecodeCount)); + __ Branch(&process_bytecode); + + __ bind(&extra_wide); + // Load the next bytecode and update table to the extra wide scaled table. + __ Add64(bytecode_offset, bytecode_offset, Operand(1)); + __ Add64(scratch2, bytecode_array, bytecode_offset); + __ Lbu(bytecode, MemOperand(scratch2)); + __ Add64(bytecode_size_table, bytecode_size_table, + Operand(2 * kIntSize * 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); + __ Branch(&end); + + __ bind(¬_jump_loop); + // Otherwise, load the size of the current bytecode and advance the offset. + __ CalcScaledAddress(scratch2, bytecode_size_table, bytecode, 2); + __ Lw(scratch2, MemOperand(scratch2)); + __ Add64(bytecode_offset, bytecode_offset, scratch2); + + __ bind(&end); +} + +// Generate code for entering a JS function with the interpreter. +// On entry to the function the receiver and arguments have been pushed on the +// stack left to right. The actual argument count matches the formal parameter +// count expected by the function. +// +// The live registers are: +// o 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 +// frames.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. + __ Ld(a0, FieldMemOperand(closure, JSFunction::kSharedFunctionInfoOffset)); + __ Ld(kInterpreterBytecodeArrayRegister, + FieldMemOperand(a0, SharedFunctionInfo::kFunctionDataOffset)); + GetSharedFunctionInfoBytecode(masm, kInterpreterBytecodeArrayRegister, a4); + + // The bytecode array could have been flushed from the shared function info, + // if so, call into CompileLazy. + Label compile_lazy; + __ GetObjectType(kInterpreterBytecodeArrayRegister, a0, a0); + __ Branch(&compile_lazy, ne, a0, Operand(BYTECODE_ARRAY_TYPE)); + + // Load the feedback vector from the closure. + __ Ld(feedback_vector, + FieldMemOperand(closure, JSFunction::kFeedbackCellOffset)); + __ Ld(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. + __ Ld(a4, FieldMemOperand(feedback_vector, HeapObject::kMapOffset)); + __ Lhu(a4, FieldMemOperand(a4, Map::kInstanceTypeOffset)); + __ Branch(&push_stack_frame, ne, a4, Operand(FEEDBACK_VECTOR_TYPE)); + + // Read off the optimized code slot in the feedback vector, and if there + // is optimized code or an optimization marker, call that instead. + Register optimized_code_entry = a4; + __ Ld(optimized_code_entry, + FieldMemOperand(feedback_vector, + FeedbackVector::kOptimizedCodeWeakOrSmiOffset)); + + // Check if the optimized code slot is not empty. + Label optimized_code_slot_not_empty; + + __ Branch(&optimized_code_slot_not_empty, ne, optimized_code_entry, + Operand(Smi::FromEnum(OptimizationMarker::kNone))); + + Label not_optimized; + __ bind(¬_optimized); + + // Increment invocation count for the function. + __ Lw(a4, FieldMemOperand(feedback_vector, + FeedbackVector::kInvocationCountOffset)); + __ Add32(a4, a4, Operand(1)); + __ Sw(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); + + // Reset code age and the OSR arming. The OSR field and BytecodeAgeOffset are + // 8-bit fields next to each other, so we could just optimize by writing a + // 16-bit. These static asserts guard our assumption is valid. + STATIC_ASSERT(BytecodeArray::kBytecodeAgeOffset == + BytecodeArray::kOsrNestingLevelOffset + kCharSize); + STATIC_ASSERT(BytecodeArray::kNoAgeBytecodeAge == 0); + __ Sh(zero_reg, FieldMemOperand(kInterpreterBytecodeArrayRegister, + BytecodeArray::kOsrNestingLevelOffset)); + + // 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. + __ Lw(a4, FieldMemOperand(kInterpreterBytecodeArrayRegister, + BytecodeArray::kFrameSizeOffset)); + + // Do a stack check to ensure we don't go over the limit. + __ Sub64(a5, sp, Operand(a4)); + LoadStackLimit(masm, a2, StackLimitKind::kRealStackLimit); + __ Branch(&stack_overflow, Uless, a5, Operand(a2)); + + // If ok, push undefined as the initial value for all register file entries. + Label loop_header; + Label loop_check; + __ LoadRoot(a5, RootIndex::kUndefinedValue); + __ Branch(&loop_check); + __ bind(&loop_header); + // TODO(rmcilroy): Consider doing more than one push per loop iteration. + __ push(a5); + // Continue loop if not done. + __ bind(&loop_check); + __ Sub64(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; + __ Lw(a5, FieldMemOperand( + kInterpreterBytecodeArrayRegister, + BytecodeArray::kIncomingNewTargetOrGeneratorRegisterOffset)); + __ Branch(&no_incoming_new_target_or_generator_register, eq, a5, + Operand(zero_reg)); + __ CalcScaledAddress(a5, fp, a5, kPointerSizeLog2); + __ Sd(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(masm, a5, StackLimitKind::kInterruptStackLimit); + __ Branch(&stack_check_interrupt, Uless, sp, Operand(a5)); + __ bind(&after_stack_check_interrupt); + + // Load accumulator as undefined. + __ LoadRoot(kInterpreterAccumulatorRegister, RootIndex::kUndefinedValue); + + // 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())); + __ Add64(a1, kInterpreterBytecodeArrayRegister, + kInterpreterBytecodeOffsetRegister); + __ Lbu(a7, MemOperand(a1)); + __ CalcScaledAddress(kScratchReg, kInterpreterDispatchTableRegister, a7, + kPointerSizeLog2); + __ Ld(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. + __ Ld(kInterpreterBytecodeArrayRegister, + MemOperand(fp, InterpreterFrameConstants::kBytecodeArrayFromFp)); + __ Ld(kInterpreterBytecodeOffsetRegister, + MemOperand(fp, InterpreterFrameConstants::kBytecodeOffsetFromFp)); + __ SmiUntag(kInterpreterBytecodeOffsetRegister); + + // Either return, or advance to the next bytecode and dispatch. + Label do_return; + __ Add64(a1, kInterpreterBytecodeArrayRegister, + kInterpreterBytecodeOffsetRegister); + __ Lbu(a1, MemOperand(a1)); + AdvanceBytecodeOffsetOrReturn(masm, kInterpreterBytecodeArrayRegister, + kInterpreterBytecodeOffsetRegister, a1, a2, a3, + a4, &do_return); + __ Branch(&do_dispatch); + + __ bind(&do_return); + // The return value is in a0. + LeaveInterpreterFrame(masm, t0); + __ 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))); + __ Sd(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. + __ Ld(kInterpreterBytecodeArrayRegister, + MemOperand(fp, InterpreterFrameConstants::kBytecodeArrayFromFp)); + __ li(kInterpreterBytecodeOffsetRegister, + Operand(BytecodeArray::kHeaderSize - kHeapObjectTag)); + __ LoadRoot(kInterpreterAccumulatorRegister, RootIndex::kUndefinedValue); + + __ SmiTag(a5, kInterpreterBytecodeOffsetRegister); + __ Sd(a5, MemOperand(fp, InterpreterFrameConstants::kBytecodeOffsetFromFp)); + + __ Branch(&after_stack_check_interrupt); + + __ bind(&optimized_code_slot_not_empty); + Label maybe_has_optimized_code; + // Check if optimized code marker is actually a weak reference to the + // optimized code as opposed to an optimization marker. + __ JumpIfNotSmi(optimized_code_entry, &maybe_has_optimized_code); + MaybeOptimizeCode(masm, feedback_vector, optimized_code_entry); + // Fall through if there's no runnable optimized code. + __ Branch(¬_optimized); + + __ bind(&maybe_has_optimized_code); + // Load code entry from the weak reference, if it was cleared, resume + // execution of unoptimized code. + __ LoadWeakValue(optimized_code_entry, optimized_code_entry, ¬_optimized); + TailCallOptimizedCodeSlot(masm, optimized_code_entry, t4, a5); + + __ bind(&compile_lazy); + GenerateTailCallToReturnedCode(masm, Runtime::kCompileLazy); + // Unreachable code. + __ break_(0xCC); + + __ bind(&stack_overflow); + __ CallRuntime(Runtime::kThrowStackOverflow); + // Unreachable code. + __ break_(0xCC); +} + +static void Generate_InterpreterPushArgs(MacroAssembler* masm, + Register num_args, Register index, + Register scratch, Register scratch2) { + // Find the address of the last argument. + __ Move(scratch2, num_args); + __ Sll64(scratch2, scratch2, kPointerSizeLog2); + __ Sub64(scratch2, index, Operand(scratch2)); + + // Push the arguments. + Label loop_header, loop_check; + __ Branch(&loop_check); + __ bind(&loop_header); + __ Ld(scratch, MemOperand(index)); + __ Add64(index, index, Operand(-kPointerSize)); + __ push(scratch); + __ bind(&loop_check); + __ Branch(&loop_header, Ugreater, index, Operand(scratch2)); +} + +// 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 (not including the receiver) + // -- 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; + + __ Add64(a3, a0, Operand(1)); // Add one for receiver. + + // Push "undefined" as the receiver arg if we need to. + if (receiver_mode == ConvertReceiverMode::kNullOrUndefined) { + __ PushRoot(RootIndex::kUndefinedValue); + __ Sub64(a3, a3, Operand(1)); // Subtract one for receiver. + } + + Generate_StackOverflowCheck(masm, a3, a4, t0, &stack_overflow); + + // This function modifies a2, t0 and a4. + Generate_InterpreterPushArgs(masm, a3, a2, a4, t0); + + if (mode == InterpreterPushArgsMode::kWithFinalSpread) { + __ Pop(a2); // Pass the spread in a register + __ Sub64(a0, a0, Operand(1)); // Subtract one for spread + } + + // 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. + __ break_(0xCC); + } +} + +// static +void Builtins::Generate_InterpreterPushArgsThenConstructImpl( + MacroAssembler* masm, InterpreterPushArgsMode mode) { + // ----------- S t a t e ------------- + // -- a0 : argument count (not including receiver) + // -- a3 : new target + // -- a1 : constructor to call + // -- a2 : allocation site feedback if available, undefined otherwise. + // -- a4 : address of the first argument + // ----------------------------------- + Label stack_overflow; + + // Push a slot for the receiver. + __ push(zero_reg); + + Generate_StackOverflowCheck(masm, a0, a5, t0, &stack_overflow); + + // This function modifies t0, a4 and a5. + Generate_InterpreterPushArgs(masm, a0, a4, a5, t0); + + if (mode == InterpreterPushArgsMode::kWithFinalSpread) { + __ Pop(a2); // Pass the spread in a register + __ Sub64(a0, a0, Operand(1)); // Subtract one for spread + } 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. + __ break_(0xCC); + } +} + +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. + __ Ld(t0, MemOperand(fp, StandardFrameConstants::kFunctionOffset)); + __ Ld(t0, FieldMemOperand(t0, JSFunction::kSharedFunctionInfoOffset)); + __ Ld(t0, FieldMemOperand(t0, SharedFunctionInfo::kFunctionDataOffset)); + __ GetObjectType(t0, kInterpreterDispatchTableRegister, + kInterpreterDispatchTableRegister); + __ Branch(&builtin_trampoline, ne, kInterpreterDispatchTableRegister, + Operand(INTERPRETER_DATA_TYPE)); + + __ Ld(t0, FieldMemOperand(t0, InterpreterData::kInterpreterTrampolineOffset)); + __ Add64(t0, t0, Operand(Code::kHeaderSize - kHeapObjectTag)); + __ Branch(&trampoline_loaded); + + __ bind(&builtin_trampoline); + __ li(t0, ExternalReference:: + address_of_interpreter_entry_trampoline_instruction_start( + masm->isolate())); + __ Ld(t0, MemOperand(t0)); + + __ bind(&trampoline_loaded); + __ Add64(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. + __ Ld(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. + __ break_(0xCC); + __ bind(&okay); + } + + // Dispatch to the target bytecode. + __ Add64(a1, kInterpreterBytecodeArrayRegister, + kInterpreterBytecodeOffsetRegister); + __ Lbu(a7, MemOperand(a1)); + __ CalcScaledAddress(a1, kInterpreterDispatchTableRegister, a7, + kPointerSizeLog2); + __ Ld(kJavaScriptCallCodeStartRegister, MemOperand(a1)); + __ Jump(kJavaScriptCallCodeStartRegister); +} + +void Builtins::Generate_InterpreterEnterBytecodeAdvance(MacroAssembler* masm) { + // Advance the current bytecode offset stored within the given interpreter + // stack frame. This simulates what all bytecode handlers do upon completion + // of the underlying operation. + __ Ld(kInterpreterBytecodeArrayRegister, + MemOperand(fp, InterpreterFrameConstants::kBytecodeArrayFromFp)); + __ Ld(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. + __ Add64(a1, kInterpreterBytecodeArrayRegister, + kInterpreterBytecodeOffsetRegister); + __ Lbu(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); + __ Sd(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_InterpreterEnterBytecodeDispatch(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(); + if (with_result) { + // Overwrite the hole inserted by the deoptimizer with the return value from + // the LAZY deopt point. + __ Sd(a0, + 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)); + } + } + __ Ld(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); + __ Add64(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(), a0.code()); + __ Ld(a0, MemOperand(sp, 0 * kPointerSize)); + __ Add64(sp, sp, Operand(1 * kPointerSize)); // Remove state. + __ Ret(); +} + +void Builtins::Generate_InterpreterOnStackReplacement(MacroAssembler* masm) { + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ CallRuntime(Runtime::kCompileForOnStackReplacement); + } + + // If the code object is null, just return to the caller. + __ Ret(eq, a0, Operand(Smi::zero())); + + // 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] + __ Ld(a1, MemOperand(a0, Code::kDeoptimizationDataOffset - 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 + + __ Add64(a0, a0, a1); + __ Add64(ra, a0, Code::kHeaderSize - kHeapObjectTag); + // And "return" to the OSR entry point of the function. + __ Ret(); +} + +// static +void Builtins::Generate_FunctionPrototypeApply(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- a0 : argc + // -- sp[0] : argArray + // -- sp[4] : thisArg + // -- sp[8] : receiver + // ----------------------------------- + + Register argc = a0; + Register arg_array = a2; + Register receiver = a1; + Register this_arg = a5; + Register undefined_value = a3; + + __ 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. + { + // Claim (2 - argc) dummy arguments form the stack, to put the stack in a + // consistent state for a simple pop operation. + + __ Sub64(sp, sp, Operand(2 * kPointerSize)); + __ CalcScaledAddress(sp, sp, argc, kPointerSizeLog2); + __ Pop(this_arg, arg_array); // Overwrite argc + + Label done0, done1; + __ Branch(&done0, ne, argc, Operand(zero_reg)); + __ Move(arg_array, undefined_value); // if argc == 0 + __ Move(this_arg, undefined_value); // if argc == 0 + __ bind(&done0); // else (i.e., argc > 0) + + __ Branch(&done1, ne, argc, Operand(1)); + __ Move(arg_array, undefined_value); // if argc == 1 + __ bind(&done1); // else (i.e., argc > 1) + + __ Ld(receiver, MemOperand(sp)); + __ Sd(this_arg, MemOperand(sp)); + } + + // ----------- 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); + { + __ Move(a0, zero_reg); + DCHECK(receiver == a1); + __ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET); + } +} + +// static +void Builtins::Generate_FunctionPrototypeCall(MacroAssembler* masm) { + // 1. Make sure we have at least one argument. + // a0: actual number of arguments + { + Label done; + __ Branch(&done, ne, a0, Operand(zero_reg)); + __ PushRoot(RootIndex::kUndefinedValue); + __ Add64(a0, a0, Operand(1)); + __ bind(&done); + } + + // 2. Get the function to call (passed as receiver) from the stack. + // a0: actual number of arguments + __ CalcScaledAddress(kScratchReg, sp, a0, kPointerSizeLog2); + __ Ld(a1, MemOperand(kScratchReg)); + + // 3. Shift arguments and return address one slot down on the stack + // (overwriting the original receiver). Adjust argument count to make + // the original first argument the new receiver. + // a0: actual number of arguments + // a1: function + { + Label loop; + // Calculate the copy start address (destination). Copy end address is sp. + __ CalcScaledAddress(a2, sp, a0, kPointerSizeLog2); + + __ bind(&loop); + __ Ld(kScratchReg, MemOperand(a2, -kPointerSize)); + __ Sd(kScratchReg, MemOperand(a2)); + __ Sub64(a2, a2, Operand(kPointerSize)); + __ Branch(&loop, ne, a2, Operand(sp)); + // Adjust the actual number of arguments and remove the top element + // (which is a copy of the last argument). + __ Sub64(a0, a0, Operand(1)); + __ Pop(); + } + + // 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] : argumentsList (if argc ==3) + // -- sp[4] : thisArgument (if argc >=2) + // -- sp[8] : target (if argc >=1) + // -- sp[12] : receiver + // ----------------------------------- + + Register argc = a0; + Register arguments_list = a2; + Register target = a1; + Register this_argument = a5; + Register undefined_value = a3; + + __ 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. + + __ Sub64(sp, sp, Operand(3 * kPointerSize)); + __ CalcScaledAddress(sp, sp, argc, kPointerSizeLog2); + __ Pop(target, this_argument, arguments_list); + + Label done0, done1, done2; + __ Branch(&done0, ne, argc, Operand(zero_reg)); + __ Move(arguments_list, undefined_value); // if argc == 0 + __ Move(this_argument, undefined_value); // if argc == 0 + __ Move(target, undefined_value); // if argc == 0 + __ bind(&done0); // argc != 0 + + __ Branch(&done1, ne, argc, Operand(1)); + __ Move(arguments_list, undefined_value); // if argc == 1 + __ Move(this_argument, undefined_value); // if argc == 1 + __ bind(&done1); // argc > 1 + + __ Branch(&done2, ne, argc, Operand(2)); + __ Move(arguments_list, undefined_value); // if argc == 2 + __ bind(&done2); // argc > 2 + + __ Sd(this_argument, MemOperand(sp, 0)); // Overwrite receiver + } + + // ----------- 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] : new.target (optional) (dummy value if argc <= 2) + // -- sp[4] : argumentsList (dummy value if argc <= 1) + // -- sp[8] : target (dummy value if argc == 0) + // -- sp[12] : receiver + // ----------------------------------- + Register argc = a0; + Register arguments_list = a2; + Register target = a1; + Register new_target = a3; + Register undefined_value = a4; + + __ 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. + __ Sub64(sp, sp, Operand(3 * kPointerSize)); + __ CalcScaledAddress(sp, sp, argc, kPointerSizeLog2); + __ Pop(target, arguments_list, new_target); + + Label done0, done1, done2; + __ Branch(&done0, ne, argc, Operand(zero_reg)); + __ Move(arguments_list, undefined_value); // if argc == 0 + __ Move(new_target, undefined_value); // if argc == 0 + __ Move(target, undefined_value); // if argc == 0 + __ bind(&done0); + + __ Branch(&done1, ne, argc, Operand(1)); + __ Move(arguments_list, undefined_value); // if argc == 1 + __ Move(new_target, target); // if argc == 1 + __ bind(&done1); + + __ Branch(&done2, ne, argc, Operand(2)); + __ Move(new_target, target); // if argc == 2 + __ bind(&done2); + + __ Sd(undefined_value, MemOperand(sp, 0)); // Overwrite receiver + } + + // ----------- 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); +} + +static void EnterArgumentsAdaptorFrame(MacroAssembler* masm) { + __ SmiTag(a0); + __ li(a4, Operand(StackFrame::TypeToMarker(StackFrame::ARGUMENTS_ADAPTOR))); + __ MultiPush(a0.bit() | a1.bit() | a4.bit() | fp.bit() | ra.bit()); + __ Push(Smi::zero()); // Padding. + __ Add64(fp, sp, + Operand(ArgumentsAdaptorFrameConstants::kFixedFrameSizeFromFp)); +} + +static void LeaveArgumentsAdaptorFrame(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- a0 : result being passed through + // ----------------------------------- + // Get the number of arguments passed (as a smi), tear down the frame and + // then tear down the parameters. + __ Ld(a1, MemOperand(fp, ArgumentsAdaptorFrameConstants::kLengthOffset)); + __ Move(sp, fp); + __ MultiPop(fp.bit() | ra.bit()); + __ SmiScale(a4, a1, kPointerSizeLog2); + __ Add64(sp, sp, a4); + // Adjust for the receiver. + __ Add64(sp, sp, Operand(kPointerSize)); +} + +// static +void Builtins::Generate_CallOrConstructVarargs(MacroAssembler* masm, + Handle code) { + // ----------- S t a t e ------------- + // -- a1 : target + // -- a0 : number of parameters on the stack (not including the receiver) + // -- a2 : arguments list (a FixedArray) + // -- a4 : len (number of elements to push from args) + // -- a3 : new.target (for [[Construct]]) + // ----------------------------------- + if (masm->emit_debug_code()) { + // Allow a2 to be a FixedArray, or a FixedDoubleArray if a4 == 0. + Label ok, fail; + __ AssertNotSmi(a2); + __ GetObjectType(a2, t5, t5); + __ Branch(&ok, eq, t5, Operand(FIXED_ARRAY_TYPE)); + __ Branch(&fail, ne, t5, 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; + Generate_StackOverflowCheck(masm, len, kScratchReg, a5, &stack_overflow); + + // Push arguments onto the stack (thisArgument is already on the stack). + { + Label done, push, loop; + Register src = a6; + Register scratch = len; + __ Add64(src, args, FixedArray::kHeaderSize - kHeapObjectTag); + __ Add64(a0, a0, len); // The 'len' argument for Call() or Construct(). + __ Branch(&done, eq, len, Operand(zero_reg)); + __ Sll64(scratch, len, kPointerSizeLog2); + __ Sub64(scratch, sp, Operand(scratch)); + __ LoadRoot(t1, RootIndex::kTheHoleValue); + __ bind(&loop); + __ Ld(a5, MemOperand(src)); + __ Branch(&push, ne, a5, Operand(t1)); + __ LoadRoot(a5, RootIndex::kUndefinedValue); + __ bind(&push); + __ Add64(src, src, kPointerSize); + __ Push(a5); + __ 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 (not including the receiver) + // -- 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); + __ Ld(t1, FieldMemOperand(a3, HeapObject::kMapOffset)); + __ Lbu(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); + } + + // Check if we have an arguments adaptor frame below the function frame. + Label arguments_adaptor, arguments_done; + __ Ld(a6, MemOperand(fp, StandardFrameConstants::kCallerFPOffset)); + __ Ld(a7, MemOperand(a6, CommonFrameConstants::kContextOrFrameTypeOffset)); + __ Branch(&arguments_adaptor, eq, a7, + Operand(StackFrame::TypeToMarker(StackFrame::ARGUMENTS_ADAPTOR))); + { + __ Ld(a7, MemOperand(fp, StandardFrameConstants::kFunctionOffset)); + __ Ld(a7, FieldMemOperand(a7, JSFunction::kSharedFunctionInfoOffset)); + __ Lhu(a7, FieldMemOperand( + a7, SharedFunctionInfo::kFormalParameterCountOffset)); + __ Move(a6, fp); + } + __ Branch(&arguments_done); + __ bind(&arguments_adaptor); + { + // Just get the length from the ArgumentsAdaptorFrame. + __ SmiUntag(a7, + MemOperand(a6, ArgumentsAdaptorFrameConstants::kLengthOffset)); + } + __ bind(&arguments_done); + + Label stack_done, stack_overflow; + __ Sub32(a7, a7, a2); + __ Branch(&stack_done, le, a7, Operand(zero_reg)); + { + // Check for stack overflow. + Generate_StackOverflowCheck(masm, a7, a4, a5, &stack_overflow); + + // Forward the arguments from the caller frame. + { + Label loop; + __ Add64(a0, a0, a7); + __ bind(&loop); + { + __ CalcScaledAddress(kScratchReg, a6, a7, kPointerSizeLog2); + __ Ld(kScratchReg, MemOperand(kScratchReg, 1 * kPointerSize)); + __ push(kScratchReg); + __ Sub32(a7, a7, Operand(1)); + __ Branch(&loop, ne, a7, 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 (not including the receiver) + // -- a1 : the function to call (checked to be a JSFunction) + // ----------------------------------- + __ AssertFunction(a1); + + // See ES6 section 9.2.1 [[Call]] ( thisArgument, argumentsList) + // Check that function is not a "classConstructor". + Label class_constructor; + __ Ld(a2, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset)); + __ Lwu(a3, FieldMemOperand(a2, SharedFunctionInfo::kFlagsOffset)); + __ And(kScratchReg, a3, + Operand(SharedFunctionInfo::IsClassConstructorBit::kMask)); + __ Branch(&class_constructor, ne, kScratchReg, Operand(zero_reg)); + + // 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. + __ Ld(cp, FieldMemOperand(a1, JSFunction::kContextOffset)); + // We need to convert the receiver for non-native sloppy mode functions. + Label done_convert; + __ Lwu(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 (not including the receiver) + // -- 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; + __ CalcScaledAddress(kScratchReg, sp, a0, kPointerSizeLog2); + __ Ld(a3, MemOperand(kScratchReg)); + __ JumpIfSmi(a3, &convert_to_object); + STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE); + __ GetObjectType(a3, a4, a4); + __ Branch(&done_convert, Ugreater_equal, 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); + __ Move(a0, a3); + __ Push(cp); + __ Call(BUILTIN_CODE(masm->isolate(), ToObject), + RelocInfo::CODE_TARGET); + __ Pop(cp); + __ Move(a3, a0); + __ Pop(a0, a1); + __ SmiUntag(a0); + } + __ Ld(a2, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset)); + __ bind(&convert_receiver); + } + __ CalcScaledAddress(kScratchReg, sp, a0, kPointerSizeLog2); + __ Sd(a3, MemOperand(kScratchReg)); + } + __ bind(&done_convert); + + // ----------- S t a t e ------------- + // -- a0 : the number of arguments (not including the receiver) + // -- a1 : the function to call (checked to be a JSFunction) + // -- a2 : the shared function info. + // -- cp : the function context. + // ----------------------------------- + + __ Lhu(a2, + FieldMemOperand(a2, SharedFunctionInfo::kFormalParameterCountOffset)); + __ InvokeFunctionCode(a1, no_reg, a2, a0, JUMP_FUNCTION); + + // The function is a "classConstructor", need to raise an exception. + __ bind(&class_constructor); + { + FrameScope frame(masm, StackFrame::INTERNAL); + __ Push(a1); + __ CallRuntime(Runtime::kThrowConstructorNonCallableError); + } +} + +// static +void Builtins::Generate_CallBoundFunctionImpl(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- a0 : the number of arguments (not including the receiver) + // -- a1 : the function to call (checked to be a JSBoundFunction) + // ----------------------------------- + __ AssertBoundFunction(a1); + + // Patch the receiver to [[BoundThis]]. + { + __ Ld(kScratchReg, FieldMemOperand(a1, JSBoundFunction::kBoundThisOffset)); + __ CalcScaledAddress(a4, sp, a0, kPointerSizeLog2); + __ Sd(kScratchReg, MemOperand(a4)); + } + + // Load [[BoundArguments]] into a2 and length of that into a4. + __ Ld(a2, FieldMemOperand(a1, JSBoundFunction::kBoundArgumentsOffset)); + __ SmiUntag(a4, FieldMemOperand(a2, FixedArray::kLengthOffset)); + + // ----------- S t a t e ------------- + // -- a0 : the number of arguments (not including the receiver) + // -- 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; + __ Sll64(a5, a4, kPointerSizeLog2); + __ Sub64(sp, 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(masm, kScratchReg, StackLimitKind::kRealStackLimit); + __ Branch(&done, Ugreater_equal, sp, Operand(kScratchReg)); + // Restore the stack pointer. + __ Add64(sp, sp, Operand(a5)); + { + FrameScope scope(masm, StackFrame::MANUAL); + __ EnterFrame(StackFrame::INTERNAL); + __ CallRuntime(Runtime::kThrowStackOverflow); + } + __ bind(&done); + } + + // Relocate arguments down the stack. + { + Label loop, done_loop; + __ Move(a5, zero_reg); + __ bind(&loop); + __ Branch(&done_loop, gt, a5, Operand(a0)); + __ CalcScaledAddress(a6, sp, a4, kPointerSizeLog2); + __ Ld(kScratchReg, MemOperand(a6)); + __ CalcScaledAddress(a6, sp, a5, kPointerSizeLog2); + __ Sd(kScratchReg, MemOperand(a6)); + __ Add64(a4, a4, Operand(1)); + __ Add64(a5, a5, Operand(1)); + __ Branch(&loop); + __ bind(&done_loop); + } + + // Copy [[BoundArguments]] to the stack (below the arguments). + { + Label loop, done_loop; + __ SmiUntag(a4, FieldMemOperand(a2, FixedArray::kLengthOffset)); + __ Add64(a2, a2, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); + __ bind(&loop); + __ Sub64(a4, a4, Operand(1)); + __ Branch(&done_loop, lt, a4, Operand(zero_reg)); + __ CalcScaledAddress(a5, a2, a4, kPointerSizeLog2); + __ Ld(kScratchReg, MemOperand(a5)); + __ CalcScaledAddress(a5, sp, a0, kPointerSizeLog2); + __ Sd(kScratchReg, MemOperand(a5)); + __ Add64(a0, a0, Operand(1)); + __ Branch(&loop); + __ bind(&done_loop); + } + + // Call the [[BoundTargetFunction]] via the Call builtin. + __ Ld(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 (not including the receiver) + // -- a1 : the target to call (can be any Object). + // ----------------------------------- + + Label non_callable, non_smi; + __ JumpIfSmi(a1, &non_callable); + __ bind(&non_smi); + __ GetObjectType(a1, t1, t2); + __ Jump(masm->isolate()->builtins()->CallFunction(mode), + RelocInfo::CODE_TARGET, eq, t2, Operand(JS_FUNCTION_TYPE)); + __ Jump(BUILTIN_CODE(masm->isolate(), CallBoundFunction), + RelocInfo::CODE_TARGET, eq, t2, Operand(JS_BOUND_FUNCTION_TYPE)); + + // Check if target has a [[Call]] internal method. + __ Lbu(t1, FieldMemOperand(t1, Map::kBitFieldOffset)); + __ And(t1, t1, Operand(Map::Bits1::IsCallableBit::kMask)); + __ Branch(&non_callable, eq, t1, Operand(zero_reg)); + + __ Jump(BUILTIN_CODE(masm->isolate(), CallProxy), RelocInfo::CODE_TARGET, eq, + t2, Operand(JS_PROXY_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. + __ CalcScaledAddress(kScratchReg, sp, a0, kPointerSizeLog2); + __ Sd(a1, MemOperand(kScratchReg)); + // Let the "call_as_function_delegate" take care of the rest. + __ LoadNativeContextSlot(Context::CALL_AS_FUNCTION_DELEGATE_INDEX, a1); + __ 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(a1); + __ CallRuntime(Runtime::kThrowCalledNonCallable); + } +} + +void Builtins::Generate_ConstructFunction(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- a0 : the number of arguments (not including the receiver) + // -- 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. + __ Ld(a4, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset)); + __ Lwu(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 (not including the receiver) + // -- 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. + __ Ld(a2, FieldMemOperand(a1, JSBoundFunction::kBoundArgumentsOffset)); + __ SmiUntag(a4, FieldMemOperand(a2, FixedArray::kLengthOffset)); + + // ----------- S t a t e ------------- + // -- a0 : the number of arguments (not including the receiver) + // -- 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; + __ Sll64(a5, a4, kPointerSizeLog2); + __ Sub64(sp, 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(masm, kScratchReg, StackLimitKind::kRealStackLimit); + __ Branch(&done, Ugreater_equal, sp, Operand(kScratchReg)); + // Restore the stack pointer. + __ Add64(sp, sp, Operand(a5)); + { + FrameScope scope(masm, StackFrame::MANUAL); + __ EnterFrame(StackFrame::INTERNAL); + __ CallRuntime(Runtime::kThrowStackOverflow); + } + __ bind(&done); + } + + // Relocate arguments down the stack. + { + Label loop, done_loop; + __ Move(a5, zero_reg); + __ bind(&loop); + __ Branch(&done_loop, ge, a5, Operand(a0)); + __ CalcScaledAddress(a6, sp, a4, kPointerSizeLog2); + __ Ld(kScratchReg, MemOperand(a6)); + __ CalcScaledAddress(a6, sp, a5, kPointerSizeLog2); + __ Sd(kScratchReg, MemOperand(a6)); + __ Add64(a4, a4, Operand(1)); + __ Add64(a5, a5, Operand(1)); + __ Branch(&loop); + __ bind(&done_loop); + } + + // Copy [[BoundArguments]] to the stack (below the arguments). + { + Label loop, done_loop; + __ SmiUntag(a4, FieldMemOperand(a2, FixedArray::kLengthOffset)); + __ Add64(a2, a2, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); + __ bind(&loop); + __ Sub64(a4, a4, Operand(1)); + __ Branch(&done_loop, lt, a4, Operand(zero_reg)); + __ CalcScaledAddress(a5, a2, a4, kPointerSizeLog2); + __ Ld(kScratchReg, MemOperand(a5)); + __ CalcScaledAddress(a5, sp, a0, kPointerSizeLog2); + __ Sd(kScratchReg, MemOperand(a5)); + __ Add64(a0, a0, Operand(1)); + __ Branch(&loop); + __ bind(&done_loop); + } + + // Patch new.target to [[BoundTargetFunction]] if new.target equals target. + { + Label skip_load; + __ Branch(&skip_load, ne, a1, Operand(a3)); + __ Ld(a3, FieldMemOperand(a1, JSBoundFunction::kBoundTargetFunctionOffset)); + __ bind(&skip_load); + } + + // Construct the [[BoundTargetFunction]] via the Construct builtin. + __ Ld(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 (not including the receiver) + // -- 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) + // ----------------------------------- + + // Check if target is a Smi. + Label non_constructor, non_proxy; + __ JumpIfSmi(a1, &non_constructor); + + // Check if target has a [[Construct]] internal method. + __ Ld(t1, FieldMemOperand(a1, HeapObject::kMapOffset)); + __ Lbu(t4, FieldMemOperand(t1, Map::kBitFieldOffset)); + __ And(t4, t4, Operand(Map::Bits1::IsConstructorBit::kMask)); + __ Branch(&non_constructor, eq, t4, Operand(zero_reg)); + + // Dispatch based on instance type. + __ Lhu(t2, FieldMemOperand(t1, Map::kInstanceTypeOffset)); + __ Jump(BUILTIN_CODE(masm->isolate(), ConstructFunction), + RelocInfo::CODE_TARGET, eq, t2, Operand(JS_FUNCTION_TYPE)); + + // Only dispatch to bound functions after checking whether they are + // constructors. + __ Jump(BUILTIN_CODE(masm->isolate(), ConstructBoundFunction), + RelocInfo::CODE_TARGET, eq, t2, Operand(JS_BOUND_FUNCTION_TYPE)); + + // Only dispatch to proxies after checking whether they are constructors. + __ Branch(&non_proxy, ne, t2, 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. + __ CalcScaledAddress(kScratchReg, sp, a0, kPointerSizeLog2); + __ Sd(a1, MemOperand(kScratchReg)); + // Let the "call_as_constructor_delegate" take care of the rest. + __ LoadNativeContextSlot(Context::CALL_AS_CONSTRUCTOR_DELEGATE_INDEX, a1); + __ 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); +} + +void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { + // State setup as expected by MacroAssembler::InvokePrologue. + // ----------- S t a t e ------------- + // -- a0: actual arguments count + // -- a1: function (passed through to callee) + // -- a2: expected arguments count + // -- a3: new target (passed through to callee) + // ----------------------------------- + + Label invoke, dont_adapt_arguments, stack_overflow; + + Label enough, too_few; + __ Branch(&dont_adapt_arguments, eq, a2, + Operand(kDontAdaptArgumentsSentinel)); + // We use Uless as the number of argument should always be greater than 0. + __ Branch(&too_few, Uless, a0, Operand(a2)); + + { // Enough parameters: actual >= expected. + // a0: actual number of arguments as a smi + // a1: function + // a2: expected number of arguments + // a3: new target (passed through to callee) + __ bind(&enough); + EnterArgumentsAdaptorFrame(masm); + Generate_StackOverflowCheck(masm, a2, a5, kScratchReg, &stack_overflow); + + // Calculate copy start address into a0 and copy end address into a4. + __ SmiScale(a0, a0, kPointerSizeLog2); + __ Add64(a0, fp, a0); + // Adjust for return address and receiver. + __ Add64(a0, a0, Operand(2 * kPointerSize)); + // Compute copy end address. + __ Sll64(a4, a2, kPointerSizeLog2); + __ Sub64(a4, a0, a4); + + // Copy the arguments (including the receiver) to the new stack frame. + // a0: copy start address + // a1: function + // a2: expected number of arguments + // a3: new target (passed through to callee) + // a4: copy end address + + Label copy; + __ bind(©); + __ Ld(a5, MemOperand(a0)); + __ push(a5); + __ Add64(a0, a0, -kPointerSize); + __ Branch(©, ge, a0, Operand(a4)); + + __ Branch(&invoke); + } + + { // Too few parameters: Actual < expected. + __ bind(&too_few); + EnterArgumentsAdaptorFrame(masm); + Generate_StackOverflowCheck(masm, a2, a5, kScratchReg, &stack_overflow); + + // Calculate copy start address into a0 and copy end address into a7. + // a0: actual number of arguments as a smi + // a1: function + // a2: expected number of arguments + // a3: new target (passed through to callee) + __ SmiScale(a0, a0, kPointerSizeLog2); + __ Add64(a0, fp, a0); + // Adjust for return address and receiver. + __ Add64(a0, a0, Operand(2 * kPointerSize)); + // Compute copy end address. Also adjust for return address. + __ Add64(a7, fp, kPointerSize); + + // Copy the arguments (including the receiver) to the new stack frame. + // a0: copy start address + // a1: function + // a2: expected number of arguments + // a3: new target (passed through to callee) + // a7: copy end address + Label copy; + __ bind(©); + __ Ld(a4, MemOperand(a0)); // Adjusted above for return addr and receiver. + __ Sub64(sp, sp, kPointerSize); + __ Sub64(a0, a0, kPointerSize); + __ Sd(a4, MemOperand(sp)); + __ Branch(©, ne, a0, Operand(a7)); + + // Fill the remaining expected arguments with undefined. + // a1: function + // a2: expected number of arguments + // a3: new target (passed through to callee) + __ LoadRoot(a5, RootIndex::kUndefinedValue); + __ Sll64(a6, a2, kPointerSizeLog2); + __ Sub64(a4, fp, Operand(a6)); + // Adjust for frame. + __ Sub64(a4, a4, + Operand(ArgumentsAdaptorFrameConstants::kFixedFrameSizeFromFp + + kPointerSize)); + + Label fill; + __ bind(&fill); + __ Sub64(sp, sp, kPointerSize); + __ Sd(a5, MemOperand(sp)); + __ Branch(&fill, ne, sp, Operand(a4)); + } + + // Call the entry point. + __ bind(&invoke); + __ Move(a0, a2); + // a0 : expected number of arguments + // a1 : function (passed through to callee) + // a3: new target (passed through to callee) + static_assert(kJavaScriptCallCodeStartRegister == a2, "ABI mismatch"); + __ Ld(a2, FieldMemOperand(a1, JSFunction::kCodeOffset)); + __ Add64(a2, a2, Operand(Code::kHeaderSize - kHeapObjectTag)); + __ Call(a2); + + // Store offset of return address for deoptimizer. + masm->isolate()->heap()->SetArgumentsAdaptorDeoptPCOffset(masm->pc_offset()); + + // Exit frame and return. + LeaveArgumentsAdaptorFrame(masm); + __ Ret(); + + // ------------------------------------------- + // Don't adapt arguments. + // ------------------------------------------- + __ bind(&dont_adapt_arguments); + static_assert(kJavaScriptCallCodeStartRegister == a2, "ABI mismatch"); + __ Ld(a2, FieldMemOperand(a1, JSFunction::kCodeOffset)); + __ Add64(a2, a2, Operand(Code::kHeaderSize - kHeapObjectTag)); + __ Jump(a2); + + __ bind(&stack_overflow); + { + FrameScope frame(masm, StackFrame::MANUAL); + __ CallRuntime(Runtime::kThrowStackOverflow); + __ break_(0xCC); + } +} + +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); + { + HardAbortScope hard_abort(masm); // Avoid calls to Abort. + FrameScope scope(masm, StackFrame::WASM_COMPILE_LAZY); + + // Save all parameter registers (see kGpParamRegisters in wasm-linkage.cc). + // They might be overwritten in the runtime call below. We don't have any + // callee-saved registers in wasm, so no need to store anything else. + constexpr RegList gp_regs = Register::ListOf(a0, a2, a3, a4, a5, a6, a7); + constexpr RegList fp_regs = + DoubleRegister::ListOf(fa0, fa1, fa2, fa3, fa4, fa5, fa6); + static_assert(WasmCompileLazyFrameConstants::kNumberOfSavedGpParamRegs == + arraysize(wasm::kGpParamRegisters), + "frame size mismatch"); + static_assert(WasmCompileLazyFrameConstants::kNumberOfSavedFpParamRegs == + arraysize(wasm::kFpParamRegisters), + "frame size mismatch"); + __ MultiPush(gp_regs); + __ MultiPushFPU(fp_regs); + + // Pass instance and function index as an explicit arguments to the runtime + // function. + __ 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); + + __ Move(s1, a0); // move return value to s1 since a0 will be restored to + // the value before the call + + // Restore registers. + __ MultiPopFPU(fp_regs); + __ MultiPop(gp_regs); + } + // Finally, jump to the entrypoint. + __ Jump(s1); +} + +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_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 == kArgvInRegister: + // a2: pointer to the first argument + + if (argv_mode == kArgvInRegister) { + // Move argv into the correct register. + __ Move(s1, a2); + } else { + // Compute the argv pointer in a callee-saved register. + __ CalcScaledAddress(s1, sp, a0, kPointerSizeLog2); + __ Sub64(s1, s1, kPointerSize); + } + + // Enter the exit frame that transitions from JavaScript to C++. + FrameScope scope(masm, StackFrame::MANUAL); + __ EnterExitFrame( + save_doubles == kSaveFPRegs, 0, + builtin_exit_frame ? StackFrame::BUILTIN_EXIT : StackFrame::EXIT); + + // s3: 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 + __ Move(s3, a0); + __ Move(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())); + __ Move(a1, s1); + + __ StoreReturnAddressAndCall(s2); + + // Result returned in a0 or a1:a0 - do not destroy these registers! + + // Check result for exception sentinel. + Label exception_returned; + __ LoadRoot(a4, RootIndex::kException); + __ Branch(&exception_returned, eq, a4, Operand(a0)); + + // 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); + __ Ld(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)); + __ stop(); + __ bind(&okay); + } + + // Exit C frame and return. + // a0:a1: result + // sp: stack pointer + // fp: frame pointer + Register argc = argv_mode == kArgvInRegister + // We don't want to pop arguments so set argc to no_reg. + ? no_reg + // s3: still holds argc (callee-saved). + : s3; + __ LeaveExitFrame(save_doubles == kSaveFPRegs, 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 a0 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); + __ Move(a0, zero_reg); + __ Move(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); + __ Ld(cp, MemOperand(cp)); + __ li(sp, pending_handler_sp_address); + __ Ld(sp, MemOperand(sp)); + __ li(fp, pending_handler_fp_address); + __ Ld(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)); + __ Sd(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); + __ bind(&zero); + + // Reset the masking register. This is done independent of the underlying + // feature flag {FLAG_untrusted_code_mitigations} to make the snapshot work + // with both configurations. It is safe to always do this, because the + // underlying register is caller-saved and can be arbitrarily clobbered. + __ ResetSpeculationPoisonRegister(); + + // Compute the handler entry address and jump to it. + __ li(t6, pending_handler_entrypoint_address); + __ Ld(t6, MemOperand(t6)); + __ Jump(t6); +} + +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); + DoubleRegister double_scratch = kScratchDoubleReg; + + // Account for saved regs. + const int kArgumentOffset = 4 * kPointerSize; + + __ Push(result_reg); + __ Push(scratch, scratch2, scratch3); + + // Load double input. + __ LoadDouble(double_scratch, MemOperand(sp, kArgumentOffset)); + + // Try a conversion to a signed integer, if exception occurs, scratch is + // set to 0 + __ Trunc_w_d(scratch3, double_scratch, scratch); + + // If we had no exceptions then set result_reg and we are done. + Label error; + __ Branch(&error, eq, scratch, Operand(zero_reg)); + __ Move(result_reg, scratch3); + __ Branch(&done); + __ bind(&error); + + // Load the double value and perform a manual truncation. + Register input_high = scratch2; + Register input_low = scratch3; + + __ Lw(input_low, MemOperand(sp, kArgumentOffset + Register::kMantissaOffset)); + __ Lw(input_high, + MemOperand(sp, kArgumentOffset + Register::kExponentOffset)); + + Label normal_exponent; + // Extract the biased exponent in result. + __ ExtractBits(result_reg, input_high, HeapNumber::kExponentShift, + HeapNumber::kExponentBits); + + // Check for Infinity and NaNs, which should return 0. + __ Sub32(scratch, result_reg, HeapNumber::kExponentMask); + __ LoadZeroIfConditionZero( + result_reg, + scratch); // result_reg = scratch == 0 ? 0 : result_reg + __ Branch(&done, eq, scratch, Operand(zero_reg)); + + // Express exponent as delta to (number of mantissa bits + 31). + __ Sub32(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)); + __ Move(result_reg, zero_reg); + __ Branch(&done); + + __ bind(&normal_exponent); + const int kShiftBase = HeapNumber::kNonMantissaBitsInTopWord - 1; + // Calculate shift. + __ Add32(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)); + + // We must specially handle shifts greater than 31. + Label high_shift_needed, high_shift_done; + __ Branch(&high_shift_needed, lt, scratch, Operand(32)); + __ Move(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. + __ Sll32(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, sign_negative; + __ li(kScratchReg, 32); + __ subw(scratch, kScratchReg, scratch); + __ Branch(&pos_shift, ge, scratch, Operand(zero_reg)); + + // Negate scratch. + __ Sub32(scratch, zero_reg, scratch); + __ Sll32(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. + __ Move(scratch, sign); + result_reg = sign; + sign = no_reg; + __ Sub32(result_reg, zero_reg, input_high); + __ Branch(&sign_negative, ne, scratch, Operand(zero_reg)); + __ Move(result_reg, input_high); + __ bind(&sign_negative); + + __ bind(&done); + + __ Sd(result_reg, MemOperand(sp, kArgumentOffset)); + __ Pop(scratch, scratch2, scratch3); + __ Pop(result_reg); + __ Ret(); +} + +void Builtins::Generate_GenericJSToWasmWrapper(MacroAssembler* masm) { + // TODO(v8:10701): Implement for this platform. + __ Trap(); +} + +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(t6, ExternalReference::is_profiling_address(isolate)); + __ Lb(t6, MemOperand(t6, 0)); + __ Branch(&profiler_enabled, ne, t6, Operand(zero_reg)); + __ li(t6, ExternalReference::address_of_runtime_stats_flag()); + __ Lw(t6, MemOperand(t6, 0)); + __ Branch(&profiler_enabled, ne, t6, Operand(zero_reg)); + { + // Call the api function directly. + __ Move(t6, function_address); + __ Branch(&end_profiler_check); + } + + __ bind(&profiler_enabled); + { + // Additional parameter is the address of the actual callback. + __ li(t6, thunk_ref); + } + __ bind(&end_profiler_check); + + // Allocate HandleScope in callee-save registers. + __ li(s5, next_address); + __ Ld(s3, MemOperand(s5, kNextOffset)); + __ Ld(s1, MemOperand(s5, kLimitOffset)); + __ Lw(s2, MemOperand(s5, kLevelOffset)); + __ Add32(s2, s2, Operand(1)); + __ Sw(s2, MemOperand(s5, kLevelOffset)); + + __ StoreReturnAddressAndCall(t6); + + Label promote_scheduled_exception; + Label delete_allocated_handles; + Label leave_exit_frame; + Label return_value_loaded; + + // Load value from ReturnValue. + __ Ld(a0, return_value_operand); + __ bind(&return_value_loaded); + + // No more valid handles (the result handle was the last one). Restore + // previous handle scope. + __ Sd(s3, MemOperand(s5, kNextOffset)); + if (__ emit_debug_code()) { + __ Lw(a1, MemOperand(s5, kLevelOffset)); + __ Check(eq, AbortReason::kUnexpectedLevelAfterReturnFromApiCall, a1, + Operand(s2)); + } + __ Sub32(s2, s2, Operand(1)); + __ Sw(s2, MemOperand(s5, kLevelOffset)); + __ Ld(kScratchReg, MemOperand(s5, 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(s3, Operand(stack_space)); + } else { + DCHECK_EQ(stack_space, 0); + STATIC_ASSERT(kCArgSlotCount == 0); + __ Ld(s3, *stack_space_operand); + } + + static constexpr bool kDontSaveDoubles = false; + static constexpr bool kRegisterContainsSlotCount = false; + __ LeaveExitFrame(kDontSaveDoubles, s3, NO_EMIT_RETURN, + kRegisterContainsSlotCount); + + // Check if the function scheduled an exception. + __ LoadRoot(a4, RootIndex::kTheHoleValue); + __ li(kScratchReg, ExternalReference::scheduled_exception_address(isolate)); + __ Ld(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); + __ Sd(s1, MemOperand(s5, kLimitOffset)); + __ Move(s3, a0); + __ PrepareCallCFunction(1, s1); + __ li(a0, ExternalReference::isolate_address(isolate)); + __ CallCFunction(ExternalReference::delete_handle_scope_extensions(), 1); + __ Move(a0, s3); + __ Branch(&leave_exit_frame); +} + +} // namespace + +void Builtins::Generate_CallApiCallback(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- cp : context + // -- a1 : api function address + // -- a2 : arguments count (not including the receiver) + // -- a3 : call data + // -- a0 : holder + // -- + // -- sp[0] : last argument + // -- ... + // -- sp[(argc - 1) * 8] : first argument + // -- sp[(argc + 0) * 8] : receiver + // ----------------------------------- + + 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). + __ CalcScaledAddress(base, sp, argc, kPointerSizeLog2); + + // Reserve space on the stack. + __ Sub64(sp, sp, Operand(FCA::kArgsLength * kPointerSize)); + + // kHolder. + __ Sd(holder, MemOperand(sp, 0 * kPointerSize)); + + // kIsolate. + __ li(scratch, ExternalReference::isolate_address(masm->isolate())); + __ Sd(scratch, MemOperand(sp, 1 * kPointerSize)); + + // kReturnValueDefaultValue and kReturnValue. + __ LoadRoot(scratch, RootIndex::kUndefinedValue); + __ Sd(scratch, MemOperand(sp, 2 * kPointerSize)); + __ Sd(scratch, MemOperand(sp, 3 * kPointerSize)); + + // kData. + __ Sd(call_data, MemOperand(sp, 4 * kPointerSize)); + + // kNewTarget. + __ Sd(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. + __ Move(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()). + __ Sd(scratch, MemOperand(sp, 1 * kPointerSize)); + + // FunctionCallbackInfo::values_ (points at the first varargs argument passed + // on the stack). + __ Sub64(scratch, base, Operand(1 * kPointerSize)); + __ Sd(scratch, MemOperand(sp, 2 * kPointerSize)); + + // FunctionCallbackInfo::length_. + // Stored as int field, 32-bit integers within struct on stack always left + // justified by n64 ABI. + __ Sw(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. + __ Add64(scratch, argc, Operand(FCA::kArgsLength + 1 /* receiver */)); + __ Sd(scratch, MemOperand(sp, 4 * kPointerSize)); + + // v8::InvocationCallback's argument. + DCHECK(!AreAliased(api_function_address, scratch, a0)); + __ Add64(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; + __ Sub64(sp, sp, (PCA::kArgsLength + 1) * kPointerSize); + __ Sd(receiver, MemOperand(sp, (PCA::kThisIndex + 1) * kPointerSize)); + __ Ld(scratch, FieldMemOperand(callback, AccessorInfo::kDataOffset)); + __ Sd(scratch, MemOperand(sp, (PCA::kDataIndex + 1) * kPointerSize)); + __ LoadRoot(scratch, RootIndex::kUndefinedValue); + __ Sd(scratch, MemOperand(sp, (PCA::kReturnValueOffset + 1) * kPointerSize)); + __ Sd(scratch, MemOperand(sp, (PCA::kReturnValueDefaultValueIndex + 1) * + kPointerSize)); + __ li(scratch, ExternalReference::isolate_address(masm->isolate())); + __ Sd(scratch, MemOperand(sp, (PCA::kIsolateIndex + 1) * kPointerSize)); + __ Sd(holder, MemOperand(sp, (PCA::kHolderIndex + 1) * kPointerSize)); + // should_throw_on_error -> false + DCHECK_EQ(0, Smi::zero().ptr()); + __ Sd(zero_reg, + MemOperand(sp, (PCA::kShouldThrowOnErrorIndex + 1) * kPointerSize)); + __ Ld(scratch, FieldMemOperand(callback, AccessorInfo::kNameOffset)); + __ Sd(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. + __ Move(a0, sp); // a0 = Handle + __ Add64(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. + __ Sd(a1, MemOperand(sp, 1 * kPointerSize)); + __ Add64(a1, sp, Operand(1 * kPointerSize)); + // a1 = v8::PropertyCallbackInfo& + + ExternalReference thunk_ref = + ExternalReference::invoke_accessor_getter_callback(); + + __ Ld(scratch, FieldMemOperand(callback, AccessorInfo::kJsGetterOffset)); + __ Ld(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. + __ Add64(sp, sp, -kCArgsSlotsSize); + + __ Sd(ra, MemOperand(sp, kCArgsSlotsSize)); // Store the return address. + __ Call(t6); // Call the C++ function. + __ Ld(t6, 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. + __ Uld(a4, MemOperand(t6)); + __ Assert(ne, AbortReason::kReceivedInvalidReturnAddress, a4, + Operand(reinterpret_cast(kZapValue))); + } + + __ Jump(t6); +} + +#undef __ + +} // namespace internal +} // namespace v8 + +#endif // V8_TARGET_ARCH_RISCV64 Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/codegen/assembler-arch.h =================================================================== --- qtwebengine-everywhere-src-5.15.3.orig/src/3rdparty/chromium/v8/src/codegen/assembler-arch.h +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/codegen/assembler-arch.h @@ -23,6 +23,10 @@ #include "src/codegen/mips64/assembler-mips64.h" #elif V8_TARGET_ARCH_S390 #include "src/codegen/s390/assembler-s390.h" +#elif V8_TARGET_ARCH_RISCV64 +#include "src/codegen/riscv64/assembler-riscv64.h" +#elif V8_TARGET_ARCH_RISCV +#include "src/codegen/riscv/assembler-riscv.h" #else #error Unknown architecture. #endif Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/codegen/assembler-inl.h =================================================================== --- qtwebengine-everywhere-src-5.15.3.orig/src/3rdparty/chromium/v8/src/codegen/assembler-inl.h +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/codegen/assembler-inl.h @@ -23,6 +23,10 @@ #include "src/codegen/mips64/assembler-mips64-inl.h" #elif V8_TARGET_ARCH_S390 #include "src/codegen/s390/assembler-s390-inl.h" +#elif V8_TARGET_ARCH_RISCV64 +#include "src/codegen/riscv64/assembler-riscv64-inl.h" +#elif V8_TARGET_ARCH_RISCV +#include "src/codegen/riscv/assembler-riscv-inl.h" #else #error Unknown architecture. #endif Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/codegen/constants-arch.h =================================================================== --- qtwebengine-everywhere-src-5.15.3.orig/src/3rdparty/chromium/v8/src/codegen/constants-arch.h +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/codegen/constants-arch.h @@ -21,6 +21,10 @@ #include "src/codegen/s390/constants-s390.h" // NOLINT #elif V8_TARGET_ARCH_X64 #include "src/codegen/x64/constants-x64.h" // NOLINT +#elif V8_TARGET_ARCH_RISCV64 +#include "src/codegen/riscv64/constants-riscv64.h" // NOLINT +#elif V8_TARGET_ARCH_RISCV +#include "src/codegen/riscv64/constants-riscv.h" // NOLINT #else #error Unsupported target architecture. #endif Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/codegen/cpu-features.h =================================================================== --- qtwebengine-everywhere-src-5.15.3.orig/src/3rdparty/chromium/v8/src/codegen/cpu-features.h +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/codegen/cpu-features.h @@ -67,6 +67,12 @@ enum CpuFeature { VECTOR_ENHANCE_FACILITY_1, VECTOR_ENHANCE_FACILITY_2, MISC_INSTR_EXT2, + +// FIXME (RISCV): add features for RISCV +#elif V8_TARGET_ARCH_RISCV64 + FPU, + FP64FPU, + RISCV_SIMD, #endif NUMBER_OF_CPU_FEATURES Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/codegen/external-reference.cc =================================================================== --- qtwebengine-everywhere-src-5.15.3.orig/src/3rdparty/chromium/v8/src/codegen/external-reference.cc +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/codegen/external-reference.cc @@ -502,6 +502,8 @@ ExternalReference ExternalReference::inv #define re_stack_check_func RegExpMacroAssemblerMIPS::CheckStackGuardState #elif V8_TARGET_ARCH_S390 #define re_stack_check_func RegExpMacroAssemblerS390::CheckStackGuardState +#elif V8_TARGET_ARCH_RISCV64 || V8_TARGET_ARCH_RISCV +#define re_stack_check_func RegExpMacroAssemblerRISCV::CheckStackGuardState #else UNREACHABLE(); #endif Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/codegen/interface-descriptors.cc =================================================================== --- qtwebengine-everywhere-src-5.15.3.orig/src/3rdparty/chromium/v8/src/codegen/interface-descriptors.cc +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/codegen/interface-descriptors.cc @@ -397,7 +397,9 @@ void WasmFloat64ToNumberDescriptor::Init } #endif // !V8_TARGET_ARCH_IA32 -#if !defined(V8_TARGET_ARCH_MIPS) && !defined(V8_TARGET_ARCH_MIPS64) +// FIXME(RISCV): Review this once atomics are added +#if !defined(V8_TARGET_ARCH_MIPS) && !defined(V8_TARGET_ARCH_MIPS64) && \ + !defined(V8_TARGET_ARCH_RISCV64) && !defined(V8_TARGET_ARCH_RISCV) void WasmI32AtomicWait32Descriptor::InitializePlatformSpecific( CallInterfaceDescriptorData* data) { DefaultInitializePlatformSpecific(data, kParameterCount); Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/codegen/macro-assembler.h =================================================================== --- qtwebengine-everywhere-src-5.15.3.orig/src/3rdparty/chromium/v8/src/codegen/macro-assembler.h +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/codegen/macro-assembler.h @@ -52,6 +52,12 @@ enum AllocationFlags { #elif V8_TARGET_ARCH_S390 #include "src/codegen/s390/constants-s390.h" #include "src/codegen/s390/macro-assembler-s390.h" +#elif V8_TARGET_ARCH_RISCV64 +#include "src/codegen/riscv64/constants-riscv64.h" +#include "src/codegen/riscv64/macro-assembler-riscv64.h" +#elif V8_TARGET_ARCH_RISCV +#include "src/codegen/riscv/constants-riscv.h" +#include "src/codegen/riscv/macro-assembler-riscv.h" #else #error Unsupported target architecture. #endif Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/codegen/register-arch.h =================================================================== --- qtwebengine-everywhere-src-5.15.3.orig/src/3rdparty/chromium/v8/src/codegen/register-arch.h +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/codegen/register-arch.h @@ -24,6 +24,10 @@ #include "src/codegen/mips64/register-mips64.h" #elif V8_TARGET_ARCH_S390 #include "src/codegen/s390/register-s390.h" +#elif V8_TARGET_ARCH_RISCV64 +#include "src/codegen/riscv64/register-riscv64.h" +#elif V8_TARGET_ARCH_RISCV +#include "src/codegen/riscv/register-riscv.h" #else #error Unknown architecture. #endif Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/codegen/register-configuration.cc =================================================================== --- qtwebengine-everywhere-src-5.15.3.orig/src/3rdparty/chromium/v8/src/codegen/register-configuration.cc +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/codegen/register-configuration.cc @@ -64,6 +64,8 @@ static int get_num_allocatable_double_re kMaxAllocatableDoubleRegisterCount; #elif V8_TARGET_ARCH_S390 kMaxAllocatableDoubleRegisterCount; +#elif V8_TARGET_ARCH_RISCV64 || V8_TARGET_ARCH_RISCV + kMaxAllocatableDoubleRegisterCount; #else #error Unsupported target architecture. #endif Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/codegen/reloc-info.cc =================================================================== --- qtwebengine-everywhere-src-5.15.3.orig/src/3rdparty/chromium/v8/src/codegen/reloc-info.cc +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/codegen/reloc-info.cc @@ -330,7 +330,8 @@ bool RelocInfo::OffHeapTargetIsCodedSpec return false; #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_PPC64) || defined(V8_TARGET_ARCH_S390) || \ + defined(V8_TARGET_ARCH_RISCV64) || defined(V8_TARGET_ARCH_RISCV) return true; #endif } Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/codegen/reloc-info.h =================================================================== --- qtwebengine-everywhere-src-5.15.3.orig/src/3rdparty/chromium/v8/src/codegen/reloc-info.h +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/codegen/reloc-info.h @@ -69,7 +69,7 @@ class RelocInfo { EXTERNAL_REFERENCE, // The address of an external C++ function. INTERNAL_REFERENCE, // An address inside the same function. - // Encoded internal reference, used only on MIPS, MIPS64 and PPC. + // Encoded internal reference, used only on RISCV64, MIPS, MIPS64 and PPC. INTERNAL_REFERENCE_ENCODED, // An off-heap instruction stream target. See http://goo.gl/Z2HUiM. Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/codegen/riscv64/assembler-riscv64-inl.h =================================================================== --- /dev/null +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/codegen/riscv64/assembler-riscv64-inl.h @@ -0,0 +1,247 @@ + +// 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_RISCV_ASSEMBLER_RISCV_INL_H_ +#define V8_CODEGEN_RISCV_ASSEMBLER_RISCV_INL_H_ + +#include "src/codegen/assembler.h" +#include "src/codegen/riscv64/assembler-riscv64.h" +#include "src/debug/debug.h" +#include "src/objects/objects-inl.h" + +namespace v8 { +namespace internal { + +bool CpuFeatures::SupportsOptimizer() { return IsSupported(FPU); } + +bool CpuFeatures::SupportsWasmSimd128() { return IsSupported(RISCV_SIMD); } + +// ----------------------------------------------------------------------------- +// 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) { + set_target_value_at(pc, static_cast(target)); +} + +void Assembler::deserialization_set_target_internal_reference_at( + Address pc, Address target, RelocInfo::Mode mode) { + if (RelocInfo::IsInternalReferenceEncoded(mode)) { + DCHECK(IsLui(instr_at(pc))); + set_target_internal_reference_encoded_at(pc, target); + } else { + DCHECK(RelocInfo::IsInternalReference(mode)); + Memory
(pc) = target; + } +} + +HeapObject RelocInfo::target_object() { + DCHECK(IsCodeTarget(rmode_) || IsFullEmbeddedObject(rmode_)); + return HeapObject::cast( + Object(Assembler::target_address_at(pc_, constant_pool_))); +} + +HeapObject RelocInfo::target_object_no_host(Isolate* isolate) { + return target_object(); +} + +Handle RelocInfo::target_object_handle(Assembler* origin) { + 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_)); + 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); + DCHECK(Assembler::IsLui(Assembler::instr_at(pc_ + 0 * kInstrSize))); + Address address = Assembler::target_address_at(pc_); + return address; + } +} + +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(); + } +} + +template +void Assembler::EmitHelper(T x) { + DEBUG_PRINTF("%p: ", pc_); + disassembleInstr((int)x); + *reinterpret_cast(pc_) = x; + pc_ += sizeof(x); + CheckTrampolinePoolQuick(); +} + +void Assembler::emit(Instr x) { + if (!is_buffer_growth_blocked()) { + CheckBuffer(); + } + EmitHelper(x); +} + +EnsureSpace::EnsureSpace(Assembler* assembler) { assembler->CheckBuffer(); } + +} // namespace internal +} // namespace v8 + +#endif // V8_CODEGEN_RISCV_ASSEMBLER_RISCV_INL_H_ Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/codegen/riscv64/assembler-riscv64.cc =================================================================== --- /dev/null +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/codegen/riscv64/assembler-riscv64.cc @@ -0,0 +1,2342 @@ +// 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. + +#if V8_TARGET_ARCH_RISCV64 + +#include "src/codegen/riscv64/assembler-riscv64.h" + +#include "src/base/cpu.h" +#include "src/codegen/riscv64/assembler-riscv64-inl.h" +#include "src/codegen/safepoint-table.h" +#include "src/codegen/string-constants.h" +#include "src/deoptimizer/deoptimizer.h" +#include "src/diagnostics/disasm.h" +#include "src/diagnostics/disassembler.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; +#ifdef CAN_USE_FPU_INSTRUCTIONS + answer |= 1u << FPU; +#endif // def CAN_USE_FPU_INSTRUCTIONS + + return answer; +} + +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; +} + +void CpuFeatures::PrintTarget() {} +void CpuFeatures::PrintFeatures() {} + +int ToNumber(Register reg) { + DCHECK(reg.is_valid()); + const int kNumbers[] = { + 0, // zero_reg + 1, // ra + 2, // sp + 3, // gp + 4, // tp + 5, // t0 + 6, // t1 + 7, // t2 + 8, // s0/fp + 9, // s1 + 10, // a0 + 11, // a1 + 12, // a2 + 13, // a3 + 14, // a4 + 15, // a5 + 16, // a6 + 17, // a7 + 18, // s2 + 19, // s3 + 20, // s4 + 21, // s5 + 22, // s6 + 23, // s7 + 24, // s8 + 25, // s9 + 26, // s10 + 27, // s11 + 28, // t3 + 29, // t4 + 30, // t5 + 31, // t6 + }; + return kNumbers[reg.code()]; +} + +Register ToRegister(int num) { + DCHECK(num >= 0 && num < kNumRegisters); + const Register kRegisters[] = { + zero_reg, ra, sp, gp, tp, t0, t1, t2, fp, s1, a0, a1, a2, a3, a4, a5, + a6, a7, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, t3, t4, t5, t6}; + return kRegisters[num]; +} + +// ----------------------------------------------------------------------------- +// 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 RISC-V means that it is a lui/addi 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-riscv64-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())); + } +} + +// ----------------------------------------------------------------------------- +// Specific instructions, constants, and masks. + +Assembler::Assembler(const AssemblerOptions& options, + std::unique_ptr buffer) + : AssemblerBase(options, std::move(buffer)), + scratch_register_list_(t3.bit()) { + 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, + SafepointTableBuilder* safepoint_table_builder, + int handler_table_offset) { + 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->GetCodeOffset(); + 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); +} + +// 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 0, +// which is an otherwise illegal value (branch 0 is inf loop). + +const int kEndOfChain = 0; + +// Determines the end of the Jump chain (a subset of the label link chain). +const int kEndOfJumpChain = 0; + +bool Assembler::IsBranch(Instr instr) { + return (instr & kBaseOpcodeMask) == BRANCH; +} + +bool Assembler::IsJump(Instr instr) { + int Op = instr & kBaseOpcodeMask; + return Op == JAL || Op == JALR; +} + +bool Assembler::IsJal(Instr instr) { return (instr & kBaseOpcodeMask) == JAL; } + +bool Assembler::IsJalr(Instr instr) { + return (instr & kBaseOpcodeMask) == JALR; +} + +bool Assembler::IsLui(Instr instr) { return (instr & kBaseOpcodeMask) == LUI; } +bool Assembler::IsAuipc(Instr instr) { return (instr & kBaseOpcodeMask) == AUIPC; } +bool Assembler::IsAddiw(Instr instr) { + return (instr & (kBaseOpcodeMask | kFunct3Mask)) == RO_ADDIW; +} +bool Assembler::IsAddi(Instr instr) { + return (instr & (kBaseOpcodeMask | kFunct3Mask)) == RO_ADDI; +} +bool Assembler::IsSlli(Instr instr) { + return (instr & (kBaseOpcodeMask | kFunct3Mask)) == RO_SLLI; +} +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); + DEBUG_PRINTF("target_at: %p (%d)\n\t", + reinterpret_cast(buffer_start_ + pos), pos); + disassembleInstr(instr); + if (IsBranch(instr)) { + int32_t imm13 = BranchOffset(instr); + if (imm13 == kEndOfJumpChain) { + // EndOfChain sentinel is returned directly, not relative to pc or pos. + return kEndOfChain; + } else { + return pos + imm13; + } + } else if (IsJal(instr)) { + int32_t imm21 = JumpOffset(instr); + if (imm21 == kEndOfJumpChain) { + // EndOfChain sentinel is returned directly, not relative to pc or pos. + return kEndOfChain; + } else { + return pos + imm21; + } + } else if (IsJalr(instr)) { + int32_t imm12 = instr >> 20; + if (imm12 == kEndOfJumpChain) { + // EndOfChain sentinel is returned directly, not relative to pc or pos. + return kEndOfChain; + } else { + return pos + imm12; + } + } else if (IsLui(instr)) { + Address pc = reinterpret_cast
(buffer_start_ + pos); + pc = target_address_at(pc); + uint64_t instr_address = reinterpret_cast(buffer_start_ + pos); + uint64_t imm = reinterpret_cast(pc); + if(imm == kEndOfJumpChain) { + return kEndOfChain; + } else { + DCHECK(instr_address - imm < INT_MAX); + int32_t delta = static_cast(instr_address - imm); + DCHECK(pos > delta); + return pos - delta; + } + } else if (IsAuipc(instr)) { + Instr instr_auipc = instr; + Instr instr_jalr = instr_at(pos + 4); + DCHECK(IsJalr(instr_jalr)); + int32_t offset = BrachlongOffset(instr_auipc, instr_jalr); + if(offset == kEndOfJumpChain) + return kEndOfChain; + return offset + pos; + } else { + // Emitted label constant, not part of a branch. + if (instr == 0) { + return kEndOfChain; + } else { + int32_t imm18 = ((instr & static_cast(kImm16Mask)) << 16) >> 14; + return (imm18 + pos); + } + } +} + +static inline Instr SetBranchOffset(int32_t pos, int32_t target_pos, + Instr instr) { + int32_t imm = target_pos - pos; + DCHECK_EQ(imm & 1, 0); + DCHECK(is_intn(imm, Assembler::kBranchOffsetBits)); + + instr &= ~kBImm12Mask; + int32_t imm12 = ((imm & 0x800) >> 4) | // bit 11 + ((imm & 0x1e) << 7) | // bits 4-1 + ((imm & 0x7e0) << 20) | // bits 10-5 + ((imm & 0x1000) << 19); // bit 12 + + return instr | (imm12 & kBImm12Mask); +} + +static inline Instr SetJalOffset(int32_t pos, int32_t target_pos, Instr instr) { + int32_t imm = target_pos - pos; + DCHECK_EQ(imm & 1, 0); + DCHECK(is_intn(imm, Assembler::kJumpOffsetBits)); + + instr &= ~kImm20Mask; + int32_t imm20 = (imm & 0xff000) | // bits 19-12 + ((imm & 0x800) << 9) | // bit 11 + ((imm & 0x7fe) << 20) | // bits 10-1 + ((imm & 0x100000) << 11); // bit 20 + + return instr | (imm20 & kImm20Mask); +} + +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; + } + DEBUG_PRINTF("target_at_put: %p (%d) to %p (%d)\n", + reinterpret_cast(buffer_start_ + pos), pos, + reinterpret_cast(buffer_start_ + target_pos), + target_pos); + Instr instr = instr_at(pos); + + if (IsBranch(instr)) { + instr = SetBranchOffset(pos, target_pos, instr); + instr_at_put(pos, instr); + } else if (IsJal(instr)) { + instr = SetJalOffset(pos, target_pos, instr); + instr_at_put(pos, instr); + } else if (IsLui(instr)) { + Address pc = reinterpret_cast
(buffer_start_ + pos); + set_target_value_at(pc, reinterpret_cast(buffer_start_ + target_pos)); + } else if (IsAuipc(instr)) { + Instr instr_auipc = instr; + Instr instr_jalr = instr_at(pos + 4); + DCHECK(IsJalr(instr_jalr)); + + int64_t offset = target_pos - pos; + DCHECK(is_int32(offset)); + + int32_t Hi20 = (((int32_t)offset + 0x800) >> 12); + int32_t Lo12 = (int32_t)offset << 20 >> 20; + + const int kImm31_12Mask = ((1 << 20) - 1) << 12; + const int kImm19_0Mask = ((1 << 20) - 1); + instr_auipc = (instr_auipc & ~kImm31_12Mask) | + ((Hi20 & kImm19_0Mask) << 12); + instr_at_put(pos, instr_auipc); + + const int kImm31_20Mask = ((1 << 12) - 1) << 20; + const int kImm11_0Mask = ((1 << 12) - 1); + instr_jalr = (instr_jalr & ~kImm31_20Mask) | + ((Lo12 & kImm11_0Mask) << 20); + instr_at_put(pos + 4, instr_jalr); + } else { + // 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)); + } + disassembleInstr(instr); +} + +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. + DEBUG_PRINTF("binding %d to label %p\n", pos, L); + 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); + DEBUG_PRINTF("\tfixup: %d to %d\n", fixup_pos, dist); + if (is_internal) { + target_at_put(fixup_pos, pos, is_internal); + } else { + if (IsBranch(instr)) { + if (dist > kMaxBranchOffset) { + if (trampoline_pos == kInvalidSlotPos) { + trampoline_pos = get_trampoline_entry(fixup_pos); + CHECK_NE(trampoline_pos, kInvalidSlotPos); + } + CHECK((trampoline_pos - fixup_pos) <= kMaxBranchOffset); + DEBUG_PRINTF("\t\ttrampolining: %d\n", trampoline_pos); + target_at_put(fixup_pos, trampoline_pos, false); + fixup_pos = trampoline_pos; + } + target_at_put(fixup_pos, pos, false); + } else if (IsJal(instr)) { + if (dist > kMaxJumpOffset) { + if (trampoline_pos == kInvalidSlotPos) { + trampoline_pos = get_trampoline_entry(fixup_pos); + CHECK_NE(trampoline_pos, kInvalidSlotPos); + } + CHECK((trampoline_pos - fixup_pos) <= kMaxJumpOffset); + DEBUG_PRINTF("\t\ttrampolining: %d\n", trampoline_pos); + target_at_put(fixup_pos, trampoline_pos, false); + fixup_pos = trampoline_pos; + } + target_at_put(fixup_pos, pos, false); + } else { + 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_GT(link, 0); + DEBUG_PRINTF("next: %p to %p (%d)\n", L, + reinterpret_cast(buffer_start_ + link), link); + L->link_to(link); + } +} + +bool Assembler::is_near(Label* L) { + DCHECK(L->is_bound()); + return is_intn((pc_offset() - L->pos()), kJumpOffsetBits); +} + +bool Assembler::is_near(Label* L, OffsetSize bits) { + if (L == nullptr || !L->is_bound()) return true; + return is_intn((pc_offset() - L->pos()), bits); +} + +bool Assembler::is_near_branch(Label* L) { + DCHECK(L->is_bound()); + return is_intn((pc_offset() - L->pos()), kBranchOffsetBits); +} + +int Assembler::BranchOffset(Instr instr) { + // | imm[12] | imm[10:5] | rs2 | rs1 | funct3 | imm[4:1|11] | opcode | + // 31 25 11 7 + int32_t imm13 = ((instr & 0xf00) >> 7) | ((instr & 0x7e000000) >> 20) | + ((instr & 0x80) << 4) | ((instr & 0x80000000) >> 19); + imm13 = imm13 << 19 >> 19; + return imm13; +} + +int Assembler::JumpOffset(Instr instr) { + int32_t imm21 = ((instr & 0x7fe00000) >> 20) | ((instr & 0x100000) >> 9) | + (instr & 0xff000) | ((instr & 0x80000000) >> 11); + imm21 = imm21 << 11 >> 11; + return imm21; +} + +int Assembler::BrachlongOffset(Instr auipc, Instr jalr) { + const int kImm19_0Mask = ((1 << 20) - 1); + int32_t imm_auipc = auipc & (kImm19_0Mask << 12); + int32_t imm_jalr = jalr >> 20; + int32_t offset = imm_jalr + imm_auipc; + return offset; +} + +// We have to use a temporary register for things that can be relocated even +// if they can be encoded in RISC-V's 12 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::IsNone(rmode); +} + +void Assembler::disassembleInstr(Instr instr) { + if (!FLAG_debug_riscv) return; + disasm::NameConverter converter; + disasm::Disassembler disasm(converter); + EmbeddedVector disasm_buffer; + + disasm.InstructionDecode(disasm_buffer, reinterpret_cast(&instr)); + DEBUG_PRINTF("%s\n", disasm_buffer.begin()); +} + +// ----- Top-level instruction formats match those in the ISA manual +// (R, I, S, B, U, J). These match the formats defined in the compiler +void Assembler::GenInstrR(uint8_t funct7, uint8_t funct3, Opcode opcode, + Register rd, Register rs1, Register rs2) { + DCHECK(is_uint7(funct7) && is_uint3(funct3) && rd.is_valid() && + rs1.is_valid() && rs2.is_valid()); + Instr instr = opcode | (rd.code() << kRdShift) | (funct3 << kFunct3Shift) | + (rs1.code() << kRs1Shift) | (rs2.code() << kRs2Shift) | + (funct7 << kFunct7Shift); + emit(instr); +} + +void Assembler::GenInstrR(uint8_t funct7, uint8_t funct3, Opcode opcode, + FPURegister rd, FPURegister rs1, FPURegister rs2) { + DCHECK(is_uint7(funct7) && is_uint3(funct3) && rd.is_valid() && + rs1.is_valid() && rs2.is_valid()); + Instr instr = opcode | (rd.code() << kRdShift) | (funct3 << kFunct3Shift) | + (rs1.code() << kRs1Shift) | (rs2.code() << kRs2Shift) | + (funct7 << kFunct7Shift); + emit(instr); +} + +void Assembler::GenInstrR(uint8_t funct7, uint8_t funct3, Opcode opcode, + Register rd, FPURegister rs1, Register rs2) { + DCHECK(is_uint7(funct7) && is_uint3(funct3) && rd.is_valid() && + rs1.is_valid() && rs2.is_valid()); + Instr instr = opcode | (rd.code() << kRdShift) | (funct3 << kFunct3Shift) | + (rs1.code() << kRs1Shift) | (rs2.code() << kRs2Shift) | + (funct7 << kFunct7Shift); + emit(instr); +} + +void Assembler::GenInstrR(uint8_t funct7, uint8_t funct3, Opcode opcode, + FPURegister rd, Register rs1, Register rs2) { + DCHECK(is_uint7(funct7) && is_uint3(funct3) && rd.is_valid() && + rs1.is_valid() && rs2.is_valid()); + Instr instr = opcode | (rd.code() << kRdShift) | (funct3 << kFunct3Shift) | + (rs1.code() << kRs1Shift) | (rs2.code() << kRs2Shift) | + (funct7 << kFunct7Shift); + emit(instr); +} + +void Assembler::GenInstrR(uint8_t funct7, uint8_t funct3, Opcode opcode, + FPURegister rd, FPURegister rs1, Register rs2) { + DCHECK(is_uint7(funct7) && is_uint3(funct3) && rd.is_valid() && + rs1.is_valid() && rs2.is_valid()); + Instr instr = opcode | (rd.code() << kRdShift) | (funct3 << kFunct3Shift) | + (rs1.code() << kRs1Shift) | (rs2.code() << kRs2Shift) | + (funct7 << kFunct7Shift); + emit(instr); +} + +void Assembler::GenInstrR(uint8_t funct7, uint8_t funct3, Opcode opcode, + Register rd, FPURegister rs1, FPURegister rs2) { + DCHECK(is_uint7(funct7) && is_uint3(funct3) && rd.is_valid() && + rs1.is_valid() && rs2.is_valid()); + Instr instr = opcode | (rd.code() << kRdShift) | (funct3 << kFunct3Shift) | + (rs1.code() << kRs1Shift) | (rs2.code() << kRs2Shift) | + (funct7 << kFunct7Shift); + emit(instr); +} + +void Assembler::GenInstrR4(uint8_t funct2, Opcode opcode, Register rd, + Register rs1, Register rs2, Register rs3, + RoundingMode frm) { + DCHECK(is_uint2(funct2) && rd.is_valid() && rs1.is_valid() && + rs2.is_valid() && rs3.is_valid() && is_uint3(frm)); + Instr instr = opcode | (rd.code() << kRdShift) | (frm << kFunct3Shift) | + (rs1.code() << kRs1Shift) | (rs2.code() << kRs2Shift) | + (funct2 << kFunct2Shift) | (rs3.code() << kRs3Shift); + emit(instr); +} + +void Assembler::GenInstrR4(uint8_t funct2, Opcode opcode, FPURegister rd, + FPURegister rs1, FPURegister rs2, FPURegister rs3, + RoundingMode frm) { + DCHECK(is_uint2(funct2) && rd.is_valid() && rs1.is_valid() && + rs2.is_valid() && rs3.is_valid() && is_uint3(frm)); + Instr instr = opcode | (rd.code() << kRdShift) | (frm << kFunct3Shift) | + (rs1.code() << kRs1Shift) | (rs2.code() << kRs2Shift) | + (funct2 << kFunct2Shift) | (rs3.code() << kRs3Shift); + emit(instr); +} + +void Assembler::GenInstrRAtomic(uint8_t funct5, bool aq, bool rl, + uint8_t funct3, Register rd, Register rs1, + Register rs2) { + DCHECK(is_uint5(funct5) && is_uint3(funct3) && rd.is_valid() && + rs1.is_valid() && rs2.is_valid()); + Instr instr = AMO | (rd.code() << kRdShift) | (funct3 << kFunct3Shift) | + (rs1.code() << kRs1Shift) | (rs2.code() << kRs2Shift) | + (rl << kRlShift) | (aq << kAqShift) | (funct5 << kFunct5Shift); + emit(instr); +} + +void Assembler::GenInstrRFrm(uint8_t funct7, Opcode opcode, Register rd, + Register rs1, Register rs2, RoundingMode frm) { + DCHECK(rd.is_valid() && rs1.is_valid() && rs2.is_valid() && is_uint3(frm)); + Instr instr = opcode | (rd.code() << kRdShift) | (frm << kFunct3Shift) | + (rs1.code() << kRs1Shift) | (rs2.code() << kRs2Shift) | + (funct7 << kFunct7Shift); + emit(instr); +} + +void Assembler::GenInstrI(uint8_t funct3, Opcode opcode, Register rd, + Register rs1, int16_t imm12) { + DCHECK(is_uint3(funct3) && rd.is_valid() && rs1.is_valid() && + (is_uint12(imm12) || is_int12(imm12))); + Instr instr = opcode | (rd.code() << kRdShift) | (funct3 << kFunct3Shift) | + (rs1.code() << kRs1Shift) | (imm12 << kImm12Shift); + emit(instr); +} + +void Assembler::GenInstrI(uint8_t funct3, Opcode opcode, FPURegister rd, + Register rs1, int16_t imm12) { + DCHECK(is_uint3(funct3) && rd.is_valid() && rs1.is_valid() && + (is_uint12(imm12) || is_int12(imm12))); + Instr instr = opcode | (rd.code() << kRdShift) | (funct3 << kFunct3Shift) | + (rs1.code() << kRs1Shift) | (imm12 << kImm12Shift); + emit(instr); +} + +void Assembler::GenInstrIShift(bool arithshift, uint8_t funct3, Opcode opcode, + Register rd, Register rs1, uint8_t shamt) { + DCHECK(is_uint3(funct3) && rd.is_valid() && rs1.is_valid() && + is_uint6(shamt)); + Instr instr = opcode | (rd.code() << kRdShift) | (funct3 << kFunct3Shift) | + (rs1.code() << kRs1Shift) | (shamt << kShamtShift) | + (arithshift << kArithShiftShift); + emit(instr); +} + +void Assembler::GenInstrIShiftW(bool arithshift, uint8_t funct3, Opcode opcode, + Register rd, Register rs1, uint8_t shamt) { + DCHECK(is_uint3(funct3) && rd.is_valid() && rs1.is_valid() && + is_uint5(shamt)); + Instr instr = opcode | (rd.code() << kRdShift) | (funct3 << kFunct3Shift) | + (rs1.code() << kRs1Shift) | (shamt << kShamtWShift) | + (arithshift << kArithShiftShift); + emit(instr); +} + +void Assembler::GenInstrS(uint8_t funct3, Opcode opcode, Register rs1, + Register rs2, int16_t imm12) { + DCHECK(is_uint3(funct3) && rs1.is_valid() && rs2.is_valid() && + is_int12(imm12)); + Instr instr = opcode | ((imm12 & 0x1f) << 7) | // bits 4-0 + (funct3 << kFunct3Shift) | (rs1.code() << kRs1Shift) | + (rs2.code() << kRs2Shift) | + ((imm12 & 0xfe0) << 20); // bits 11-5 + emit(instr); +} + +void Assembler::GenInstrS(uint8_t funct3, Opcode opcode, Register rs1, + FPURegister rs2, int16_t imm12) { + DCHECK(is_uint3(funct3) && rs1.is_valid() && rs2.is_valid() && + is_int12(imm12)); + Instr instr = opcode | ((imm12 & 0x1f) << 7) | // bits 4-0 + (funct3 << kFunct3Shift) | (rs1.code() << kRs1Shift) | + (rs2.code() << kRs2Shift) | + ((imm12 & 0xfe0) << 20); // bits 11-5 + emit(instr); +} + +void Assembler::GenInstrB(uint8_t funct3, Opcode opcode, Register rs1, + Register rs2, int16_t imm13) { + DCHECK(is_uint3(funct3) && rs1.is_valid() && rs2.is_valid() && + is_int13(imm13) && ((imm13 & 1) == 0)); + Instr instr = opcode | ((imm13 & 0x800) >> 4) | // bit 11 + ((imm13 & 0x1e) << 7) | // bits 4-1 + (funct3 << kFunct3Shift) | (rs1.code() << kRs1Shift) | + (rs2.code() << kRs2Shift) | + ((imm13 & 0x7e0) << 20) | // bits 10-5 + ((imm13 & 0x1000) << 19); // bit 12 + emit(instr); +} + +void Assembler::GenInstrU(Opcode opcode, Register rd, int32_t imm20) { + DCHECK(rd.is_valid() && (is_int20(imm20) || is_uint20(imm20))); + Instr instr = opcode | (rd.code() << kRdShift) | (imm20 << kImm20Shift); + emit(instr); +} + +void Assembler::GenInstrJ(Opcode opcode, Register rd, int32_t imm21) { + DCHECK(rd.is_valid() && is_int21(imm21) && ((imm21 & 1) == 0)); + Instr instr = opcode | (rd.code() << kRdShift) | + (imm21 & 0xff000) | // bits 19-12 + ((imm21 & 0x800) << 9) | // bit 11 + ((imm21 & 0x7fe) << 20) | // bits 10-1 + ((imm21 & 0x100000) << 11); // bit 20 + emit(instr); +} + +// ----- Instruction class templates match those in the compiler + +void Assembler::GenInstrBranchCC_rri(uint8_t funct3, Register rs1, Register rs2, + int16_t imm13) { + GenInstrB(funct3, BRANCH, rs1, rs2, imm13); +} + +void Assembler::GenInstrLoad_ri(uint8_t funct3, Register rd, Register rs1, + int16_t imm12) { + GenInstrI(funct3, LOAD, rd, rs1, imm12); +} + +void Assembler::GenInstrStore_rri(uint8_t funct3, Register rs1, Register rs2, + int16_t imm12) { + GenInstrS(funct3, STORE, rs1, rs2, imm12); +} + +void Assembler::GenInstrALU_ri(uint8_t funct3, Register rd, Register rs1, + int16_t imm12) { + GenInstrI(funct3, OP_IMM, rd, rs1, imm12); +} + +void Assembler::GenInstrShift_ri(bool arithshift, uint8_t funct3, Register rd, + Register rs1, uint8_t shamt) { + DCHECK(is_uint6(shamt)); + GenInstrI(funct3, OP_IMM, rd, rs1, (arithshift << 10) | shamt); +} + +void Assembler::GenInstrALU_rr(uint8_t funct7, uint8_t funct3, Register rd, + Register rs1, Register rs2) { + GenInstrR(funct7, funct3, OP, rd, rs1, rs2); +} + +void Assembler::GenInstrCSR_ir(uint8_t funct3, Register rd, + ControlStatusReg csr, Register rs1) { + GenInstrI(funct3, SYSTEM, rd, rs1, csr); +} + +void Assembler::GenInstrCSR_ii(uint8_t funct3, Register rd, + ControlStatusReg csr, uint8_t imm5) { + GenInstrI(funct3, SYSTEM, rd, ToRegister(imm5), csr); +} + +void Assembler::GenInstrShiftW_ri(bool arithshift, uint8_t funct3, Register rd, + Register rs1, uint8_t shamt) { + GenInstrIShiftW(arithshift, funct3, OP_IMM_32, rd, rs1, shamt); +} + +void Assembler::GenInstrALUW_rr(uint8_t funct7, uint8_t funct3, Register rd, + Register rs1, Register rs2) { + GenInstrR(funct7, funct3, OP_32, rd, rs1, rs2); +} + +void Assembler::GenInstrPriv(uint8_t funct7, Register rs1, Register rs2) { + GenInstrR(funct7, 0b000, SYSTEM, ToRegister(0), rs1, rs2); +} + +void Assembler::GenInstrLoadFP_ri(uint8_t funct3, FPURegister rd, Register rs1, + int16_t imm12) { + GenInstrI(funct3, LOAD_FP, rd, rs1, imm12); +} + +void Assembler::GenInstrStoreFP_rri(uint8_t funct3, Register rs1, + FPURegister rs2, int16_t imm12) { + GenInstrS(funct3, STORE_FP, rs1, rs2, imm12); +} + +void Assembler::GenInstrALUFP_rr(uint8_t funct7, uint8_t funct3, FPURegister rd, + FPURegister rs1, FPURegister rs2) { + GenInstrR(funct7, funct3, OP_FP, rd, rs1, rs2); +} + +void Assembler::GenInstrALUFP_rr(uint8_t funct7, uint8_t funct3, FPURegister rd, + Register rs1, Register rs2) { + GenInstrR(funct7, funct3, OP_FP, rd, rs1, rs2); +} + +void Assembler::GenInstrALUFP_rr(uint8_t funct7, uint8_t funct3, FPURegister rd, + FPURegister rs1, Register rs2) { + GenInstrR(funct7, funct3, OP_FP, rd, rs1, rs2); +} + +void Assembler::GenInstrALUFP_rr(uint8_t funct7, uint8_t funct3, Register rd, + FPURegister rs1, Register rs2) { + GenInstrR(funct7, funct3, OP_FP, rd, rs1, rs2); +} + +void Assembler::GenInstrALUFP_rr(uint8_t funct7, uint8_t funct3, Register rd, + FPURegister rs1, FPURegister rs2) { + GenInstrR(funct7, funct3, OP_FP, rd, rs1, rs2); +} + +// 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; + DEBUG_PRINTF("jump_address: %p to %p (%d)\n", L, + reinterpret_cast(buffer_start_ + pc_offset()), + pc_offset()); + 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()); + if (!trampoline_emitted_) { + unbound_labels_count_++; + next_buffer_check_ -= kTrampolineSlotsSize; + } + DEBUG_PRINTF("\tstarted link\n"); + return kEndOfJumpChain; + } + } + uint64_t imm = reinterpret_cast(buffer_start_) + target_pos; + DCHECK_EQ(imm & 3, 0); + + return imm; +} + +uint64_t Assembler::branch_long_offset(Label* L) { + int64_t target_pos; + + DEBUG_PRINTF("branch_long_offset: %p to %p (%d)\n", L, + reinterpret_cast(buffer_start_ + pc_offset()), + pc_offset()); + 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()); + if (!trampoline_emitted_) { + unbound_labels_count_++; + next_buffer_check_ -= kTrampolineSlotsSize; + } + DEBUG_PRINTF("\tstarted link\n"); + return kEndOfJumpChain; + } + } + int64_t offset = target_pos - pc_offset(); + DCHECK_EQ(offset & 3, 0); + + return static_cast(offset); +} + +int32_t Assembler::branch_offset_helper(Label* L, OffsetSize bits) { + int32_t target_pos; + + DEBUG_PRINTF("branch_offset_helper: %p to %p (%d)\n", L, + reinterpret_cast(buffer_start_ + pc_offset()), + pc_offset()); + if (L->is_bound()) { + target_pos = L->pos(); + DEBUG_PRINTF("\tbound: %d", target_pos); + } else { + if (L->is_linked()) { + target_pos = L->pos(); + L->link_to(pc_offset()); + DEBUG_PRINTF("\tadded to link: %d\n", target_pos); + } else { + L->link_to(pc_offset()); + if (!trampoline_emitted_) { + unbound_labels_count_++; + next_buffer_check_ -= kTrampolineSlotsSize; + } + DEBUG_PRINTF("\tstarted link\n"); + return kEndOfChain; + } + } + + int32_t offset = target_pos - pc_offset(); + DCHECK(is_intn(offset, bits)); + DCHECK_EQ(offset & 1, 0); + DEBUG_PRINTF("\toffset = %d\n", offset); + return offset; +} + +void Assembler::label_at_put(Label* L, int at_offset) { + int target_pos; + DEBUG_PRINTF("label_at_put: %p @ %p (%d)\n", L, + reinterpret_cast(buffer_start_ + at_offset), at_offset); + 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); + } +} + +//===----------------------------------------------------------------------===// +// Instructions +//===----------------------------------------------------------------------===// + +void Assembler::lui(Register rd, int32_t imm20) { GenInstrU(LUI, rd, imm20); } + +void Assembler::auipc(Register rd, int32_t imm20) { + GenInstrU(AUIPC, rd, imm20); +} + +// Jumps + +void Assembler::jal(Register rd, int32_t imm21) { + BlockTrampolinePoolScope block_trampoline_pool(this); + GenInstrJ(JAL, rd, imm21); + BlockTrampolinePoolFor(1); +} + +void Assembler::jalr(Register rd, Register rs1, int16_t imm12) { + BlockTrampolinePoolScope block_trampoline_pool(this); + GenInstrI(0b000, JALR, rd, rs1, imm12); + BlockTrampolinePoolFor(1); +} + +// Branches + +void Assembler::beq(Register rs1, Register rs2, int16_t imm13) { + GenInstrBranchCC_rri(0b000, rs1, rs2, imm13); +} + +void Assembler::bne(Register rs1, Register rs2, int16_t imm13) { + GenInstrBranchCC_rri(0b001, rs1, rs2, imm13); +} + +void Assembler::blt(Register rs1, Register rs2, int16_t imm13) { + GenInstrBranchCC_rri(0b100, rs1, rs2, imm13); +} + +void Assembler::bge(Register rs1, Register rs2, int16_t imm13) { + GenInstrBranchCC_rri(0b101, rs1, rs2, imm13); +} + +void Assembler::bltu(Register rs1, Register rs2, int16_t imm13) { + GenInstrBranchCC_rri(0b110, rs1, rs2, imm13); +} + +void Assembler::bgeu(Register rs1, Register rs2, int16_t imm13) { + GenInstrBranchCC_rri(0b111, rs1, rs2, imm13); +} + +// Loads + +void Assembler::lb(Register rd, Register rs1, int16_t imm12) { + GenInstrLoad_ri(0b000, rd, rs1, imm12); +} + +void Assembler::lh(Register rd, Register rs1, int16_t imm12) { + GenInstrLoad_ri(0b001, rd, rs1, imm12); +} + +void Assembler::lw(Register rd, Register rs1, int16_t imm12) { + GenInstrLoad_ri(0b010, rd, rs1, imm12); +} + +void Assembler::lbu(Register rd, Register rs1, int16_t imm12) { + GenInstrLoad_ri(0b100, rd, rs1, imm12); +} + +void Assembler::lhu(Register rd, Register rs1, int16_t imm12) { + GenInstrLoad_ri(0b101, rd, rs1, imm12); +} + +// Stores + +void Assembler::sb(Register source, Register base, int16_t imm12) { + GenInstrStore_rri(0b000, base, source, imm12); +} + +void Assembler::sh(Register source, Register base, int16_t imm12) { + GenInstrStore_rri(0b001, base, source, imm12); +} + +void Assembler::sw(Register source, Register base, int16_t imm12) { + GenInstrStore_rri(0b010, base, source, imm12); +} + +// Arithmetic with immediate + +void Assembler::addi(Register rd, Register rs1, int16_t imm12) { + GenInstrALU_ri(0b000, rd, rs1, imm12); +} + +void Assembler::slti(Register rd, Register rs1, int16_t imm12) { + GenInstrALU_ri(0b010, rd, rs1, imm12); +} + +void Assembler::sltiu(Register rd, Register rs1, int16_t imm12) { + GenInstrALU_ri(0b011, rd, rs1, imm12); +} + +void Assembler::xori(Register rd, Register rs1, int16_t imm12) { + GenInstrALU_ri(0b100, rd, rs1, imm12); +} + +void Assembler::ori(Register rd, Register rs1, int16_t imm12) { + GenInstrALU_ri(0b110, rd, rs1, imm12); +} + +void Assembler::andi(Register rd, Register rs1, int16_t imm12) { + GenInstrALU_ri(0b111, rd, rs1, imm12); +} + +void Assembler::slli(Register rd, Register rs1, uint8_t shamt) { + GenInstrShift_ri(0, 0b001, rd, rs1, shamt & 0x3f); +} + +void Assembler::srli(Register rd, Register rs1, uint8_t shamt) { + GenInstrShift_ri(0, 0b101, rd, rs1, shamt & 0x3f); +} + +void Assembler::srai(Register rd, Register rs1, uint8_t shamt) { + GenInstrShift_ri(1, 0b101, rd, rs1, shamt & 0x3f); +} + +// Arithmetic + +void Assembler::add(Register rd, Register rs1, Register rs2) { + GenInstrALU_rr(0b0000000, 0b000, rd, rs1, rs2); +} + +void Assembler::sub(Register rd, Register rs1, Register rs2) { + GenInstrALU_rr(0b0100000, 0b000, rd, rs1, rs2); +} + +void Assembler::sll(Register rd, Register rs1, Register rs2) { + GenInstrALU_rr(0b0000000, 0b001, rd, rs1, rs2); +} + +void Assembler::slt(Register rd, Register rs1, Register rs2) { + GenInstrALU_rr(0b0000000, 0b010, rd, rs1, rs2); +} + +void Assembler::sltu(Register rd, Register rs1, Register rs2) { + GenInstrALU_rr(0b0000000, 0b011, rd, rs1, rs2); +} + +void Assembler::xor_(Register rd, Register rs1, Register rs2) { + GenInstrALU_rr(0b0000000, 0b100, rd, rs1, rs2); +} + +void Assembler::srl(Register rd, Register rs1, Register rs2) { + GenInstrALU_rr(0b0000000, 0b101, rd, rs1, rs2); +} + +void Assembler::sra(Register rd, Register rs1, Register rs2) { + GenInstrALU_rr(0b0100000, 0b101, rd, rs1, rs2); +} + +void Assembler::or_(Register rd, Register rs1, Register rs2) { + GenInstrALU_rr(0b0000000, 0b110, rd, rs1, rs2); +} + +void Assembler::and_(Register rd, Register rs1, Register rs2) { + GenInstrALU_rr(0b0000000, 0b111, rd, rs1, rs2); +} + +// Memory fences + +void Assembler::fence(uint8_t pred, uint8_t succ) { + DCHECK(is_uint4(pred) && is_uint4(succ)); + uint16_t imm12 = succ | (pred << 4) | (0b0000 << 8); + GenInstrI(0b000, MISC_MEM, ToRegister(0), ToRegister(0), imm12); +} + +void Assembler::fence_tso() { + uint16_t imm12 = (0b0011) | (0b0011 << 4) | (0b1000 << 8); + GenInstrI(0b000, MISC_MEM, ToRegister(0), ToRegister(0), imm12); +} + +// Environment call / break + +void Assembler::ecall() { + GenInstrI(0b000, SYSTEM, ToRegister(0), ToRegister(0), 0); +} + +void Assembler::ebreak() { + GenInstrI(0b000, SYSTEM, ToRegister(0), ToRegister(0), 1); +} + +// This is a de facto standard (as set by GNU binutils) 32-bit unimplemented +// instruction (i.e., it should always trap, if your implementation has invalid +// instruction traps). +void Assembler::unimp() { + GenInstrI(0b001, SYSTEM, ToRegister(0), ToRegister(0), 0b110000000000); +} + +// CSR + +void Assembler::csrrw(Register rd, ControlStatusReg csr, Register rs1) { + GenInstrCSR_ir(0b001, rd, csr, rs1); +} + +void Assembler::csrrs(Register rd, ControlStatusReg csr, Register rs1) { + GenInstrCSR_ir(0b010, rd, csr, rs1); +} + +void Assembler::csrrc(Register rd, ControlStatusReg csr, Register rs1) { + GenInstrCSR_ir(0b011, rd, csr, rs1); +} + +void Assembler::csrrwi(Register rd, ControlStatusReg csr, uint8_t imm5) { + GenInstrCSR_ii(0b101, rd, csr, imm5); +} + +void Assembler::csrrsi(Register rd, ControlStatusReg csr, uint8_t imm5) { + GenInstrCSR_ii(0b110, rd, csr, imm5); +} + +void Assembler::csrrci(Register rd, ControlStatusReg csr, uint8_t imm5) { + GenInstrCSR_ii(0b111, rd, csr, imm5); +} + +// RV64I + +void Assembler::lwu(Register rd, Register rs1, int16_t imm12) { + GenInstrLoad_ri(0b110, rd, rs1, imm12); +} + +void Assembler::ld(Register rd, Register rs1, int16_t imm12) { + GenInstrLoad_ri(0b011, rd, rs1, imm12); +} + +void Assembler::sd(Register source, Register base, int16_t imm12) { + GenInstrStore_rri(0b011, base, source, imm12); +} + +void Assembler::addiw(Register rd, Register rs1, int16_t imm12) { + GenInstrI(0b000, OP_IMM_32, rd, rs1, imm12); +} + +void Assembler::slliw(Register rd, Register rs1, uint8_t shamt) { + GenInstrShiftW_ri(0, 0b001, rd, rs1, shamt & 0x1f); +} + +void Assembler::srliw(Register rd, Register rs1, uint8_t shamt) { + GenInstrShiftW_ri(0, 0b101, rd, rs1, shamt & 0x1f); +} + +void Assembler::sraiw(Register rd, Register rs1, uint8_t shamt) { + GenInstrShiftW_ri(1, 0b101, rd, rs1, shamt & 0x1f); +} + +void Assembler::addw(Register rd, Register rs1, Register rs2) { + GenInstrALUW_rr(0b0000000, 0b000, rd, rs1, rs2); +} + +void Assembler::subw(Register rd, Register rs1, Register rs2) { + GenInstrALUW_rr(0b0100000, 0b000, rd, rs1, rs2); +} + +void Assembler::sllw(Register rd, Register rs1, Register rs2) { + GenInstrALUW_rr(0b0000000, 0b001, rd, rs1, rs2); +} + +void Assembler::srlw(Register rd, Register rs1, Register rs2) { + GenInstrALUW_rr(0b0000000, 0b101, rd, rs1, rs2); +} + +void Assembler::sraw(Register rd, Register rs1, Register rs2) { + GenInstrALUW_rr(0b0100000, 0b101, rd, rs1, rs2); +} + +// RV32M Standard Extension + +void Assembler::mul(Register rd, Register rs1, Register rs2) { + GenInstrALU_rr(0b0000001, 0b000, rd, rs1, rs2); +} + +void Assembler::mulh(Register rd, Register rs1, Register rs2) { + GenInstrALU_rr(0b0000001, 0b001, rd, rs1, rs2); +} + +void Assembler::mulhsu(Register rd, Register rs1, Register rs2) { + GenInstrALU_rr(0b0000001, 0b010, rd, rs1, rs2); +} + +void Assembler::mulhu(Register rd, Register rs1, Register rs2) { + GenInstrALU_rr(0b0000001, 0b011, rd, rs1, rs2); +} + +void Assembler::div(Register rd, Register rs1, Register rs2) { + GenInstrALU_rr(0b0000001, 0b100, rd, rs1, rs2); +} + +void Assembler::divu(Register rd, Register rs1, Register rs2) { + GenInstrALU_rr(0b0000001, 0b101, rd, rs1, rs2); +} + +void Assembler::rem(Register rd, Register rs1, Register rs2) { + GenInstrALU_rr(0b0000001, 0b110, rd, rs1, rs2); +} + +void Assembler::remu(Register rd, Register rs1, Register rs2) { + GenInstrALU_rr(0b0000001, 0b111, rd, rs1, rs2); +} + +// RV64M Standard Extension (in addition to RV32M) + +void Assembler::mulw(Register rd, Register rs1, Register rs2) { + GenInstrALUW_rr(0b0000001, 0b000, rd, rs1, rs2); +} + +void Assembler::divw(Register rd, Register rs1, Register rs2) { + GenInstrALUW_rr(0b0000001, 0b100, rd, rs1, rs2); +} + +void Assembler::divuw(Register rd, Register rs1, Register rs2) { + GenInstrALUW_rr(0b0000001, 0b101, rd, rs1, rs2); +} + +void Assembler::remw(Register rd, Register rs1, Register rs2) { + GenInstrALUW_rr(0b0000001, 0b110, rd, rs1, rs2); +} + +void Assembler::remuw(Register rd, Register rs1, Register rs2) { + GenInstrALUW_rr(0b0000001, 0b111, rd, rs1, rs2); +} + +// RV32A Standard Extension + +void Assembler::lr_w(bool aq, bool rl, Register rd, Register rs1) { + GenInstrRAtomic(0b00010, aq, rl, 0b010, rd, rs1, zero_reg); +} + +void Assembler::sc_w(bool aq, bool rl, Register rd, Register rs1, + Register rs2) { + GenInstrRAtomic(0b00011, aq, rl, 0b010, rd, rs1, rs2); +} + +void Assembler::amoswap_w(bool aq, bool rl, Register rd, Register rs1, + Register rs2) { + GenInstrRAtomic(0b00001, aq, rl, 0b010, rd, rs1, rs2); +} + +void Assembler::amoadd_w(bool aq, bool rl, Register rd, Register rs1, + Register rs2) { + GenInstrRAtomic(0b00000, aq, rl, 0b010, rd, rs1, rs2); +} + +void Assembler::amoxor_w(bool aq, bool rl, Register rd, Register rs1, + Register rs2) { + GenInstrRAtomic(0b00100, aq, rl, 0b010, rd, rs1, rs2); +} + +void Assembler::amoand_w(bool aq, bool rl, Register rd, Register rs1, + Register rs2) { + GenInstrRAtomic(0b01100, aq, rl, 0b010, rd, rs1, rs2); +} + +void Assembler::amoor_w(bool aq, bool rl, Register rd, Register rs1, + Register rs2) { + GenInstrRAtomic(0b01000, aq, rl, 0b010, rd, rs1, rs2); +} + +void Assembler::amomin_w(bool aq, bool rl, Register rd, Register rs1, + Register rs2) { + GenInstrRAtomic(0b10000, aq, rl, 0b010, rd, rs1, rs2); +} + +void Assembler::amomax_w(bool aq, bool rl, Register rd, Register rs1, + Register rs2) { + GenInstrRAtomic(0b10100, aq, rl, 0b010, rd, rs1, rs2); +} + +void Assembler::amominu_w(bool aq, bool rl, Register rd, Register rs1, + Register rs2) { + GenInstrRAtomic(0b11000, aq, rl, 0b010, rd, rs1, rs2); +} + +void Assembler::amomaxu_w(bool aq, bool rl, Register rd, Register rs1, + Register rs2) { + GenInstrRAtomic(0b11100, aq, rl, 0b010, rd, rs1, rs2); +} + +// RV64A Standard Extension (in addition to RV32A) + +void Assembler::lr_d(bool aq, bool rl, Register rd, Register rs1) { + GenInstrRAtomic(0b00010, aq, rl, 0b011, rd, rs1, zero_reg); +} + +void Assembler::sc_d(bool aq, bool rl, Register rd, Register rs1, + Register rs2) { + GenInstrRAtomic(0b00011, aq, rl, 0b011, rd, rs1, rs2); +} + +void Assembler::amoswap_d(bool aq, bool rl, Register rd, Register rs1, + Register rs2) { + GenInstrRAtomic(0b00001, aq, rl, 0b011, rd, rs1, rs2); +} + +void Assembler::amoadd_d(bool aq, bool rl, Register rd, Register rs1, + Register rs2) { + GenInstrRAtomic(0b00000, aq, rl, 0b011, rd, rs1, rs2); +} + +void Assembler::amoxor_d(bool aq, bool rl, Register rd, Register rs1, + Register rs2) { + GenInstrRAtomic(0b00100, aq, rl, 0b011, rd, rs1, rs2); +} + +void Assembler::amoand_d(bool aq, bool rl, Register rd, Register rs1, + Register rs2) { + GenInstrRAtomic(0b01100, aq, rl, 0b011, rd, rs1, rs2); +} + +void Assembler::amoor_d(bool aq, bool rl, Register rd, Register rs1, + Register rs2) { + GenInstrRAtomic(0b01000, aq, rl, 0b011, rd, rs1, rs2); +} + +void Assembler::amomin_d(bool aq, bool rl, Register rd, Register rs1, + Register rs2) { + GenInstrRAtomic(0b10000, aq, rl, 0b011, rd, rs1, rs2); +} + +void Assembler::amomax_d(bool aq, bool rl, Register rd, Register rs1, + Register rs2) { + GenInstrRAtomic(0b10100, aq, rl, 0b011, rd, rs1, rs2); +} + +void Assembler::amominu_d(bool aq, bool rl, Register rd, Register rs1, + Register rs2) { + GenInstrRAtomic(0b11000, aq, rl, 0b011, rd, rs1, rs2); +} + +void Assembler::amomaxu_d(bool aq, bool rl, Register rd, Register rs1, + Register rs2) { + GenInstrRAtomic(0b11100, aq, rl, 0b011, rd, rs1, rs2); +} + +// RV32F Standard Extension + +void Assembler::flw(FPURegister rd, Register rs1, int16_t imm12) { + GenInstrLoadFP_ri(0b010, rd, rs1, imm12); +} + +void Assembler::fsw(FPURegister source, Register base, int16_t imm12) { + GenInstrStoreFP_rri(0b010, base, source, imm12); +} + +void Assembler::fmadd_s(FPURegister rd, FPURegister rs1, FPURegister rs2, + FPURegister rs3, RoundingMode frm) { + GenInstrR4(0b00, MADD, rd, rs1, rs2, rs3, frm); +} + +void Assembler::fmsub_s(FPURegister rd, FPURegister rs1, FPURegister rs2, + FPURegister rs3, RoundingMode frm) { + GenInstrR4(0b00, MSUB, rd, rs1, rs2, rs3, frm); +} + +void Assembler::fnmsub_s(FPURegister rd, FPURegister rs1, FPURegister rs2, + FPURegister rs3, RoundingMode frm) { + GenInstrR4(0b00, NMSUB, rd, rs1, rs2, rs3, frm); +} + +void Assembler::fnmadd_s(FPURegister rd, FPURegister rs1, FPURegister rs2, + FPURegister rs3, RoundingMode frm) { + GenInstrR4(0b00, NMADD, rd, rs1, rs2, rs3, frm); +} + +void Assembler::fadd_s(FPURegister rd, FPURegister rs1, FPURegister rs2, + RoundingMode frm) { + GenInstrALUFP_rr(0b0000000, frm, rd, rs1, rs2); +} + +void Assembler::fsub_s(FPURegister rd, FPURegister rs1, FPURegister rs2, + RoundingMode frm) { + GenInstrALUFP_rr(0b0000100, frm, rd, rs1, rs2); +} + +void Assembler::fmul_s(FPURegister rd, FPURegister rs1, FPURegister rs2, + RoundingMode frm) { + GenInstrALUFP_rr(0b0001000, frm, rd, rs1, rs2); +} + +void Assembler::fdiv_s(FPURegister rd, FPURegister rs1, FPURegister rs2, + RoundingMode frm) { + GenInstrALUFP_rr(0b0001100, frm, rd, rs1, rs2); +} + +void Assembler::fsqrt_s(FPURegister rd, FPURegister rs1, RoundingMode frm) { + GenInstrALUFP_rr(0b0101100, frm, rd, rs1, zero_reg); +} + +void Assembler::fsgnj_s(FPURegister rd, FPURegister rs1, FPURegister rs2) { + GenInstrALUFP_rr(0b0010000, 0b000, rd, rs1, rs2); +} + +void Assembler::fsgnjn_s(FPURegister rd, FPURegister rs1, FPURegister rs2) { + GenInstrALUFP_rr(0b0010000, 0b001, rd, rs1, rs2); +} + +void Assembler::fsgnjx_s(FPURegister rd, FPURegister rs1, FPURegister rs2) { + GenInstrALUFP_rr(0b0010000, 0b010, rd, rs1, rs2); +} + +void Assembler::fmin_s(FPURegister rd, FPURegister rs1, FPURegister rs2) { + GenInstrALUFP_rr(0b0010100, 0b000, rd, rs1, rs2); +} + +void Assembler::fmax_s(FPURegister rd, FPURegister rs1, FPURegister rs2) { + GenInstrALUFP_rr(0b0010100, 0b001, rd, rs1, rs2); +} + +void Assembler::fcvt_w_s(Register rd, FPURegister rs1, RoundingMode frm) { + GenInstrALUFP_rr(0b1100000, frm, rd, rs1, zero_reg); +} + +void Assembler::fcvt_wu_s(Register rd, FPURegister rs1, RoundingMode frm) { + GenInstrALUFP_rr(0b1100000, frm, rd, rs1, ToRegister(1)); +} + +void Assembler::fmv_x_w(Register rd, FPURegister rs1) { + GenInstrALUFP_rr(0b1110000, 0b000, rd, rs1, zero_reg); +} + +void Assembler::feq_s(Register rd, FPURegister rs1, FPURegister rs2) { + GenInstrALUFP_rr(0b1010000, 0b010, rd, rs1, rs2); +} + +void Assembler::flt_s(Register rd, FPURegister rs1, FPURegister rs2) { + GenInstrALUFP_rr(0b1010000, 0b001, rd, rs1, rs2); +} + +void Assembler::fle_s(Register rd, FPURegister rs1, FPURegister rs2) { + GenInstrALUFP_rr(0b1010000, 0b000, rd, rs1, rs2); +} + +void Assembler::fclass_s(Register rd, FPURegister rs1) { + GenInstrALUFP_rr(0b1110000, 0b001, rd, rs1, zero_reg); +} + +void Assembler::fcvt_s_w(FPURegister rd, Register rs1, RoundingMode frm) { + GenInstrALUFP_rr(0b1101000, frm, rd, rs1, zero_reg); +} + +void Assembler::fcvt_s_wu(FPURegister rd, Register rs1, RoundingMode frm) { + GenInstrALUFP_rr(0b1101000, frm, rd, rs1, ToRegister(1)); +} + +void Assembler::fmv_w_x(FPURegister rd, Register rs1) { + GenInstrALUFP_rr(0b1111000, 0b000, rd, rs1, zero_reg); +} + +// RV64F Standard Extension (in addition to RV32F) + +void Assembler::fcvt_l_s(Register rd, FPURegister rs1, RoundingMode frm) { + GenInstrALUFP_rr(0b1100000, frm, rd, rs1, ToRegister(2)); +} + +void Assembler::fcvt_lu_s(Register rd, FPURegister rs1, RoundingMode frm) { + GenInstrALUFP_rr(0b1100000, frm, rd, rs1, ToRegister(3)); +} + +void Assembler::fcvt_s_l(FPURegister rd, Register rs1, RoundingMode frm) { + GenInstrALUFP_rr(0b1101000, frm, rd, rs1, ToRegister(2)); +} + +void Assembler::fcvt_s_lu(FPURegister rd, Register rs1, RoundingMode frm) { + GenInstrALUFP_rr(0b1101000, frm, rd, rs1, ToRegister(3)); +} + +// RV32D Standard Extension + +void Assembler::fld(FPURegister rd, Register rs1, int16_t imm12) { + GenInstrLoadFP_ri(0b011, rd, rs1, imm12); +} + +void Assembler::fsd(FPURegister source, Register base, int16_t imm12) { + GenInstrStoreFP_rri(0b011, base, source, imm12); +} + +void Assembler::fmadd_d(FPURegister rd, FPURegister rs1, FPURegister rs2, + FPURegister rs3, RoundingMode frm) { + GenInstrR4(0b01, MADD, rd, rs1, rs2, rs3, frm); +} + +void Assembler::fmsub_d(FPURegister rd, FPURegister rs1, FPURegister rs2, + FPURegister rs3, RoundingMode frm) { + GenInstrR4(0b01, MSUB, rd, rs1, rs2, rs3, frm); +} + +void Assembler::fnmsub_d(FPURegister rd, FPURegister rs1, FPURegister rs2, + FPURegister rs3, RoundingMode frm) { + GenInstrR4(0b01, NMSUB, rd, rs1, rs2, rs3, frm); +} + +void Assembler::fnmadd_d(FPURegister rd, FPURegister rs1, FPURegister rs2, + FPURegister rs3, RoundingMode frm) { + GenInstrR4(0b01, NMADD, rd, rs1, rs2, rs3, frm); +} + +void Assembler::fadd_d(FPURegister rd, FPURegister rs1, FPURegister rs2, + RoundingMode frm) { + GenInstrALUFP_rr(0b0000001, frm, rd, rs1, rs2); +} + +void Assembler::fsub_d(FPURegister rd, FPURegister rs1, FPURegister rs2, + RoundingMode frm) { + GenInstrALUFP_rr(0b0000101, frm, rd, rs1, rs2); +} + +void Assembler::fmul_d(FPURegister rd, FPURegister rs1, FPURegister rs2, + RoundingMode frm) { + GenInstrALUFP_rr(0b0001001, frm, rd, rs1, rs2); +} + +void Assembler::fdiv_d(FPURegister rd, FPURegister rs1, FPURegister rs2, + RoundingMode frm) { + GenInstrALUFP_rr(0b0001101, frm, rd, rs1, rs2); +} + +void Assembler::fsqrt_d(FPURegister rd, FPURegister rs1, RoundingMode frm) { + GenInstrALUFP_rr(0b0101101, frm, rd, rs1, zero_reg); +} + +void Assembler::fsgnj_d(FPURegister rd, FPURegister rs1, FPURegister rs2) { + GenInstrALUFP_rr(0b0010001, 0b000, rd, rs1, rs2); +} + +void Assembler::fsgnjn_d(FPURegister rd, FPURegister rs1, FPURegister rs2) { + GenInstrALUFP_rr(0b0010001, 0b001, rd, rs1, rs2); +} + +void Assembler::fsgnjx_d(FPURegister rd, FPURegister rs1, FPURegister rs2) { + GenInstrALUFP_rr(0b0010001, 0b010, rd, rs1, rs2); +} + +void Assembler::fmin_d(FPURegister rd, FPURegister rs1, FPURegister rs2) { + GenInstrALUFP_rr(0b0010101, 0b000, rd, rs1, rs2); +} + +void Assembler::fmax_d(FPURegister rd, FPURegister rs1, FPURegister rs2) { + GenInstrALUFP_rr(0b0010101, 0b001, rd, rs1, rs2); +} + +void Assembler::fcvt_s_d(FPURegister rd, FPURegister rs1, RoundingMode frm) { + GenInstrALUFP_rr(0b0100000, frm, rd, rs1, ToRegister(1)); +} + +void Assembler::fcvt_d_s(FPURegister rd, FPURegister rs1, RoundingMode frm) { + GenInstrALUFP_rr(0b0100001, frm, rd, rs1, zero_reg); +} + +void Assembler::feq_d(Register rd, FPURegister rs1, FPURegister rs2) { + GenInstrALUFP_rr(0b1010001, 0b010, rd, rs1, rs2); +} + +void Assembler::flt_d(Register rd, FPURegister rs1, FPURegister rs2) { + GenInstrALUFP_rr(0b1010001, 0b001, rd, rs1, rs2); +} + +void Assembler::fle_d(Register rd, FPURegister rs1, FPURegister rs2) { + GenInstrALUFP_rr(0b1010001, 0b000, rd, rs1, rs2); +} + +void Assembler::fclass_d(Register rd, FPURegister rs1) { + GenInstrALUFP_rr(0b1110001, 0b001, rd, rs1, zero_reg); +} + +void Assembler::fcvt_w_d(Register rd, FPURegister rs1, RoundingMode frm) { + GenInstrALUFP_rr(0b1100001, frm, rd, rs1, zero_reg); +} + +void Assembler::fcvt_wu_d(Register rd, FPURegister rs1, RoundingMode frm) { + GenInstrALUFP_rr(0b1100001, frm, rd, rs1, ToRegister(1)); +} + +void Assembler::fcvt_d_w(FPURegister rd, Register rs1, RoundingMode frm) { + GenInstrALUFP_rr(0b1101001, frm, rd, rs1, zero_reg); +} + +void Assembler::fcvt_d_wu(FPURegister rd, Register rs1, RoundingMode frm) { + GenInstrALUFP_rr(0b1101001, frm, rd, rs1, ToRegister(1)); +} + +// RV64D Standard Extension (in addition to RV32D) + +void Assembler::fcvt_l_d(Register rd, FPURegister rs1, RoundingMode frm) { + GenInstrALUFP_rr(0b1100001, frm, rd, rs1, ToRegister(2)); +} + +void Assembler::fcvt_lu_d(Register rd, FPURegister rs1, RoundingMode frm) { + GenInstrALUFP_rr(0b1100001, frm, rd, rs1, ToRegister(3)); +} + +void Assembler::fmv_x_d(Register rd, FPURegister rs1) { + GenInstrALUFP_rr(0b1110001, 0b000, rd, rs1, zero_reg); +} + +void Assembler::fcvt_d_l(FPURegister rd, Register rs1, RoundingMode frm) { + GenInstrALUFP_rr(0b1101001, frm, rd, rs1, ToRegister(2)); +} + +void Assembler::fcvt_d_lu(FPURegister rd, Register rs1, RoundingMode frm) { + GenInstrALUFP_rr(0b1101001, frm, rd, rs1, ToRegister(3)); +} + +void Assembler::fmv_d_x(FPURegister rd, Register rs1) { + GenInstrALUFP_rr(0b1111001, 0b000, rd, rs1, zero_reg); +} + +// Privileged + +void Assembler::uret() { + GenInstrPriv(0b0000000, ToRegister(0), ToRegister(0b00010)); +} + +void Assembler::sret() { + GenInstrPriv(0b0001000, ToRegister(0), ToRegister(0b00010)); +} + +void Assembler::mret() { + GenInstrPriv(0b0011000, ToRegister(0), ToRegister(0b00010)); +} + +void Assembler::wfi() { + GenInstrPriv(0b0001000, ToRegister(0), ToRegister(0b00101)); +} + +void Assembler::sfence_vma(Register rs1, Register rs2) { + GenInstrR(0b0001001, 0b000, SYSTEM, ToRegister(0), rs1, rs2); +} + +// Assembler Pseudo Instructions (Tables 25.2 and 25.3, RISC-V Unprivileged ISA) + +void Assembler::nop() { addi(ToRegister(0), ToRegister(0), 0); } + +void Assembler::RV_li(Register rd, int64_t imm) { + // 64-bit imm is put in the register rd. + // In most cases the imm is 32 bit and 2 instructions are generated. If a + // temporary register is available, in the worst case, 6 instructions are + // generated for a full 64-bit immediate. If temporay register is not + // available the maximum will be 8 instructions. If imm is more than 32 bits + // and a temp register is available, imm is divided into two 32-bit parts, + // low_32 and up_32. Each part is built in a separate register. low_32 is + // built before up_32. If low_32 is negative (upper 32 bits are 1), 0xffffffff + // is subtracted from up_32 before up_32 is built. This compensates for 32 + // bits of 1's in the lower when the two registers are added. If no temp is + // available, the upper 32 bit is built in rd, and the lower 32 bits are + // devided to 3 parts (11, 11, and 10 bits). The parts are shifted and added + // to the upper part built in rd. + if (is_int32(imm + 0x800)) { + // 32-bit case. Maximum of 2 instructions generated + int64_t high_20 = ((imm + 0x800) >> 12); + int64_t low_12 = imm << 52 >> 52; + if (high_20) { + lui(rd, (int32_t)high_20); + if (low_12) { + addi(rd, rd, low_12); + } + } else { + addi(rd, zero_reg, low_12); + } + return; + } else { + // 64-bit case: divide imm into two 32-bit parts, upper and lower + int64_t up_32 = imm >> 32; + int64_t low_32 = imm & 0xffffffffull; + Register temp_reg = rd; + // Check if a temporary register is available + if (up_32 == 0 || low_32 == 0) { + // No temp register is needed + } else { + UseScratchRegisterScope temps(this); + BlockTrampolinePoolScope block_trampoline_pool(this); + temp_reg = temps.hasAvailable() ? temps.Acquire() : no_reg; + } + if (temp_reg != no_reg) { + // keep track of hardware behavior for lower part in sim_low + int64_t sim_low = 0; + // Build lower part + if (low_32 != 0) { + int64_t high_20 = ((low_32 + 0x800) >> 12); + int64_t low_12 = low_32 & 0xfff; + if (high_20) { + // Adjust to 20 bits for the case of overflow + high_20 &= 0xfffff; + sim_low = ((high_20 << 12) << 32) >> 32; + lui(rd, (int32_t)high_20); + if (low_12) { + sim_low += (low_12 << 52 >> 52) | low_12; + addi(rd, rd, low_12); + } + } else { + sim_low = low_12; + ori(rd, zero_reg, low_12); + } + } + if (sim_low & 0x100000000) { + // Bit 31 is 1. Either an overflow or a negative 64 bit + if (up_32 == 0) { + // Positive number, but overflow because of the add 0x800 + slli(rd, rd, 32); + srli(rd, rd, 32); + return; + } + // low_32 is a negative 64 bit after the build + up_32 = (up_32 - 0xffffffff) & 0xffffffff; + } + if (up_32 == 0) { + return; + } + // Build upper part in a temporary register + if (low_32 == 0) { + // Build upper part in rd + temp_reg = rd; + } + int64_t high_20 = (up_32 + 0x800) >> 12; + int64_t low_12 = up_32 & 0xfff; + if (high_20) { + // Adjust to 20 bits for the case of overflow + high_20 &= 0xfffff; + lui(temp_reg, (int32_t)high_20); + if (low_12) { + addi(temp_reg, temp_reg, low_12); + } + } else { + ori(temp_reg, zero_reg, low_12); + } + // Put it at the bgining of register + slli(temp_reg, temp_reg, 32); + if (low_32 != 0) { + add(rd, rd, temp_reg); + } + return; + } + // No temp register. Build imm in rd. + // Build upper 32 bits first in rd. Divide lower 32 bits parts and add + // parts to the upper part by doing shift and add. + // First build upper part in rd. + int64_t high_20 = (up_32 + 0x800) >> 12; + int64_t low_12 = up_32 & 0xfff; + if (high_20) { + // Adjust to 20 bits for the case of overflow + high_20 &= 0xfffff; + lui(rd, (int32_t)high_20); + if (low_12) { + addi(rd, rd, low_12); + } + } else { + ori(rd, zero_reg, low_12); + } + // upper part already in rd. Each part to be added to rd, has maximum of 11 + // bits, and always starts with a 1. rd is shifted by the size of the part + // plus the number of zeros between the parts. Each part is added after the + // left shift. + uint32_t mask = 0x80000000; + int32_t shift_val = 0; + int32_t i; + for (i = 0; i < 32; i++) { + if ((low_32 & mask) == 0) { + mask >>= 1; + shift_val++; + if (i == 31) { + // rest is zero + slli(rd, rd, shift_val); + } + continue; + } + // The first 1 seen + int32_t part; + if ((i + 11) < 32) { + // Pick 11 bits + part = ((uint32_t)(low_32 << i) >> i) >> (32 - (i + 11)); + slli(rd, rd, shift_val + 11); + ori(rd, rd, part); + i += 10; + mask >>= 11; + } else { + part = (uint32_t)(low_32 << i) >> i; + slli(rd, rd, shift_val + (32 - i)); + ori(rd, rd, part); + break; + } + shift_val = 0; + } + } +} + +int Assembler::li_count(int64_t imm) { + int count = 0; + if (is_int32(imm + 0x800)) { + int64_t Hi20 = ((imm + 0x800) >> 12); + int64_t Lo12 = imm << 52 >> 52; + + if (Hi20) count++; + + if (Lo12 || Hi20 == 0) count++; + return count; + } + + int64_t Lo12 = imm << 52 >> 52; + int64_t Hi52 = ((uint64_t)imm + 0x800ull) >> 12; + int FirstBit = 0; + uint64_t Val = Hi52; + while ((Val & 1) == 0) { + Val = Val >> 1; + FirstBit++; + } + int ShiftAmount = 12 + FirstBit; + Hi52 = (Hi52 >> (ShiftAmount - 12)) << ShiftAmount >> ShiftAmount; + + count += li_count(Hi52); + + count++; + if (Lo12) count++; + + return count; +} + +void Assembler::li_constant(Register rd, int64_t imm) { + DEBUG_PRINTF("li_constant(%d, %lx <%ld>)\n", ToNumber(rd), imm, imm); + lui(rd, (imm + (1LL << 47) + (1LL << 35) + (1LL << 23) + (1LL << 11)) >> + 48); // Bits 63:48 + addiw(rd, rd, + (imm + (1LL << 35) + (1LL << 23) + (1LL << 11)) << 16 >> + 52); // Bits 47:36 + slli(rd, rd, 12); + addi(rd, rd, (imm + (1LL << 23) + (1LL << 11)) << 28 >> 52); // Bits 35:24 + slli(rd, rd, 12); + addi(rd, rd, (imm + (1LL << 11)) << 40 >> 52); // Bits 23:12 + slli(rd, rd, 12); + addi(rd, rd, imm << 52 >> 52); // Bits 11:0 +} + +// Break / Trap instructions. +void Assembler::break_(uint32_t code, bool break_as_stop) { + // We need to invalidate breaks that could be stops as well because the + // simulator expects a char pointer after the stop instruction. + // See constants-mips.h for explanation. + DCHECK( + (break_as_stop && code <= kMaxStopCode && code > kMaxWatchpointCode) || + (!break_as_stop && (code > kMaxStopCode || code <= kMaxWatchpointCode))); + + // since ebreak does not allow additional immediate field, we use the + // immediate field of lui instruction immediately following the ebreak to + // encode the "code" info + ebreak(); + DCHECK(is_uint20(code)); + lui(zero_reg, code); +} + +void Assembler::stop(uint32_t code) { + DCHECK_GT(code, kMaxWatchpointCode); + DCHECK_LE(code, kMaxStopCode); +#if defined(V8_HOST_ARCH_RISCV64) + // FIXME: does RISCV expect a special value? + break_(0x54321); +#else // V8_HOST_ARCH_RISCV64 + break_(code, true); +#endif +} + +// Original MIPS Instructions + +// ------------Memory-instructions------------- + +bool Assembler::NeedAdjustBaseAndOffset(const MemOperand& src, + OffsetAccessType access_type, + int second_access_add_to_offset) { + bool two_accesses = static_cast(access_type); + DCHECK_LE(second_access_add_to_offset, 7); // Must be <= 7. + + // is_int12 must be passed a signed value, hence the static cast below. + if (is_int12(src.offset()) && + (!two_accesses || is_int12(static_cast( + src.offset() + second_access_add_to_offset)))) { + // Nothing to do: 'offset' (and, if needed, 'offset + 4', or other specified + // value) fits into int12. + return false; + } + return true; +} + +void Assembler::AdjustBaseAndOffset(MemOperand* src, Register scratch, + 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 int12. + + // Must not overwrite the register 'base' while loading 'offset'. + DCHECK(src->rm() != scratch); + + // FIXME(RISC-V): There may be a more optimal way to do this + RV_li(scratch, src->offset()); + add(scratch, scratch, src->rm()); + src->offset_ = 0; + src->rm_ = scratch; +} + +// FIXME (RISCV): not yet ported (or not used?) +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. + } + Instr instr = instr_at(pc); + DCHECK(RelocInfo::IsInternalReferenceEncoded(rmode)); + if (IsLui(instr)) { + uint64_t target_address = target_address_at(pc) + pc_delta; + DEBUG_PRINTF("target_address 0x%lx\n", target_address); + set_target_value_at(pc, target_address); + return 8; // Number of instructions patched. + } else { + UNIMPLEMENTED(); + return 1; + } +} + +void Assembler::GrowBuffer() { + DEBUG_PRINTF("GrowBuffer: %p -> ", buffer_start_); + // 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; + DEBUG_PRINTF("%p\n", buffer_start_); + pc_ += pc_delta; + reloc_info_writer.Reposition(reloc_info_writer.pos() + rc_delta, + reloc_info_writer.last_pc() + pc_delta); + + // Relocate runtime entries. + Vector instructions{buffer_start_, (size_t)pc_offset()}; + 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) { + if (!is_buffer_growth_blocked()) CheckBuffer(); + EmitHelper(data); +} + +void Assembler::dd(uint32_t data) { + if (!is_buffer_growth_blocked()) CheckBuffer(); + EmitHelper(data); +} + +void Assembler::dq(uint64_t data) { + if (!is_buffer_growth_blocked()) CheckBuffer(); + EmitHelper(data); +} + +void Assembler::dd(Label* label) { + uint64_t data; + if (!is_buffer_growth_blocked()) CheckBuffer(); + if (label->is_bound()) { + data = reinterpret_cast(buffer_start_ + label->pos()); + } else { + data = jump_address(label); + 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, then we emit trampoline pool. + { + DEBUG_PRINTF("inserting trampoline pool at %p (%d)\n", + reinterpret_cast(buffer_start_ + pc_offset()), + pc_offset()); + BlockTrampolinePoolScope block_trampoline_pool(this); + Label after_pool; + j(&after_pool); + + int pool_start = pc_offset(); + for (int i = 0; i < unbound_labels_count_; i++) { + int64_t imm64; + imm64 = branch_long_offset(&after_pool); + DCHECK(is_int32(imm64)); + int32_t Hi20 = (((int32_t)imm64 + 0x800) >> 12); + int32_t Lo12 = (int32_t)imm64 << 20 >> 20; + auipc(t5, Hi20); // Read PC + Hi20 into t5. + jr(t5, Lo12); // jump PC + Hi20 + Lo12 + } + // 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) { + DEBUG_PRINTF("target_address_at: pc: %lx\t", pc); + Instruction* instr0 = Instruction::At((unsigned char*)pc); + Instruction* instr1 = Instruction::At((unsigned char*)(pc + 1 * kInstrSize)); + Instruction* instr3 = Instruction::At((unsigned char*)(pc + 3 * kInstrSize)); + Instruction* instr5 = Instruction::At((unsigned char*)(pc + 5 * kInstrSize)); + Instruction* instr7 = Instruction::At((unsigned char*)(pc + 7 * kInstrSize)); + + // Interpret 5 instructions for address generated by li: See listing in + // Assembler::set_target_address_at() just below. + if (IsLui(*reinterpret_cast(instr0)) && + IsAddiw(*reinterpret_cast(instr1)) && + IsAddi(*reinterpret_cast(instr3)) && + IsAddi(*reinterpret_cast(instr5)) && + IsAddi(*reinterpret_cast(instr7))) { + // Assemble the 64 bit value. + int64_t addr = (int64_t)(instr0->Imm20UValue() << kImm20Shift) + + (int64_t)instr1->Imm12Value(); + addr <<= 12; + addr += (int64_t)instr3->Imm12Value(); + addr <<= 12; + addr += (int64_t)instr5->Imm12Value(); + addr <<= 12; + addr += (int64_t)instr7->Imm12Value(); + + DEBUG_PRINTF("addr: %lx\n", addr); + return static_cast
(addr); + } + // We should never get here, force a bad address if we do. + UNREACHABLE(); +} + +// On RISC-V, a 64-bit target address is stored in an 8-instruction sequence: +// 0: lui(rd, (j.imm64_ + (1LL << 47) + (1LL << 35) + +// (1LL << 23) + (1LL << 11)) >> 48); +// 1: addiw(rd, rd, (j.imm64_ + (1LL << 35) + (1LL << 23) + (1LL << 11)) +// << 16 >> 52); +// 2: slli(rd, rd, 12); +// 3: addi(rd, rd, (j.imm64_ + (1LL << 23) + (1LL << 11)) << 28 >> 52); +// 4: slli(rd, rd, 12); +// 5: addi(rd, rd, (j.imm64_ + (1 << 11)) << 40 >> 52); +// 6: slli(rd, rd, 12); +// 7: addi(rd, rd, j.imm64_ << 52 >> 52); +// +// Patching the address must replace all the lui & addi instructions, +// and flush the i-cache. +void Assembler::set_target_value_at(Address pc, uint64_t target, + ICacheFlushMode icache_flush_mode) { + // FIXME(RISC-V): Does the below statement apply to RISC-V? If so, we do not + // need all 8 instructions. + // There is an optimization where only 4 instructions are used to load address + // in code on MIP64 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. + Instruction* instr0 = Instruction::At((unsigned char*)pc); + DEBUG_PRINTF("set_target_value_at: pc: %lx\ttarget: %lx\n", pc, target); + int rd_code = instr0->RdValue(); + uint32_t* p = reinterpret_cast(pc); + +#ifdef DEBUG + // Check we have the result from a li macro-instruction. + Instruction* instr1 = Instruction::At((unsigned char*)(pc + 1 * kInstrSize)); + Instruction* instr3 = Instruction::At((unsigned char*)(pc + 3 * kInstrSize)); + Instruction* instr5 = Instruction::At((unsigned char*)(pc + 5 * kInstrSize)); + Instruction* instr7 = Instruction::At((unsigned char*)(pc + 7 * kInstrSize)); + DCHECK(IsLui(*reinterpret_cast(instr0)) && + IsAddiw(*reinterpret_cast(instr1)) && + IsAddi(*reinterpret_cast(instr3)) && + IsAddi(*reinterpret_cast(instr5)) && + IsAddi(*reinterpret_cast(instr7))); +#endif + + // Must use 8 instructions to insure patchable code (see above comment). + *p = LUI | (rd_code << kRdShift) | + ((uint32_t)( + (target + (1LL << 47) + (1LL << 35) + (1LL << 23) + (1LL << 11)) >> + 48) + << kImm20Shift); + *(p + 1) = + OP_IMM_32 | (rd_code << kRdShift) | (0b000 << kFunct3Shift) | + (rd_code << kRs1Shift) | + ((uint32_t)((target + (1LL << 35) + (1LL << 23) + (1LL << 11)) << 16 >> + 52) + << kImm12Shift); + *(p + 2) = OP_IMM | (rd_code << kRdShift) | (0b001 << kFunct3Shift) | + (rd_code << kRs1Shift) | (12 << kImm12Shift); + *(p + 3) = OP_IMM | (rd_code << kRdShift) | (0b000 << kFunct3Shift) | + (rd_code << kRs1Shift) | + ((uint32_t)((target + (1LL << 23) + (1LL << 11)) << 28 >> 52) + << kImm12Shift); + *(p + 4) = OP_IMM | (rd_code << kRdShift) | (0b001 << kFunct3Shift) | + (rd_code << kRs1Shift) | (12 << kImm12Shift); + *(p + 5) = OP_IMM | (rd_code << kRdShift) | (0b000 << kFunct3Shift) | + (rd_code << kRs1Shift) | + ((uint32_t)((target + (1LL << 11)) << 40 >> 52) << kImm12Shift); + *(p + 6) = OP_IMM | (rd_code << kRdShift) | (0b001 << kFunct3Shift) | + (rd_code << kRs1Shift) | (12 << kImm12Shift); + *(p + 7) = OP_IMM | (rd_code << kRdShift) | (0b000 << kFunct3Shift) | + (rd_code << kRs1Shift) | + ((uint32_t)(target << 52 >> 52) << kImm12Shift); + + if (icache_flush_mode != SKIP_ICACHE_FLUSH) { + FlushInstructionCache(pc, 8 * kInstrSize); + } + DCHECK_EQ(target_address_at(pc), target); +} + +UseScratchRegisterScope::UseScratchRegisterScope(Assembler* assembler) + : available_(assembler->GetScratchRegisterList()), + old_available_(*available_) {} + +UseScratchRegisterScope::~UseScratchRegisterScope() { + *available_ = old_available_; +} + +Register UseScratchRegisterScope::Acquire() { + DCHECK_NOT_NULL(available_); + DCHECK_NE(*available_, 0); + int index = static_cast(base::bits::CountTrailingZeros32(*available_)); + *available_ &= ~(1UL << index); + + return Register::from_code(index); +} + +bool UseScratchRegisterScope::hasAvailable() const { return *available_ != 0; } + +} // namespace internal +} // namespace v8 + +#endif // V8_TARGET_ARCH_RISCV64 Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/codegen/riscv64/assembler-riscv64.h =================================================================== --- /dev/null +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/codegen/riscv64/assembler-riscv64.h @@ -0,0 +1,1110 @@ +// 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_RISCV_ASSEMBLER_RISCV_H_ +#define V8_CODEGEN_RISCV_ASSEMBLER_RISCV_H_ + +#include + +#include +#include + +#include "src/codegen/assembler.h" +#include "src/codegen/external-reference.h" +#include "src/codegen/label.h" +#include "src/codegen/riscv64/constants-riscv64.h" +#include "src/codegen/riscv64/register-riscv64.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::NONE) + : rm_(no_reg), rmode_(rmode) { + value_.immediate = immediate; + } + 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::NONE) { + 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 RISC-V 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 OffsetIsInt12Encodable() const { return is_int12(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; + void GetCode(Isolate* isolate, CodeDesc* desc, + SafepointTableBuilder* safepoint_table_builder, + int handler_table_offset); + + // Convenience wrapper for code without safepoint or handler tables. + void GetCode(Isolate* isolate, CodeDesc* desc) { + GetCode(isolate, desc, kNoSafepointTable, kNoHandlerTable); + } + + // Unused on this architecture. + void MaybeEmitOutOfLineConstantPool() {} + + // 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. + + enum OffsetSize : int { + kOffset21 = 21, // RISCV jal + kOffset12 = 12, // RISCV imm12 + kOffset20 = 20, // RISCV imm20 + kOffset13 = 13 // RISCV branch + }; + + // Determines if Label is bound and near enough so that branch instruction + // can be used to reach it, instead of jump instruction. + bool is_near(Label* L); + bool is_near(Label* L, OffsetSize bits); + bool is_near_branch(Label* L); + + //Get offset from instr. + int BranchOffset(Instr instr); + int BrachlongOffset(Instr auipc, Instr jalr); + int JumpOffset(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::kOffset13); + } + inline int32_t jump_offset(Label* L) { + return branch_offset_helper(L, OffsetSize::kOffset21); + } + + uint64_t jump_address(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 RISC-V 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 lui/ori 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); + + // Difference between address of current opcode and target address offset. + static constexpr int kBranchPCOffset = kInstrSize; + + // Difference between address of current opcode and target address offset, + // when we are generatinga sequence of instructions for long relative PC + // branches + static constexpr int kLongBranchPCOffset = 3 * kInstrSize; + + // Adjust ra register in branch delay slot of bal instruction so to skip + // instructions not needed after optimization of PIC in + // TurboAssembler::BranchAndLink method. + + static constexpr int kOptimizedBranchAndLinkLongReturnOffset = 4 * kInstrSize; + + // Here we are patching the address in the LUI/ADDI instruction pair. + // These values are used in the serialization process and must be zero for + // RISC-V platform, as Code, Embedded Object or External-reference pointers + // are split across two consecutive instructions and don't exist separately + // in the code, so the serializer should not step forwards in memory after + // a target is resolved and written. + 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 + // LUI/ADDI instruction pair. + static constexpr int kInstructionsFor32BitConstant = 2; + static constexpr int kInstructionsFor64BitConstant = 8; + + // Difference between address of current opcode and value read from pc + // register. + static constexpr int kPcLoadDelta = 4; + + // Bits available for offset field in branches + static constexpr int kBranchOffsetBits = 13; + + // Bits available for offset field in jump + static constexpr int kJumpOffsetBits = 21; + + // Max offset for b instructions with 12-bit offset field (multiple of 2) + static constexpr int kMaxBranchOffset = (1 << (13 - 1)) - 1; + + // Max offset for jal instruction with 20-bit offset field (multiple of 2) + static constexpr int kMaxJumpOffset = (1 << (21 - 1)) - 1; + + static constexpr int kTrampolineSlotsSize = 2 * kInstrSize; + + RegList* GetScratchRegisterList() { return &scratch_register_list_; } + + // --------------------------------------------------------------------------- + // Code 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(); + + // 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, + }; + + // RISC-V Instructions Emited to a buffer + + void lui(Register rd, int32_t imm20); + void auipc(Register rd, int32_t imm20); + + // Jumps + void jal(Register rd, int32_t imm20); + void jalr(Register rd, Register rs1, int16_t imm12); + + // Branches + void beq(Register rs1, Register rs2, int16_t imm12); + inline void beq(Register rs1, Register rs2, Label* L) { + beq(rs1, rs2, branch_offset(L)); + } + void bne(Register rs1, Register rs2, int16_t imm12); + inline void bne(Register rs1, Register rs2, Label* L) { + bne(rs1, rs2, branch_offset(L)); + } + void blt(Register rs1, Register rs2, int16_t imm12); + inline void blt(Register rs1, Register rs2, Label* L) { + blt(rs1, rs2, branch_offset(L)); + } + void bge(Register rs1, Register rs2, int16_t imm12); + inline void bge(Register rs1, Register rs2, Label* L) { + bge(rs1, rs2, branch_offset(L)); + } + void bltu(Register rs1, Register rs2, int16_t imm12); + inline void bltu(Register rs1, Register rs2, Label* L) { + bltu(rs1, rs2, branch_offset(L)); + } + void bgeu(Register rs1, Register rs2, int16_t imm12); + inline void bgeu(Register rs1, Register rs2, Label* L) { + bgeu(rs1, rs2, branch_offset(L)); + } + + // Loads + void lb(Register rd, Register rs1, int16_t imm12); + void lh(Register rd, Register rs1, int16_t imm12); + void lw(Register rd, Register rs1, int16_t imm12); + void lbu(Register rd, Register rs1, int16_t imm12); + void lhu(Register rd, Register rs1, int16_t imm12); + + // Stores + void sb(Register source, Register base, int16_t imm12); + void sh(Register source, Register base, int16_t imm12); + void sw(Register source, Register base, int16_t imm12); + + // Arithmetic with immediate + void addi(Register rd, Register rs1, int16_t imm12); + void slti(Register rd, Register rs1, int16_t imm12); + void sltiu(Register rd, Register rs1, int16_t imm12); + void xori(Register rd, Register rs1, int16_t imm12); + void ori(Register rd, Register rs1, int16_t imm12); + void andi(Register rd, Register rs1, int16_t imm12); + void slli(Register rd, Register rs1, uint8_t shamt); + void srli(Register rd, Register rs1, uint8_t shamt); + void srai(Register rd, Register rs1, uint8_t shamt); + + // Arithmetic + void add(Register rd, Register rs1, Register rs2); + void sub(Register rd, Register rs1, Register rs2); + void sll(Register rd, Register rs1, Register rs2); + void slt(Register rd, Register rs1, Register rs2); + void sltu(Register rd, Register rs1, Register rs2); + void xor_(Register rd, Register rs1, Register rs2); + void srl(Register rd, Register rs1, Register rs2); + void sra(Register rd, Register rs1, Register rs2); + void or_(Register rd, Register rs1, Register rs2); + void and_(Register rd, Register rs1, Register rs2); + + // Memory fences + void fence(uint8_t pred, uint8_t succ); + void fence_tso(); + + // Environment call / break + void ecall(); + void ebreak(); + + // This is a de facto standard (as set by GNU binutils) 32-bit unimplemented + // instruction (i.e., it should always trap, if your implementation has + // invalid instruction traps). + void unimp(); + + // CSR + void csrrw(Register rd, ControlStatusReg csr, Register rs1); + void csrrs(Register rd, ControlStatusReg csr, Register rs1); + void csrrc(Register rd, ControlStatusReg csr, Register rs1); + void csrrwi(Register rd, ControlStatusReg csr, uint8_t imm5); + void csrrsi(Register rd, ControlStatusReg csr, uint8_t imm5); + void csrrci(Register rd, ControlStatusReg csr, uint8_t imm5); + + // RV64I + void lwu(Register rd, Register rs1, int16_t imm12); + void ld(Register rd, Register rs1, int16_t imm12); + void sd(Register source, Register base, int16_t imm12); + void addiw(Register rd, Register rs1, int16_t imm12); + void slliw(Register rd, Register rs1, uint8_t shamt); + void srliw(Register rd, Register rs1, uint8_t shamt); + void sraiw(Register rd, Register rs1, uint8_t shamt); + void addw(Register rd, Register rs1, Register rs2); + void subw(Register rd, Register rs1, Register rs2); + void sllw(Register rd, Register rs1, Register rs2); + void srlw(Register rd, Register rs1, Register rs2); + void sraw(Register rd, Register rs1, Register rs2); + + // RV32M Standard Extension + void mul(Register rd, Register rs1, Register rs2); + void mulh(Register rd, Register rs1, Register rs2); + void mulhsu(Register rd, Register rs1, Register rs2); + void mulhu(Register rd, Register rs1, Register rs2); + void div(Register rd, Register rs1, Register rs2); + void divu(Register rd, Register rs1, Register rs2); + void rem(Register rd, Register rs1, Register rs2); + void remu(Register rd, Register rs1, Register rs2); + + // RV64M Standard Extension (in addition to RV32M) + void mulw(Register rd, Register rs1, Register rs2); + void divw(Register rd, Register rs1, Register rs2); + void divuw(Register rd, Register rs1, Register rs2); + void remw(Register rd, Register rs1, Register rs2); + void remuw(Register rd, Register rs1, Register rs2); + + // RV32A Standard Extension + void lr_w(bool aq, bool rl, Register rd, Register rs1); + void sc_w(bool aq, bool rl, Register rd, Register rs1, Register rs2); + void amoswap_w(bool aq, bool rl, Register rd, Register rs1, Register rs2); + void amoadd_w(bool aq, bool rl, Register rd, Register rs1, Register rs2); + void amoxor_w(bool aq, bool rl, Register rd, Register rs1, Register rs2); + void amoand_w(bool aq, bool rl, Register rd, Register rs1, Register rs2); + void amoor_w(bool aq, bool rl, Register rd, Register rs1, Register rs2); + void amomin_w(bool aq, bool rl, Register rd, Register rs1, Register rs2); + void amomax_w(bool aq, bool rl, Register rd, Register rs1, Register rs2); + void amominu_w(bool aq, bool rl, Register rd, Register rs1, Register rs2); + void amomaxu_w(bool aq, bool rl, Register rd, Register rs1, Register rs2); + + // RV64A Standard Extension (in addition to RV32A) + void lr_d(bool aq, bool rl, Register rd, Register rs1); + void sc_d(bool aq, bool rl, Register rd, Register rs1, Register rs2); + void amoswap_d(bool aq, bool rl, Register rd, Register rs1, Register rs2); + void amoadd_d(bool aq, bool rl, Register rd, Register rs1, Register rs2); + void amoxor_d(bool aq, bool rl, Register rd, Register rs1, Register rs2); + void amoand_d(bool aq, bool rl, Register rd, Register rs1, Register rs2); + void amoor_d(bool aq, bool rl, Register rd, Register rs1, Register rs2); + void amomin_d(bool aq, bool rl, Register rd, Register rs1, Register rs2); + void amomax_d(bool aq, bool rl, Register rd, Register rs1, Register rs2); + void amominu_d(bool aq, bool rl, Register rd, Register rs1, Register rs2); + void amomaxu_d(bool aq, bool rl, Register rd, Register rs1, Register rs2); + + // RV32F Standard Extension + void flw(FPURegister rd, Register rs1, int16_t imm12); + void fsw(FPURegister source, Register base, int16_t imm12); + void fmadd_s(FPURegister rd, FPURegister rs1, FPURegister rs2, + FPURegister rs3, RoundingMode frm = RNE); + void fmsub_s(FPURegister rd, FPURegister rs1, FPURegister rs2, + FPURegister rs3, RoundingMode frm = RNE); + void fnmsub_s(FPURegister rd, FPURegister rs1, FPURegister rs2, + FPURegister rs3, RoundingMode frm = RNE); + void fnmadd_s(FPURegister rd, FPURegister rs1, FPURegister rs2, + FPURegister rs3, RoundingMode frm = RNE); + void fadd_s(FPURegister rd, FPURegister rs1, FPURegister rs2, + RoundingMode frm = RNE); + void fsub_s(FPURegister rd, FPURegister rs1, FPURegister rs2, + RoundingMode frm = RNE); + void fmul_s(FPURegister rd, FPURegister rs1, FPURegister rs2, + RoundingMode frm = RNE); + void fdiv_s(FPURegister rd, FPURegister rs1, FPURegister rs2, + RoundingMode frm = RNE); + void fsqrt_s(FPURegister rd, FPURegister rs1, RoundingMode frm = RNE); + void fsgnj_s(FPURegister rd, FPURegister rs1, FPURegister rs2); + void fsgnjn_s(FPURegister rd, FPURegister rs1, FPURegister rs2); + void fsgnjx_s(FPURegister rd, FPURegister rs1, FPURegister rs2); + void fmin_s(FPURegister rd, FPURegister rs1, FPURegister rs2); + void fmax_s(FPURegister rd, FPURegister rs1, FPURegister rs2); + void fcvt_w_s(Register rd, FPURegister rs1, RoundingMode frm = RNE); + void fcvt_wu_s(Register rd, FPURegister rs1, RoundingMode frm = RNE); + void fmv_x_w(Register rd, FPURegister rs1); + void feq_s(Register rd, FPURegister rs1, FPURegister rs2); + void flt_s(Register rd, FPURegister rs1, FPURegister rs2); + void fle_s(Register rd, FPURegister rs1, FPURegister rs2); + void fclass_s(Register rd, FPURegister rs1); + void fcvt_s_w(FPURegister rd, Register rs1, RoundingMode frm = RNE); + void fcvt_s_wu(FPURegister rd, Register rs1, RoundingMode frm = RNE); + void fmv_w_x(FPURegister rd, Register rs1); + + // RV64F Standard Extension (in addition to RV32F) + void fcvt_l_s(Register rd, FPURegister rs1, RoundingMode frm = RNE); + void fcvt_lu_s(Register rd, FPURegister rs1, RoundingMode frm = RNE); + void fcvt_s_l(FPURegister rd, Register rs1, RoundingMode frm = RNE); + void fcvt_s_lu(FPURegister rd, Register rs1, RoundingMode frm = RNE); + + // RV32D Standard Extension + void fld(FPURegister rd, Register rs1, int16_t imm12); + void fsd(FPURegister source, Register base, int16_t imm12); + void fmadd_d(FPURegister rd, FPURegister rs1, FPURegister rs2, + FPURegister rs3, RoundingMode frm = RNE); + void fmsub_d(FPURegister rd, FPURegister rs1, FPURegister rs2, + FPURegister rs3, RoundingMode frm = RNE); + void fnmsub_d(FPURegister rd, FPURegister rs1, FPURegister rs2, + FPURegister rs3, RoundingMode frm = RNE); + void fnmadd_d(FPURegister rd, FPURegister rs1, FPURegister rs2, + FPURegister rs3, RoundingMode frm = RNE); + void fadd_d(FPURegister rd, FPURegister rs1, FPURegister rs2, + RoundingMode frm = RNE); + void fsub_d(FPURegister rd, FPURegister rs1, FPURegister rs2, + RoundingMode frm = RNE); + void fmul_d(FPURegister rd, FPURegister rs1, FPURegister rs2, + RoundingMode frm = RNE); + void fdiv_d(FPURegister rd, FPURegister rs1, FPURegister rs2, + RoundingMode frm = RNE); + void fsqrt_d(FPURegister rd, FPURegister rs1, RoundingMode frm = RNE); + void fsgnj_d(FPURegister rd, FPURegister rs1, FPURegister rs2); + void fsgnjn_d(FPURegister rd, FPURegister rs1, FPURegister rs2); + void fsgnjx_d(FPURegister rd, FPURegister rs1, FPURegister rs2); + void fmin_d(FPURegister rd, FPURegister rs1, FPURegister rs2); + void fmax_d(FPURegister rd, FPURegister rs1, FPURegister rs2); + void fcvt_s_d(FPURegister rd, FPURegister rs1, RoundingMode frm = RNE); + void fcvt_d_s(FPURegister rd, FPURegister rs1, RoundingMode frm = RNE); + void feq_d(Register rd, FPURegister rs1, FPURegister rs2); + void flt_d(Register rd, FPURegister rs1, FPURegister rs2); + void fle_d(Register rd, FPURegister rs1, FPURegister rs2); + void fclass_d(Register rd, FPURegister rs1); + void fcvt_w_d(Register rd, FPURegister rs1, RoundingMode frm = RNE); + void fcvt_wu_d(Register rd, FPURegister rs1, RoundingMode frm = RNE); + void fcvt_d_w(FPURegister rd, Register rs1, RoundingMode frm = RNE); + void fcvt_d_wu(FPURegister rd, Register rs1, RoundingMode frm = RNE); + + // RV64D Standard Extension (in addition to RV32D) + void fcvt_l_d(Register rd, FPURegister rs1, RoundingMode frm = RNE); + void fcvt_lu_d(Register rd, FPURegister rs1, RoundingMode frm = RNE); + void fmv_x_d(Register rd, FPURegister rs1); + void fcvt_d_l(FPURegister rd, Register rs1, RoundingMode frm = RNE); + void fcvt_d_lu(FPURegister rd, Register rs1, RoundingMode frm = RNE); + void fmv_d_x(FPURegister rd, Register rs1); + + // Privileged + void uret(); + void sret(); + void mret(); + void wfi(); + void sfence_vma(Register rs1, Register rs2); + + // Assembler Pseudo Instructions (Tables 25.2, 25.3, RISC-V Unprivileged ISA) + void nop(); + void RV_li(Register rd, int64_t imm); + // Returns the number of instructions required to load the immediate + static int li_count(int64_t imm); + // Loads an immediate, always using 8 instructions, regardless of the value, + // so that it can be modified later. + void li_constant(Register rd, int64_t imm); + + void mv(Register rd, Register rs) { addi(rd, rs, 0); } + void not_(Register rd, Register rs) { xori(rd, rs, -1); } + void neg(Register rd, Register rs) { sub(rd, zero_reg, rs); } + void negw(Register rd, Register rs) { subw(rd, zero_reg, rs); } + void sext_w(Register rd, Register rs) { addiw(rd, rs, 0); } + void seqz(Register rd, Register rs) { sltiu(rd, rs, 1); } + void snez(Register rd, Register rs) { sltu(rd, zero_reg, rs); } + void sltz(Register rd, Register rs) { slt(rd, rs, zero_reg); } + void sgtz(Register rd, Register rs) { slt(rd, zero_reg, rs); } + + void fmv_s(FPURegister rd, FPURegister rs) { fsgnj_s(rd, rs, rs); } + void fabs_s(FPURegister rd, FPURegister rs) { fsgnjx_s(rd, rs, rs); } + void fneg_s(FPURegister rd, FPURegister rs) { fsgnjn_s(rd, rs, rs); } + void fmv_d(FPURegister rd, FPURegister rs) { fsgnj_d(rd, rs, rs); } + void fabs_d(FPURegister rd, FPURegister rs) { fsgnjx_d(rd, rs, rs); } + void fneg_d(FPURegister rd, FPURegister rs) { fsgnjn_d(rd, rs, rs); } + + void beqz(Register rs, int16_t imm13) { beq(rs, zero_reg, imm13); } + inline void beqz(Register rs1, Label* L) { beqz(rs1, branch_offset(L)); } + void bnez(Register rs, int16_t imm13) { bne(rs, zero_reg, imm13); } + inline void bnez(Register rs1, Label* L) { bnez(rs1, branch_offset(L)); } + void blez(Register rs, int16_t imm13) { bge(zero_reg, rs, imm13); } + inline void blez(Register rs1, Label* L) { blez(rs1, branch_offset(L)); } + void bgez(Register rs, int16_t imm13) { bge(rs, zero_reg, imm13); } + inline void bgez(Register rs1, Label* L) { bgez(rs1, branch_offset(L)); } + void bltz(Register rs, int16_t imm13) { blt(rs, zero_reg, imm13); } + inline void bltz(Register rs1, Label* L) { bltz(rs1, branch_offset(L)); } + void bgtz(Register rs, int16_t imm13) { blt(zero_reg, rs, imm13); } + + inline void bgtz(Register rs1, Label* L) { bgtz(rs1, branch_offset(L)); } + void bgt(Register rs1, Register rs2, int16_t imm13) { blt(rs2, rs1, imm13); } + inline void bgt(Register rs1, Register rs2, Label* L) { + bgt(rs1, rs2, branch_offset(L)); + } + void ble(Register rs1, Register rs2, int16_t imm13) { bge(rs2, rs1, imm13); } + inline void ble(Register rs1, Register rs2, Label* L) { + ble(rs1, rs2, branch_offset(L)); + } + void bgtu(Register rs1, Register rs2, int16_t imm13) { + bltu(rs2, rs1, imm13); + } + inline void bgtu(Register rs1, Register rs2, Label* L) { + bgtu(rs1, rs2, branch_offset(L)); + } + void bleu(Register rs1, Register rs2, int16_t imm13) { + bgeu(rs2, rs1, imm13); + } + inline void bleu(Register rs1, Register rs2, Label* L) { + bleu(rs1, rs2, branch_offset(L)); + } + + // TODO: Replace uses of ToRegister with names once they are properly defined + void j(int32_t imm21) { jal(zero_reg, imm21); } + inline void j(Label* L) { j(jump_offset(L)); } + void jal(int32_t imm21) { jal(ToRegister(1), imm21); } + inline void jal(Label* L) { jal(jump_offset(L)); } + void jr(Register rs) { jalr(zero_reg, rs, 0); } + void jr(Register rs, int32_t imm12) { jalr(zero_reg, rs, imm12); } + void jalr(Register rs, int32_t imm12) { jalr(ToRegister(1), rs, imm12); } + void jalr(Register rs) { jalr(ToRegister(1), rs, 0); } + void ret() { jalr(zero_reg, ToRegister(1), 0); } + void call(int32_t offset) { + auipc(ToRegister(1), (offset >> 12) + ((offset & 0x800) >> 11)); + jalr(ToRegister(1), ToRegister(1), offset << 20 >> 20); + } + + // Read instructions-retired counter + void rdinstret(Register rd) { csrrs(rd, csr_instret, zero_reg); } + void rdinstreth(Register rd) { csrrs(rd, csr_instreth, zero_reg); } + void rdcycle(Register rd) { csrrs(rd, csr_cycle, zero_reg); } + void rdcycleh(Register rd) { csrrs(rd, csr_cycleh, zero_reg); } + void rdtime(Register rd) { csrrs(rd, csr_time, zero_reg); } + void rdtimeh(Register rd) { csrrs(rd, csr_timeh, zero_reg); } + + void csrr(Register rd, ControlStatusReg csr) { csrrs(rd, csr, zero_reg); } + void csrw(ControlStatusReg csr, Register rs) { csrrw(zero_reg, csr, rs); } + void csrs(ControlStatusReg csr, Register rs) { csrrs(zero_reg, csr, rs); } + void csrc(ControlStatusReg csr, Register rs) { csrrc(zero_reg, csr, rs); } + + void csrwi(ControlStatusReg csr, uint8_t imm) { csrrwi(zero_reg, csr, imm); } + void csrsi(ControlStatusReg csr, uint8_t imm) { csrrsi(zero_reg, csr, imm); } + void csrci(ControlStatusReg csr, uint8_t imm) { csrrci(zero_reg, csr, imm); } + + void frcsr(Register rd) { csrrs(rd, csr_fcsr, zero_reg); } + void fscsr(Register rd, Register rs) { csrrw(rd, csr_fcsr, rs); } + void fscsr(Register rs) { csrrw(zero_reg, csr_fcsr, rs); } + + void frrm(Register rd) { csrrs(rd, csr_frm, zero_reg); } + void fsrm(Register rd, Register rs) { csrrw(rd, csr_frm, rs); } + void fsrm(Register rs) { csrrw(zero_reg, csr_frm, rs); } + + void frflags(Register rd) { csrrs(rd, csr_fflags, zero_reg); } + void fsflags(Register rd, Register rs) { csrrw(rd, csr_fflags, rs); } + void fsflags(Register rs) { csrrw(zero_reg, csr_fflags, rs); } + + // Other pseudo instructions that are not part of RISCV pseudo assemly + void nor(Register rd, Register rs, Register rt) { + or_(rd, rs, rt); + not_(rd, rd); + } + + void sync() { fence(0b1111, 0b1111); } + void break_(uint32_t code, bool break_as_stop = false); + void stop(uint32_t code = kMaxStopCode); + + // 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 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 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, 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); + void dq(uint64_t data); + void dp(uintptr_t data) { dq(data); } + 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 IsBranch(Instr instr); + static bool IsJump(Instr instr); + static bool IsJal(Instr instr); + static bool IsJalr(Instr instr); + static bool IsLui(Instr instr); + static bool IsAuipc(Instr instr); + static bool IsAddiw(Instr instr); + static bool IsAddi(Instr instr); + static bool IsSlli(Instr instr); + + void CheckTrampolinePool(); + + inline int UnboundLabelsCount() { return unbound_labels_count_; } + + 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 + }; + + // Determine whether need to adjust base and offset of memroy load/store + bool NeedAdjustBaseAndOffset( + const MemOperand& src, OffsetAccessType = OffsetAccessType::SINGLE_ACCESS, + int second_Access_add_to_offset = 4); + + // Helper function for memory load/store using base register and offset. + void AdjustBaseAndOffset( + MemOperand* src, Register scratch, + 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_; } + + bool is_trampoline_emitted() const { return trampoline_emitted_; } + + // 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(); + } + } + + 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; + + // Code 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_; + + // Code emission. + inline void CheckBuffer(); + void GrowBuffer(); + inline void emit(Instr x); + inline void emit(uint64_t x); + template + inline void EmitHelper(T x); + + static void disassembleInstr(Instr instr); + + // Instruction generation. + + // ----- Top-level instruction formats match those in the ISA manual + // (R, I, S, B, U, J). These match the formats defined in LLVM's + // RISCVInstrFormats.td. + void GenInstrR(uint8_t funct7, uint8_t funct3, Opcode opcode, Register rd, + Register rs1, Register rs2); + void GenInstrR(uint8_t funct7, uint8_t funct3, Opcode opcode, FPURegister rd, + FPURegister rs1, FPURegister rs2); + void GenInstrR(uint8_t funct7, uint8_t funct3, Opcode opcode, Register rd, + FPURegister rs1, Register rs2); + void GenInstrR(uint8_t funct7, uint8_t funct3, Opcode opcode, FPURegister rd, + Register rs1, Register rs2); + void GenInstrR(uint8_t funct7, uint8_t funct3, Opcode opcode, FPURegister rd, + FPURegister rs1, Register rs2); + void GenInstrR(uint8_t funct7, uint8_t funct3, Opcode opcode, Register rd, + FPURegister rs1, FPURegister rs2); + void GenInstrR4(uint8_t funct2, Opcode opcode, Register rd, Register rs1, + Register rs2, Register rs3, RoundingMode frm); + void GenInstrR4(uint8_t funct2, Opcode opcode, FPURegister rd, + FPURegister rs1, FPURegister rs2, FPURegister rs3, + RoundingMode frm); + void GenInstrRAtomic(uint8_t funct5, bool aq, bool rl, uint8_t funct3, + Register rd, Register rs1, Register rs2); + void GenInstrRFrm(uint8_t funct7, Opcode opcode, Register rd, Register rs1, + Register rs2, RoundingMode frm); + void GenInstrI(uint8_t funct3, Opcode opcode, Register rd, Register rs1, + int16_t imm12); + void GenInstrI(uint8_t funct3, Opcode opcode, FPURegister rd, Register rs1, + int16_t imm12); + void GenInstrIShift(bool arithshift, uint8_t funct3, Opcode opcode, + Register rd, Register rs1, uint8_t shamt); + void GenInstrIShiftW(bool arithshift, uint8_t funct3, Opcode opcode, + Register rd, Register rs1, uint8_t shamt); + void GenInstrS(uint8_t funct3, Opcode opcode, Register rs1, Register rs2, + int16_t imm12); + void GenInstrS(uint8_t funct3, Opcode opcode, Register rs1, FPURegister rs2, + int16_t imm12); + void GenInstrB(uint8_t funct3, Opcode opcode, Register rs1, Register rs2, + int16_t imm12); + void GenInstrU(Opcode opcode, Register rd, int32_t imm20); + void GenInstrJ(Opcode opcode, Register rd, int32_t imm20); + + // ----- Instruction class templates match those in LLVM's RISCVInstrInfo.td + void GenInstrBranchCC_rri(uint8_t funct3, Register rs1, Register rs2, + int16_t imm12); + void GenInstrLoad_ri(uint8_t funct3, Register rd, Register rs1, + int16_t imm12); + void GenInstrStore_rri(uint8_t funct3, Register rs1, Register rs2, + int16_t imm12); + void GenInstrALU_ri(uint8_t funct3, Register rd, Register rs1, int16_t imm12); + void GenInstrShift_ri(bool arithshift, uint8_t funct3, Register rd, + Register rs1, uint8_t shamt); + void GenInstrALU_rr(uint8_t funct7, uint8_t funct3, Register rd, Register rs1, + Register rs2); + void GenInstrCSR_ir(uint8_t funct3, Register rd, ControlStatusReg csr, + Register rs1); + void GenInstrCSR_ii(uint8_t funct3, Register rd, ControlStatusReg csr, + uint8_t rs1); + void GenInstrShiftW_ri(bool arithshift, uint8_t funct3, Register rd, + Register rs1, uint8_t shamt); + void GenInstrALUW_rr(uint8_t funct7, uint8_t funct3, Register rd, + Register rs1, Register rs2); + void GenInstrPriv(uint8_t funct7, Register rs1, Register rs2); + void GenInstrLoadFP_ri(uint8_t funct3, FPURegister rd, Register rs1, + int16_t imm12); + void GenInstrStoreFP_rri(uint8_t funct3, Register rs1, FPURegister rs2, + int16_t imm12); + void GenInstrALUFP_rr(uint8_t funct7, uint8_t funct3, FPURegister rd, + FPURegister rs1, FPURegister rs2); + void GenInstrALUFP_rr(uint8_t funct7, uint8_t funct3, FPURegister rd, + Register rs1, Register rs2); + void GenInstrALUFP_rr(uint8_t funct7, uint8_t funct3, FPURegister rd, + FPURegister rs1, Register rs2); + void GenInstrALUFP_rr(uint8_t funct7, uint8_t funct3, Register rd, + FPURegister rs1, Register rs2); + void GenInstrALUFP_rr(uint8_t funct7, uint8_t funct3, Register rd, + FPURegister rs1, FPURegister rs2); + + // 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_ = false; + 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_; + + RegList scratch_register_list_; + + private: + void AllocateAndInstallRequestedHeapObjects(Isolate* isolate); + + int WriteCodeComments(); + + friend class RegExpMacroAssemblerRISCV; + friend class RelocInfo; + friend class BlockTrampolinePoolScope; + friend class EnsureSpace; +}; + +class EnsureSpace { + public: + explicit inline EnsureSpace(Assembler* assembler); +}; + +class V8_EXPORT_PRIVATE UseScratchRegisterScope { + public: + explicit UseScratchRegisterScope(Assembler* assembler); + ~UseScratchRegisterScope(); + + Register Acquire(); + bool hasAvailable() const; + + private: + RegList* available_; + RegList old_available_; +}; + +} // namespace internal +} // namespace v8 + +#endif // V8_CODEGEN_RISCV_ASSEMBLER_RISCV_H_ Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/codegen/riscv64/constants-riscv64.cc =================================================================== --- /dev/null +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/codegen/riscv64/constants-riscv64.cc @@ -0,0 +1,111 @@ +// 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_RISCV64 + +#include "src/codegen/riscv64/constants-riscv64.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_[kNumSimuRegisters] = { + "zero_reg", "ra", "sp", "gp", "tp", "t0", "t1", "t2", "fp", "s1", "a0", + "a1", "a2", "a3", "a4", "a5", "a6", "a7", "s2", "s3", "s4", "s5", + "s6", "s7", "s8", "s9", "s10", "s11", "t3", "t4", "t5", "t6", "pc"}; + +// List of alias names which can be used when referring to RISC-V registers. +const Registers::RegisterAlias Registers::aliases_[] = { + {0, "zero"}, + {33, "pc"}, + {8, "s0"}, + {8, "s0_fp"}, + {kInvalidRegister, nullptr}}; + +const char* Registers::Name(int reg) { + const char* result; + if ((0 <= reg) && (reg < kNumSimuRegisters)) { + result = names_[reg]; + } else { + result = "noreg"; + } + return result; +} + +int Registers::Number(const char* name) { + // Look through the canonical names. + for (int i = 0; i < kNumSimuRegisters; 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"}; +*/ +const char* FPURegisters::names_[kNumFPURegisters] = { + "ft0", "ft1", "ft2", "ft3", "ft4", "ft5", "ft6", "ft7", + "fs0", "fs1", "fa0", "fa1", "fa2", "fa3", "fa4", "fa5", + "fa6", "fa7", "fs2", "fs3", "fs4", "fs5", "fs6", "fs7", + "fs8", "fs9", "fs10", "fs11", "ft8", "ft9", "ft10", "ft11"}; + +// List of alias names which can be used when referring to RISC-V FP 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; +} + +} // namespace internal +} // namespace v8 + +#endif // V8_TARGET_ARCH_RISCV64 Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/codegen/riscv64/constants-riscv64.h =================================================================== --- /dev/null +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/codegen/riscv64/constants-riscv64.h @@ -0,0 +1,947 @@ +// 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_RISCV_CONSTANTS_RISCV_H_ +#define V8_CODEGEN_RISCV_CONSTANTS_RISCV_H_ + +#include "src/base/logging.h" +#include "src/base/macros.h" +#include "src/common/globals.h" + +// UNIMPLEMENTED_ macro for RISCV. +#ifdef DEBUG +#define UNIMPLEMENTED_RISCV() \ + v8::internal::PrintF("%s, \tline %d: \tfunction %s not implemented. \n", \ + __FILE__, __LINE__, __func__) +#else +#define UNIMPLEMENTED_RISCV() +#endif + +#define UNSUPPORTED_RISCV() v8::internal::PrintF("Unsupported instruction.\n") + +enum Endianness { kLittle, kBig }; + +#if defined(V8_TARGET_LITTLE_ENDIAN) +static const Endianness kArchEndian = kLittle; +#elif defined(V8_TARGET_BIG_ENDIAN) +static const Endianness kArchEndian = kBig; +#else +#error Unknown endianness +#endif + +#if defined(V8_TARGET_LITTLE_ENDIAN) +const uint32_t kLeastSignificantByteInInt32Offset = 0; +const uint32_t kLessSignificantWordInDoublewordOffset = 0; +#elif defined(V8_TARGET_BIG_ENDIAN) +const uint32_t kLeastSignificantByteInInt32Offset = 3; +const uint32_t kLessSignificantWordInDoublewordOffset = 4; +#else +#error Unknown endianness +#endif + +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS +#endif +#include + +// Defines constants and accessor classes to assemble, disassemble and +// simulate RISC-V instructions. +// +// See: The RISC-V Instruction Set Manual +// Volume I: User-Level ISA +// Try https://content.riscv.org/wp-content/uploads/2017/05/riscv-spec-v2.2.pdf. + +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 pc. +const int kNumSimuRegisters = 33; + +// 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; + +// '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. +// TODO(sigurds): Choose best value. +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_[kNumSimuRegisters]; + 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_[]; +}; + +// ----------------------------------------------------------------------------- +// Instructions encoding constants. + +// On RISCV all instructions are 32 bits. +using Instr = int32_t; + +// Special Software Interrupt codes when used in the presence of the RISC-V +// simulator. +enum SoftwareInterruptCodes { + // Transition to C code. + call_rt_redirected = 0xfffff +}; + +// On RISC-V Simulator breakpoints can have different codes: +// - Breaks between 0 and kMaxWatchpointCode are treated as simple watchpoints, +// the simulator will run through them and print the registers. +// - Breaks between kMaxWatchpointCode and kMaxStopCode are treated as stop() +// instructions (see Assembler::stop()). +// - Breaks larger than kMaxStopCode are simple breaks, dropping you into the +// debugger. +const uint32_t kMaxWatchpointCode = 31; +const uint32_t kMaxStopCode = 127; +STATIC_ASSERT(kMaxWatchpointCode < kMaxStopCode); + +// ----- Fields offset and length. +// RISCV constants +const int kBaseOpcodeShift = 0; +const int kBaseOpcodeBits = 7; +const int kFunct7Shift = 25; +const int kFunct7Bits = 7; +const int kFunct5Shift = 27; +const int kFunct5Bits = 5; +const int kFunct3Shift = 12; +const int kFunct3Bits = 3; +const int kFunct2Shift = 25; +const int kFunct2Bits = 2; +const int kRs1Shift = 15; +const int kRs1Bits = 5; +const int kRs2Shift = 20; +const int kRs2Bits = 5; +const int kRs3Shift = 27; +const int kRs3Bits = 5; +const int kRdShift = 7; +const int kRdBits = 5; +const int kRlShift = 25; +const int kAqShift = 26; +const int kImm12Shift = 20; +const int kImm12Bits = 12; +const int kShamtShift = 20; +const int kShamtBits = 5; +const int kShamtWShift = 20; +const int kShamtWBits = 6; +const int kArithShiftShift = 30; +const int kImm20Shift = 12; +const int kImm20Bits = 20; +const int kCsrShift = 20; +const int kCsrBits = 12; +const int kMemOrderBits = 4; +const int kPredOrderShift = 24; +const int kSuccOrderShift = 20; + +// RISCV Instruction bit masks +const uint32_t kBaseOpcodeMask = ((1 << kBaseOpcodeBits) - 1) << kBaseOpcodeShift; +const uint32_t kFunct3Mask = ((1 << kFunct3Bits) - 1) << kFunct3Shift; +const uint32_t kFunct5Mask = ((1 << kFunct5Bits) - 1) << kFunct5Shift; +const uint32_t kFunct7Mask = ((1 << kFunct7Bits) - 1) << kFunct7Shift; +const uint32_t kFunct2Mask = 0b11 << kFunct7Shift; +const uint32_t kRTypeMask = kBaseOpcodeMask | kFunct3Mask | kFunct7Mask; +const uint32_t kRATypeMask = kBaseOpcodeMask | kFunct3Mask | kFunct5Mask; +const uint32_t kRFPTypeMask = kBaseOpcodeMask | kFunct7Mask; +const uint32_t kR4TypeMask = kBaseOpcodeMask | kFunct3Mask | kFunct2Mask; +const uint32_t kITypeMask = kBaseOpcodeMask | kFunct3Mask; +const uint32_t kSTypeMask = kBaseOpcodeMask | kFunct3Mask; +const uint32_t kBTypeMask = kBaseOpcodeMask | kFunct3Mask; +const uint32_t kUTypeMask = kBaseOpcodeMask; +const uint32_t kJTypeMask = kBaseOpcodeMask; +const uint32_t kRs1FieldMask = ((1 << kRs1Bits) - 1) << kRs1Shift; +const uint32_t kRs2FieldMask = ((1 << kRs2Bits) - 1) << kRs2Shift; +const uint32_t kRs3FieldMask = ((1 << kRs3Bits) - 1) << kRs3Shift; +const uint32_t kRdFieldMask = ((1 << kRdBits) - 1) << kRdShift; +const uint32_t kBImm12Mask = kFunct7Mask | kRdFieldMask; +const uint32_t kImm20Mask = ((1 << kImm20Bits) - 1) << kImm20Shift; +const uint32_t kImm12Mask = ((1 << kImm12Bits) - 1) << kImm12Shift; + +// RISCV CSR related bit mask and shift +const uint32_t kFcsrFlagsBits = 5; +const uint32_t kFcsrFlagsMask = (1 << kFcsrFlagsBits) - 1; +const uint32_t kFcsrFrmBits = 3; +const uint32_t kFcsrFrmShift = kFcsrFlagsBits; +const uint32_t kFcsrFrmMask = ((1 << kFcsrFrmBits) - 1) << kFcsrFrmShift; +const uint32_t kFcsrBits = kFcsrFlagsBits + kFcsrFrmBits; +const uint32_t kFcsrMask = kFcsrFlagsMask | kFcsrFrmMask; + +// Original MIPS constants +// FIXME (RISCV): to be cleaned up +const uint32_t kImm16Shift = 0; +const uint32_t kImm16Bits = 16; +const uint32_t kImm16Mask = ((1 << kImm16Bits) - 1) << kImm16Shift; +// end of FIXME (RISCV): to be cleaned up + +// ----- RISCV Base Opcodes + +enum BaseOpcode : uint32_t { + +}; + +// ----- RISC-V Opcodes and Function Fields. +enum Opcode : uint32_t { + LOAD = 0b0000011, // I form: LB LH LW LBU LHU + LOAD_FP = 0b0000111, // I form: FLW FLD FLQ + MISC_MEM = 0b0001111, // I special form: FENCE FENCE.I + OP_IMM = 0b0010011, // I form: ADDI SLTI SLTIU XORI ORI ANDI SLLI SRLI SARI + // Note: SLLI/SRLI/SRAI I form first, then func3 001/101 => R type + AUIPC = 0b0010111, // U form: AUIPC + OP_IMM_32 = 0b0011011, // I form: ADDIW SLLIW SRLIW SRAIW + // Note: SRLIW SRAIW I form first, then func3 101 special shift encoding + STORE = 0b0100011, // S form: SB SH SW SD + STORE_FP = 0b0100111, // S form: FSW FSD FSQ + AMO = 0b0101111, // R form: All A instructions + OP = 0b0110011, // R: ADD SUB SLL SLT SLTU XOR SRL SRA OR AND and 32M set + LUI = 0b0110111, // U form: LUI + OP_32 = 0b0111011, // R: ADDW SUBW SLLW SRLW SRAW MULW DIVW DIVUW REMW REMUW + MADD = 0b1000011, // R4 type: FMADD.S FMADD.D FMADD.Q + MSUB = 0b1000111, // R4 type: FMSUB.S FMSUB.D FMSUB.Q + NMSUB = 0b1001011, // R4 type: FNMSUB.S FNMSUB.D FNMSUB.Q + NMADD = 0b1001111, // R4 type: FNMADD.S FNMADD.D FNMADD.Q + OP_FP = 0b1010011, // R type: Q ext + BRANCH = 0b1100011, // B form: BEQ BNE, BLT, BGE, BLTU BGEU + JALR = 0b1100111, // I form: JALR + JAL = 0b1101111, // J form: JAL + SYSTEM = 0b1110011, // I form: ECALL EBREAK Zicsr ext + + // Note use RO (RiscV Opcode) prefix + // RV32I Base Instruction Set + RO_LUI = LUI, + RO_AUIPC = AUIPC, + RO_JAL = JAL, + RO_JALR = JALR | (0b000 << kFunct3Shift), + RO_BEQ = BRANCH | (0b000 << kFunct3Shift), + RO_BNE = BRANCH | (0b001 << kFunct3Shift), + RO_BLT = BRANCH | (0b100 << kFunct3Shift), + RO_BGE = BRANCH | (0b101 << kFunct3Shift), + RO_BLTU = BRANCH | (0b110 << kFunct3Shift), + RO_BGEU = BRANCH | (0b111 << kFunct3Shift), + RO_LB = LOAD | (0b000 << kFunct3Shift), + RO_LH = LOAD | (0b001 << kFunct3Shift), + RO_LW = LOAD | (0b010 << kFunct3Shift), + RO_LBU = LOAD | (0b100 << kFunct3Shift), + RO_LHU = LOAD | (0b101 << kFunct3Shift), + RO_SB = STORE | (0b000 << kFunct3Shift), + RO_SH = STORE | (0b001 << kFunct3Shift), + RO_SW = STORE | (0b010 << kFunct3Shift), + RO_ADDI = OP_IMM | (0b000 << kFunct3Shift), + RO_SLTI = OP_IMM | (0b010 << kFunct3Shift), + RO_SLTIU = OP_IMM | (0b011 << kFunct3Shift), + RO_XORI = OP_IMM | (0b100 << kFunct3Shift), + RO_ORI = OP_IMM | (0b110 << kFunct3Shift), + RO_ANDI = OP_IMM | (0b111 << kFunct3Shift), + RO_SLLI = OP_IMM | (0b001 << kFunct3Shift), + RO_SRLI = OP_IMM | (0b101 << kFunct3Shift), + // RO_SRAI = OP_IMM | (0b101 << kFunct3Shift), // Same as SRLI, use func7 + RO_ADD = OP | (0b000 << kFunct3Shift) | (0b0000000 << kFunct7Shift), + RO_SUB = OP | (0b000 << kFunct3Shift) | (0b0100000 << kFunct7Shift), + RO_SLL = OP | (0b001 << kFunct3Shift) | (0b0000000 << kFunct7Shift), + RO_SLT = OP | (0b010 << kFunct3Shift) | (0b0000000 << kFunct7Shift), + RO_SLTU = OP | (0b011 << kFunct3Shift) | (0b0000000 << kFunct7Shift), + RO_XOR = OP | (0b100 << kFunct3Shift) | (0b0000000 << kFunct7Shift), + RO_SRL = OP | (0b101 << kFunct3Shift) | (0b0000000 << kFunct7Shift), + RO_SRA = OP | (0b101 << kFunct3Shift) | (0b0100000 << kFunct7Shift), + RO_OR = OP | (0b110 << kFunct3Shift) | (0b0000000 << kFunct7Shift), + RO_AND = OP | (0b111 << kFunct3Shift) | (0b0000000 << kFunct7Shift), + RO_FENCE = MISC_MEM | (0b000 << kFunct3Shift), + RO_ECALL = SYSTEM | (0b000 << kFunct3Shift), + // RO_EBREAK = SYSTEM | (0b000 << kFunct3Shift), // Same as ECALL, use imm12 + + // RV64I Base Instruction Set (in addition to RV32I) + RO_LWU = LOAD | (0b110 << kFunct3Shift), + RO_LD = LOAD | (0b011 << kFunct3Shift), + RO_SD = STORE | (0b011 << kFunct3Shift), + RO_ADDIW = OP_IMM_32 | (0b000 << kFunct3Shift), + RO_SLLIW = OP_IMM_32 | (0b001 << kFunct3Shift), + RO_SRLIW = OP_IMM_32 | (0b101 << kFunct3Shift), + // RO_SRAIW = OP_IMM_32 | (0b101 << kFunct3Shift), // Same as SRLIW, use func7 + RO_ADDW = OP_32 | (0b000 << kFunct3Shift) | (0b0000000 << kFunct7Shift), + RO_SUBW = OP_32 | (0b000 << kFunct3Shift) | (0b0100000 << kFunct7Shift), + RO_SLLW = OP_32 | (0b001 << kFunct3Shift) | (0b0000000 << kFunct7Shift), + RO_SRLW = OP_32 | (0b101 << kFunct3Shift) | (0b0000000 << kFunct7Shift), + RO_SRAW = OP_32 | (0b101 << kFunct3Shift) | (0b0100000 << kFunct7Shift), + + // RV32/RV64 Zifencei Standard Extension + RO_FENCE_I = MISC_MEM | (0b001 << kFunct3Shift), + + // RV32/RV64 Zicsr Standard Extension + RO_CSRRW = SYSTEM | (0b001 << kFunct3Shift), + RO_CSRRS = SYSTEM | (0b010 << kFunct3Shift), + RO_CSRRC = SYSTEM | (0b011 << kFunct3Shift), + RO_CSRRWI = SYSTEM | (0b101 << kFunct3Shift), + RO_CSRRSI = SYSTEM | (0b110 << kFunct3Shift), + RO_CSRRCI = SYSTEM | (0b111 << kFunct3Shift), + + // RV32M Standard Extension + RO_MUL = OP | (0b000 << kFunct3Shift) | (0b0000001 << kFunct7Shift), + RO_MULH = OP | (0b001 << kFunct3Shift) | (0b0000001 << kFunct7Shift), + RO_MULHSU = OP | (0b010 << kFunct3Shift) | (0b0000001 << kFunct7Shift), + RO_MULHU = OP | (0b011 << kFunct3Shift) | (0b0000001 << kFunct7Shift), + RO_DIV = OP | (0b100 << kFunct3Shift) | (0b0000001 << kFunct7Shift), + RO_DIVU = OP | (0b101 << kFunct3Shift) | (0b0000001 << kFunct7Shift), + RO_REM = OP | (0b110 << kFunct3Shift) | (0b0000001 << kFunct7Shift), + RO_REMU = OP | (0b111 << kFunct3Shift) | (0b0000001 << kFunct7Shift), + + // RV64M Standard Extension (in addition to RV32M) + RO_MULW = OP_32 | (0b000 << kFunct3Shift) | (0b0000001 << kFunct7Shift), + RO_DIVW = OP_32 | (0b100 << kFunct3Shift) | (0b0000001 << kFunct7Shift), + RO_DIVUW = OP_32 | (0b101 << kFunct3Shift) | (0b0000001 << kFunct7Shift), + RO_REMW = OP_32 | (0b110 << kFunct3Shift) | (0b0000001 << kFunct7Shift), + RO_REMUW = OP_32 | (0b111 << kFunct3Shift) | (0b0000001 << kFunct7Shift), + + // RV32A Standard Extension + RO_LR_W = AMO | (0b010 << kFunct3Shift) | (0b00010 << kFunct5Shift), + RO_SC_W = AMO | (0b010 << kFunct3Shift) | (0b00011 << kFunct5Shift), + RO_AMOSWAP_W = AMO | (0b010 << kFunct3Shift) | (0b00001 << kFunct5Shift), + RO_AMOADD_W = AMO | (0b010 << kFunct3Shift) | (0b00000 << kFunct5Shift), + RO_AMOXOR_W = AMO | (0b010 << kFunct3Shift) | (0b00100 << kFunct5Shift), + RO_AMOAND_W = AMO | (0b010 << kFunct3Shift) | (0b01100 << kFunct5Shift), + RO_AMOOR_W = AMO | (0b010 << kFunct3Shift) | (0b01000 << kFunct5Shift), + RO_AMOMIN_W = AMO | (0b010 << kFunct3Shift) | (0b10000 << kFunct5Shift), + RO_AMOMAX_W = AMO | (0b010 << kFunct3Shift) | (0b10100 << kFunct5Shift), + RO_AMOMINU_W = AMO | (0b010 << kFunct3Shift) | (0b11000 << kFunct5Shift), + RO_AMOMAXU_W = AMO | (0b010 << kFunct3Shift) | (0b11100 << kFunct5Shift), + + // RV64A Standard Extension (in addition to RV32A) + RO_LR_D = AMO | (0b011 << kFunct3Shift) | (0b00010 << kFunct5Shift), + RO_SC_D = AMO | (0b011 << kFunct3Shift) | (0b00011 << kFunct5Shift), + RO_AMOSWAP_D = AMO | (0b011 << kFunct3Shift) | (0b00001 << kFunct5Shift), + RO_AMOADD_D = AMO | (0b011 << kFunct3Shift) | (0b00000 << kFunct5Shift), + RO_AMOXOR_D = AMO | (0b011 << kFunct3Shift) | (0b00100 << kFunct5Shift), + RO_AMOAND_D = AMO | (0b011 << kFunct3Shift) | (0b01100 << kFunct5Shift), + RO_AMOOR_D = AMO | (0b011 << kFunct3Shift) | (0b01000 << kFunct5Shift), + RO_AMOMIN_D = AMO | (0b011 << kFunct3Shift) | (0b10000 << kFunct5Shift), + RO_AMOMAX_D = AMO | (0b011 << kFunct3Shift) | (0b10100 << kFunct5Shift), + RO_AMOMINU_D = AMO | (0b011 << kFunct3Shift) | (0b11000 << kFunct5Shift), + RO_AMOMAXU_D = AMO | (0b011 << kFunct3Shift) | (0b11100 << kFunct5Shift), + + // RV32F Standard Extension + RO_FLW = LOAD_FP | (0b010 << kFunct3Shift), + RO_FSW = STORE_FP | (0b010 << kFunct3Shift), + RO_FMADD_S = MADD | (0b00 << kFunct2Shift), + RO_FMSUB_S = MSUB | (0b00 << kFunct2Shift), + RO_FNMSUB_S = NMSUB | (0b00 << kFunct2Shift), + RO_FNMADD_S = NMADD | (0b00 << kFunct2Shift), + RO_FADD_S = OP_FP | (0b0000000 << kFunct7Shift), + RO_FSUB_S = OP_FP | (0b0000100 << kFunct7Shift), + RO_FMUL_S = OP_FP | (0b0001000 << kFunct7Shift), + RO_FDIV_S = OP_FP | (0b0001100 << kFunct7Shift), + RO_FSQRT_S = OP_FP | (0b0101100 << kFunct7Shift) | (0b00000 << kRs2Shift), + RO_FSGNJ_S = OP_FP | (0b000 << kFunct3Shift) | (0b0010000 << kFunct7Shift), + RO_FSGNJN_S = OP_FP | (0b001 << kFunct3Shift) | (0b0010000 << kFunct7Shift), + RO_FSQNJX_S = OP_FP | (0b010 << kFunct3Shift) | (0b0010000 << kFunct7Shift), + RO_FMIN_S = OP_FP | (0b000 << kFunct3Shift) | (0b0010100 << kFunct7Shift), + RO_FMAX_S = OP_FP | (0b001 << kFunct3Shift) | (0b0010100 << kFunct7Shift), + RO_FCVT_W_S = OP_FP | (0b1100000 << kFunct7Shift) | (0b00000 << kRs2Shift), + RO_FCVT_WU_S = OP_FP | (0b1100000 << kFunct7Shift) | (0b00001 << kRs2Shift), + RO_FMV = OP_FP | (0b1110000 << kFunct7Shift) | (0b000 << kFunct3Shift) | + (0b00000 << kRs2Shift), + RO_FEQ_S = OP_FP | (0b010 << kFunct3Shift) | (0b1010000 << kFunct7Shift), + RO_FLT_S = OP_FP | (0b001 << kFunct3Shift) | (0b1010000 << kFunct7Shift), + RO_FLE_S = OP_FP | (0b000 << kFunct3Shift) | (0b1010000 << kFunct7Shift), + RO_FCLASS_S = OP_FP | (0b001 << kFunct3Shift) | (0b1110000 << kFunct7Shift), + RO_FCVT_S_W = OP_FP | (0b1101000 << kFunct7Shift) | (0b00000 << kRs2Shift), + RO_FCVT_S_WU = OP_FP | (0b1101000 << kFunct7Shift) | (0b00001 << kRs2Shift), + RO_FMV_W_X = OP_FP | (0b000 << kFunct3Shift) | (0b1111000 << kFunct7Shift), + + // RV64F Standard Extension (in addition to RV32F) + RO_FCVT_L_S = OP_FP | (0b1100000 << kFunct7Shift) | (0b00010 << kRs2Shift), + RO_FCVT_LU_S = OP_FP | (0b1100000 << kFunct7Shift) | (0b00011 << kRs2Shift), + RO_FCVT_S_L = OP_FP | (0b1101000 << kFunct7Shift) | (0b00010 << kRs2Shift), + RO_FCVT_S_LU = OP_FP | (0b1101000 << kFunct7Shift) | (0b00011 << kRs2Shift), + + // RV32D Standard Extension + RO_FLD = LOAD_FP | (0b011 << kFunct3Shift), + RO_FSD = STORE_FP | (0b011 << kFunct3Shift), + RO_FMADD_D = MADD | (0b01 << kFunct2Shift), + RO_FMSUB_D = MSUB | (0b01 << kFunct2Shift), + RO_FNMSUB_D = NMSUB | (0b01 << kFunct2Shift), + RO_FNMADD_D = NMADD | (0b01 << kFunct2Shift), + RO_FADD_D = OP_FP | (0b0000001 << kFunct7Shift), + RO_FSUB_D = OP_FP | (0b0000101 << kFunct7Shift), + RO_FMUL_D = OP_FP | (0b0001001 << kFunct7Shift), + RO_FDIV_D = OP_FP | (0b0001101 << kFunct7Shift), + RO_FSQRT_D = OP_FP | (0b0101101 << kFunct7Shift) | (0b00000 << kRs2Shift), + RO_FSGNJ_D = OP_FP | (0b000 << kFunct3Shift) | (0b0010001 << kFunct7Shift), + RO_FSGNJN_D = OP_FP | (0b001 << kFunct3Shift) | (0b0010001 << kFunct7Shift), + RO_FSQNJX_D = OP_FP | (0b010 << kFunct3Shift) | (0b0010001 << kFunct7Shift), + RO_FMIN_D = OP_FP | (0b000 << kFunct3Shift) | (0b0010101 << kFunct7Shift), + RO_FMAX_D = OP_FP | (0b001 << kFunct3Shift) | (0b0010101 << kFunct7Shift), + RO_FCVT_S_D = OP_FP | (0b0100000 << kFunct7Shift) | (0b00001 << kRs2Shift), + RO_FCVT_D_S = OP_FP | (0b0100001 << kFunct7Shift) | (0b00000 << kRs2Shift), + RO_FEQ_D = OP_FP | (0b010 << kFunct3Shift) | (0b1010001 << kFunct7Shift), + RO_FLT_D = OP_FP | (0b001 << kFunct3Shift) | (0b1010001 << kFunct7Shift), + RO_FLE_D = OP_FP | (0b000 << kFunct3Shift) | (0b1010001 << kFunct7Shift), + RO_FCLASS_D = OP_FP | (0b001 << kFunct3Shift) | (0b1110001 << kFunct7Shift) | + (0b00000 << kRs2Shift), + RO_FCVT_W_D = OP_FP | (0b1100001 << kFunct7Shift) | (0b00000 << kRs2Shift), + RO_FCVT_WU_D = OP_FP | (0b1100001 << kFunct7Shift) | (0b00001 << kRs2Shift), + RO_FCVT_D_W = OP_FP | (0b1101001 << kFunct7Shift) | (0b00000 << kRs2Shift), + RO_FCVT_D_WU = OP_FP | (0b1101001 << kFunct7Shift) | (0b00001 << kRs2Shift), + + // RV64D Standard Extension (in addition to RV32D) + RO_FCVT_L_D = OP_FP | (0b1100001 << kFunct7Shift) | (0b00010 << kRs2Shift), + RO_FCVT_LU_D = OP_FP | (0b1100001 << kFunct7Shift) | (0b00011 << kRs2Shift), + RO_FMV_X_D = OP_FP | (0b000 << kFunct3Shift) | (0b1110001 << kFunct7Shift) | + (0b00000 << kRs2Shift), + RO_FCVT_D_L = OP_FP | (0b1101001 << kFunct7Shift) | (0b00010 << kRs2Shift), + RO_FCVT_D_LU = OP_FP | (0b1101001 << kFunct7Shift) | (0b00011 << kRs2Shift), + RO_FMV_D_X = OP_FP | (0b000 << kFunct3Shift) | (0b1111001 << kFunct7Shift) | + (0b00000 << kRs2Shift), +}; + +// ----- Emulated conditions. +// On RISC-V we use this enum to abstract from conditional branch instructions. +// The 'U' prefix is used to specify unsigned comparisons. +// Opposite conditions must be paired as odd/even numbers +// because 'NegateCondition' function flips LSB to negate condition. +enum 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. + less = 8, + greater_equal = 9, + less_equal = 10, + greater = 11, + cc_always = 12, + + // Aliases. + eq = equal, + ne = not_equal, + ge = greater_equal, + lt = less, + gt = greater, + le = less_equal, + al = cc_always, + ult = Uless, + uge = Ugreater_equal, + ule = Uless_equal, + ugt = Ugreater, +}; + +// 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; + default: + return cc; + } +} + +// ----- Coprocessor conditions. +enum FPUCondition { + kNoFPUCondition = -1, + EQ = 0x02, // Ordered and Equal + NE = 0x03, // Unordered or Not Equal + LT = 0x04, // Ordered and Less Than + GE = 0x05, // Ordered and Greater Than or Equal + LE = 0x06, // Ordered and Less Than or Equal + GT = 0x07, // Ordered and Greater Than +}; + +enum CheckForInexactConversion { + kCheckForInexactConversion, + kDontCheckForInexactConversion +}; + +enum class MaxMinKind : int { kMin = 0, kMax = 1 }; + +// ---------------------------------------------------------------------------- +// RISCV flags + +enum ControlStatusReg { + csr_fflags = 0x001, // Floating-Point Accrued Exceptions (RW) + csr_frm = 0x002, // Floating-Point Dynamic Rounding Mode (RW) + csr_fcsr = 0x003, // Floating-Point Control and Status Register (RW) + csr_cycle = 0xc00, // Cycle counter for RDCYCLE instruction (RO) + csr_time = 0xc01, // Timer for RDTIME instruction (RO) + csr_instret = 0xc02, // Insns-retired counter for RDINSTRET instruction (RO) + csr_cycleh = 0xc80, // Upper 32 bits of cycle, RV32I only (RO) + csr_timeh = 0xc81, // Upper 32 bits of time, RV32I only (RO) + csr_instreth = 0xc82 // Upper 32 bits of instret, RV32I only (RO) +}; + +enum FFlagsMask { + kInvalidOperation = 0b10000, // NV: Invalid + kDivideByZero = 0b1000, // DZ: Divide by Zero + kOverflow = 0b100, // OF: Overflow + kUnderflow = 0b10, // UF: Underflow + kInexact = 0b1 // NX: Inexact +}; + +enum RoundingMode { + RNE = 0b000, // Round to Nearest, ties to Even + RTZ = 0b001, // Round towards Zero + RDN = 0b010, // Round Down (towards -infinity) + RUP = 0b011, // Round Up (towards +infinity) + RMM = 0b100, // Round to Nearest, tiest to Max Magnitude + DYN = 0b111 // In instruction's rm field, selects dynamic rounding mode; + // In Rounding Mode register, Invalid +}; + +enum MemoryOdering { + PSI = 0b1000, // PI or SI + PSO = 0b0100, // PO or SO + PSR = 0b0010, // PR or SR + PSW = 0b0001, // PW or SW + PSIORW = PSI | PSO | PSR | PSW +}; + +enum FClassFlag { + kNegativeInfinity = 1, + kNegativeNormalNumber = 1 << 1, + kNegativeSubnormalNumber = 1 << 2, + kNegativeZero = 1 << 3, + kPositiveZero = 1 << 4, + kPositiveSubnormalNumber = 1 << 5, + kPositiveNormalNumber = 1 << 6, + kPositiveInfinity = 1 << 7, + kSignalingNaN = 1 << 8, + kQuietNaN = 1 << 9 +}; + +// ----------------------------------------------------------------------------- +// Hints. + +// Branch hints are not used on RISC-V. They are defined so that they can +// appear in shared function signatures, but will be ignored in RISC-V +// implementations. +enum Hint { no_hint = 0 }; + +inline Hint NegateHint(Hint hint) { return no_hint; } + +// ----------------------------------------------------------------------------- +// Specific instructions, constants, and masks. +// These constants are declared in assembler-riscv64.cc, as they use named +// registers and other constants. + +// An ECALL instruction, used for redirected real time call +const Instr rtCallRedirInstr = SYSTEM; // All other bits are 0s (i.e., ecall) +// An EBreak instruction, used for debugging and semi-hosting +const Instr kBreakInstr = SYSTEM | 1 << kImm12Shift; // ebreak + +constexpr uint8_t kInstrSize = 4; +constexpr uint8_t kInstrSizeLog2 = 2; + +class InstructionBase { + public: + enum { + // On RISC-V, PC cannot actually be directly accessed. We behave as if PC + // was always the value of the current instruction being executed. + kPCReadOffset = 0 + }; + + // Instruction type. + enum Type { + kRType, + kR4Type, // Special R4 for Q extension + kIType, + kSType, + kBType, + kUType, + kJType, + 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 RISC-V encoding. + inline Opcode BaseOpcodeValue() const { + return static_cast( + Bits(kBaseOpcodeShift + kBaseOpcodeBits - 1, kBaseOpcodeShift)); + } + + // Return the fields at their original place in the instruction encoding. + inline Opcode BaseOpcodeFieldRaw() const { + return static_cast(InstructionBits() & kBaseOpcodeMask); + } + + // Safe to call within R-type instructions + inline int Funct7FieldRaw() const { return InstructionBits() & kFunct7Mask; } + + // Safe to call within R-, I-, S-, or B-type instructions + inline int Funct3FieldRaw() const { return InstructionBits() & kFunct3Mask; } + + // Safe to call within R-, I-, S-, or B-type instructions + inline int Rs1FieldRawNoAssert() const { + return InstructionBits() & kRs1FieldMask; + } + + // Safe to call within R-, S-, or B-type instructions + inline int Rs2FieldRawNoAssert() const { + return InstructionBits() & kRs2FieldMask; + } + + // Safe to call within R4-type instructions + inline int Rs3FieldRawNoAssert() const { + return InstructionBits() & kRs3FieldMask; + } + + inline int32_t ITypeBits() const { return InstructionBits() & kITypeMask; } + + // Get the encoding type of the instruction. + inline Type InstructionType() const; + + protected: + InstructionBase() {} +}; + +template +class InstructionGetters : public T { + public: + inline int BaseOpcode() const { + return this->InstructionBits() & kBaseOpcodeMask; + } + + inline int Rs1Value() const { + DCHECK(this->InstructionType() == InstructionBase::kRType || + this->InstructionType() == InstructionBase::kR4Type || + this->InstructionType() == InstructionBase::kIType || + this->InstructionType() == InstructionBase::kSType || + this->InstructionType() == InstructionBase::kBType); + return this->Bits(kRs1Shift + kRs1Bits - 1, kRs1Shift); + } + + inline int Rs2Value() const { + DCHECK(this->InstructionType() == InstructionBase::kRType || + this->InstructionType() == InstructionBase::kR4Type || + this->InstructionType() == InstructionBase::kSType || + this->InstructionType() == InstructionBase::kBType); + return this->Bits(kRs2Shift + kRs2Bits - 1, kRs2Shift); + } + + inline int Rs3Value() const { + DCHECK(this->InstructionType() == InstructionBase::kR4Type); + return this->Bits(kRs3Shift + kRs3Bits - 1, kRs3Shift); + } + + inline int RdValue() const { + DCHECK(this->InstructionType() == InstructionBase::kRType || + this->InstructionType() == InstructionBase::kR4Type || + this->InstructionType() == InstructionBase::kIType || + this->InstructionType() == InstructionBase::kUType || + this->InstructionType() == InstructionBase::kJType); + return this->Bits(kRdShift + kRdBits - 1, kRdShift); + } + + inline int Funct7Value() const { + DCHECK(this->InstructionType() == InstructionBase::kRType); + return this->Bits(kFunct7Shift + kFunct7Bits - 1, kFunct7Shift); + } + + inline int Funct3Value() const { + DCHECK(this->InstructionType() == InstructionBase::kRType || + this->InstructionType() == InstructionBase::kIType || + this->InstructionType() == InstructionBase::kSType || + this->InstructionType() == InstructionBase::kBType); + return this->Bits(kFunct3Shift + kFunct3Bits - 1, kFunct3Shift); + } + + inline int Funct5Value() const { + DCHECK(this->InstructionType() == InstructionBase::kRType && + this->BaseOpcode() == OP_FP); + return this->Bits(kFunct5Shift + kFunct5Bits - 1, kFunct5Shift); + } + + inline int CsrValue() const { + DCHECK(this->InstructionType() == InstructionBase::kIType && + this->BaseOpcode() == SYSTEM); + return (this->Bits(kCsrShift + kCsrBits - 1, kCsrShift)); + } + + inline int RoundMode() const { + DCHECK((this->InstructionType() == InstructionBase::kRType || + this->InstructionType() == InstructionBase::kR4Type) && + this->BaseOpcode() == OP_FP); + return this->Bits(kFunct3Shift + kFunct3Bits - 1, kFunct3Shift); + } + + inline int MemoryOrder(bool is_pred) const { + DCHECK((this->InstructionType() == InstructionBase::kIType && + this->BaseOpcode() == MISC_MEM)); + if (is_pred) { + return this->Bits(kPredOrderShift + kMemOrderBits - 1, kPredOrderShift); + } else { + return this->Bits(kSuccOrderShift + kMemOrderBits - 1, kSuccOrderShift); + } + } + + inline int Imm12Value() const { + DCHECK(this->InstructionType() == InstructionBase::kIType); + int Value = this->Bits(kImm12Shift + kImm12Bits - 1, kImm12Shift); + return Value << 20 >> 20; + } + + inline int32_t Imm12SExtValue() const { + int32_t Value = this->Imm12Value() << 20 >> 20; + return Value; + } + + inline int BranchOffset() const { + DCHECK(this->InstructionType() == InstructionBase::kBType); + // | imm[12|10:5] | rs2 | rs1 | funct3 | imm[4:1|11] | opcode | + // 31 25 11 7 + uint32_t Bits = this->InstructionBits(); + int16_t imm13 = ((Bits & 0xf00) >> 7) | ((Bits & 0x7e000000) >> 20) | + ((Bits & 0x80) << 4) | ((Bits & 0x80000000) >> 19); + return imm13 << 19 >> 19; + } + + inline int StoreOffset() const { + DCHECK(this->InstructionType() == InstructionBase::kSType); + // | imm[11:5] | rs2 | rs1 | funct3 | imm[4:0] | opcode | + // 31 25 11 7 + uint32_t Bits = this->InstructionBits(); + int16_t imm12 = ((Bits & 0xf80) >> 7) | ((Bits & 0xfe000000) >> 20); + return imm12 << 20 >> 20; + } + + inline int Imm20UValue() const { + DCHECK(this->InstructionType() == InstructionBase::kUType); + // | imm[31:12] | rd | opcode | + // 31 12 + int32_t Bits = this->InstructionBits(); + return Bits >> 12; + } + + inline int Imm20JValue() const { + DCHECK(this->InstructionType() == InstructionBase::kJType); + // | imm[20|10:1|11|19:12] | rd | opcode | + // 31 12 + uint32_t Bits = this->InstructionBits(); + int32_t imm20 = ((Bits & 0x7fe00000) >> 20) | ((Bits & 0x100000) >> 9) | + (Bits & 0xff000) | ((Bits & 0x80000000) >> 11); + return imm20 << 11 >> 11; + } + + inline bool IsArithShift() const { + // Valid only for right shift operations + DCHECK((this->BaseOpcode() == OP || this->BaseOpcode() == OP_32 || + this->BaseOpcode() == OP_IMM || this->BaseOpcode() == OP_IMM_32) && + this->Funct3Value() == 0b101); + return this->InstructionBits() & 0x40000000; + } + + inline int Shamt() const { + // Valid only for shift instructions (SLLI, SRLI, SRAI) + DCHECK((this->InstructionBits() & kBaseOpcodeMask) == OP_IMM && + (this->Funct3Value() == 0b001 || this->Funct3Value() == 0b101)); + // | 0A0000 | shamt | rs1 | funct3 | rd | opcode | + // 31 25 20 + return this->Bits(kImm12Shift + 5, kImm12Shift); + } + + inline int Shamt32() const { + // Valid only for shift instructions (SLLIW, SRLIW, SRAIW) + DCHECK((this->InstructionBits() & kBaseOpcodeMask) == OP_IMM_32 && + (this->Funct3Value() == 0b001 || this->Funct3Value() == 0b101)); + // | 0A00000 | shamt | rs1 | funct3 | rd | opcode | + // 31 24 20 + return this->Bits(kImm12Shift + 4, kImm12Shift); + } + + inline bool AqValue() const { return this->Bits(kAqShift, kAqShift); } + + inline bool RlValue() const { return this->Bits(kRlShift, kRlShift); } + + // 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); +}; + +// ----------------------------------------------------------------------------- +// RISC-V 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 = 2 * kInstrSize; + +static const int kNegOffset = 0x00008000; + +InstructionBase::Type InstructionBase::InstructionType() const { + // RISCV routine + switch (InstructionBits() & kBaseOpcodeMask) { + case LOAD: + return kIType; + case LOAD_FP: + return kIType; + case MISC_MEM: + return kIType; + case OP_IMM: + return kIType; + case AUIPC: + return kUType; + case OP_IMM_32: + return kIType; + case STORE: + return kSType; + case STORE_FP: + return kSType; + case AMO: + return kRType; + case OP: + return kRType; + case LUI: + return kUType; + case OP_32: + return kRType; + case MADD: + case MSUB: + case NMSUB: + case NMADD: + return kR4Type; + case OP_FP: + return kRType; + case BRANCH: + return kBType; + case JALR: + return kIType; + case JAL: + return kJType; + case SYSTEM: + return kIType; + } + return kUnsupported; +} + +// ----------------------------------------------------------------------------- +// Instructions. + +template +bool InstructionGetters

::IsTrap() const { + return (this->InstructionBits() == kBreakInstr); +} + +} // namespace internal +} // namespace v8 + +#endif // V8_CODEGEN_RISCV_CONSTANTS_RISCV_H_ Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/codegen/riscv64/cpu-riscv64.cc =================================================================== --- /dev/null +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/codegen/riscv64/cpu-riscv64.cc @@ -0,0 +1,28 @@ +// 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_RISCV64 + +#include "src/codegen/cpu-features.h" + +namespace v8 { +namespace internal { + +void CpuFeatures::FlushICache(void* start, size_t size) { +#if !defined(USE_SIMULATOR) + // FIXME(RISCV): builtin_clear_cache doesn't work yet, so use `fence.i` for now + // __builtin___clear_cache(start, (char *)start + size); + asm volatile("fence.i" ::: "memory"); +#endif // !USE_SIMULATOR. +} + +} // namespace internal +} // namespace v8 + +#endif // V8_TARGET_ARCH_RISCV64 Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/codegen/riscv64/interface-descriptors-riscv64.cc =================================================================== --- /dev/null +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/codegen/riscv64/interface-descriptors-riscv64.cc @@ -0,0 +1,348 @@ +// 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_RISCV64 + +#include "src/codegen/interface-descriptors.h" +#include "src/execution/frames.h" + +namespace v8 { +namespace internal { + +const Register CallInterfaceDescriptor::ContextRegister() { return cp; } + +void CallInterfaceDescriptor::DefaultInitializePlatformSpecific( + CallInterfaceDescriptorData* data, int register_parameter_count) { + const Register default_stub_registers[] = {a0, a1, a2, a3, a4}; + CHECK_LE(static_cast(register_parameter_count), + arraysize(default_stub_registers)); + data->InitializePlatformSpecific(register_parameter_count, + default_stub_registers); +} + +void WasmI32AtomicWait32Descriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + const Register default_stub_registers[] = {a0, a1, a2, a3}; + CHECK_EQ(static_cast(kParameterCount), + arraysize(default_stub_registers)); + data->InitializePlatformSpecific(kParameterCount, default_stub_registers); +} + +void WasmI64AtomicWait32Descriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + const Register default_stub_registers[] = {a0, a1, a2, a3, a4}; + CHECK_EQ(static_cast(kParameterCount - kStackArgumentsCount), + arraysize(default_stub_registers)); + data->InitializePlatformSpecific(kParameterCount - kStackArgumentsCount, + default_stub_registers); +} + +void RecordWriteDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + const Register default_stub_registers[] = {a0, a1, a2, a3, kReturnRegister0}; + + data->RestrictAllocatableRegisters(default_stub_registers, + arraysize(default_stub_registers)); + + CHECK_LE(static_cast(kParameterCount), + arraysize(default_stub_registers)); + data->InitializePlatformSpecific(kParameterCount, default_stub_registers); +} + +void EphemeronKeyBarrierDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + const Register default_stub_registers[] = {a0, a1, a2, a3, kReturnRegister0}; + + data->RestrictAllocatableRegisters(default_stub_registers, + arraysize(default_stub_registers)); + + CHECK_LE(static_cast(kParameterCount), + arraysize(default_stub_registers)); + data->InitializePlatformSpecific(kParameterCount, default_stub_registers); +} + +const Register LoadDescriptor::ReceiverRegister() { return a1; } +const Register LoadDescriptor::NameRegister() { return a2; } +const Register LoadDescriptor::SlotRegister() { return a0; } + +const Register LoadWithVectorDescriptor::VectorRegister() { return a3; } + +const Register +LoadWithReceiverAndVectorDescriptor::LookupStartObjectRegister() { + return a4; +} + +const Register StoreDescriptor::ReceiverRegister() { return a1; } +const Register StoreDescriptor::NameRegister() { return a2; } +const Register StoreDescriptor::ValueRegister() { return a0; } +const Register StoreDescriptor::SlotRegister() { return a4; } + +const Register StoreWithVectorDescriptor::VectorRegister() { return a3; } + +const Register StoreTransitionDescriptor::SlotRegister() { return a4; } +const Register StoreTransitionDescriptor::VectorRegister() { return a3; } +const Register StoreTransitionDescriptor::MapRegister() { return a5; } + +const Register ApiGetterDescriptor::HolderRegister() { return a0; } +const Register ApiGetterDescriptor::CallbackRegister() { return a3; } + +const Register GrowArrayElementsDescriptor::ObjectRegister() { return a0; } +const Register GrowArrayElementsDescriptor::KeyRegister() { return a3; } + +// static +const Register TypeConversionDescriptor::ArgumentRegister() { return a0; } + +void TypeofDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + Register registers[] = {a3}; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + +void CallTrampolineDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + // a1: target + // a0: number of arguments + Register registers[] = {a1, a0}; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + +void CallVarargsDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + // a0 : number of arguments (on the stack, not including receiver) + // a1 : the target to call + // a4 : arguments list length (untagged) + // a2 : arguments list (FixedArray) + Register registers[] = {a1, a0, a4, a2}; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + +void CallForwardVarargsDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + // a1: the target to call + // a0: number of arguments + // a2: start index (to support rest parameters) + Register registers[] = {a1, a0, a2}; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + +void CallFunctionTemplateDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + // a1 : function template info + // a0 : number of arguments (on the stack, not including receiver) + Register registers[] = {a1, a0}; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + +void CallWithSpreadDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + // a0 : number of arguments (on the stack, not including receiver) + // a1 : the target to call + // a2 : the object to spread + Register registers[] = {a1, a0, a2}; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + +void CallWithArrayLikeDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + // a1 : the target to call + // a2 : the arguments list + Register registers[] = {a1, a2}; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + +void ConstructVarargsDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + // a0 : number of arguments (on the stack, not including receiver) + // a1 : the target to call + // a3 : the new target + // a4 : arguments list length (untagged) + // a2 : arguments list (FixedArray) + Register registers[] = {a1, a3, a0, a4, a2}; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + +void ConstructForwardVarargsDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + // a1: the target to call + // a3: new target + // a0: number of arguments + // a2: start index (to support rest parameters) + Register registers[] = {a1, a3, a0, a2}; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + +void ConstructWithSpreadDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + // a0 : number of arguments (on the stack, not including receiver) + // a1 : the target to call + // a3 : the new target + // a2 : the object to spread + Register registers[] = {a1, a3, a0, a2}; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + +void ConstructWithArrayLikeDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + // a1 : the target to call + // a3 : the new target + // a2 : the arguments list + Register registers[] = {a1, a3, a2}; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + +void ConstructStubDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + // a1: target + // a3: new target + // a0: number of arguments + // a2: allocation site or undefined + Register registers[] = {a1, a3, a0, a2}; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + +void AbortDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + Register registers[] = {a0}; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + +void CompareDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + Register registers[] = {a1, a0}; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + +void BinaryOpDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + Register registers[] = {a1, a0}; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + +void ArgumentsAdaptorDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + Register registers[] = { + a1, // JSFunction + a3, // the new target + a0, // actual number of arguments + a2, // expected number of arguments + }; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + +void ApiCallbackDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + Register registers[] = { + a1, // kApiFunctionAddress + a2, // kArgc + a3, // kCallData + a0, // kHolder + }; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + +void InterpreterDispatchDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + Register registers[] = { + kInterpreterAccumulatorRegister, kInterpreterBytecodeOffsetRegister, + kInterpreterBytecodeArrayRegister, kInterpreterDispatchTableRegister}; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + +void InterpreterPushArgsThenCallDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + Register registers[] = { + a0, // argument count (not including receiver) + a2, // address of first argument + a1 // the target callable to be call + }; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + +void InterpreterPushArgsThenConstructDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + Register registers[] = { + a0, // argument count (not including receiver) + a4, // address of the first argument + a1, // constructor to call + a3, // new target + a2, // allocation site feedback if available, undefined otherwise + }; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + +void ResumeGeneratorDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + Register registers[] = { + a0, // the value to pass to the generator + a1 // the JSGeneratorObject to resume + }; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + +void FrameDropperTrampolineDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + Register registers[] = { + a1, // loaded new FP + }; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + +void RunMicrotasksEntryDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + Register registers[] = {a0, a1}; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + +void BinaryOp_WithFeedbackDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + // TODO(v8:8888): Implement on this platform. + DefaultInitializePlatformSpecific(data, 4); +} + +void CallTrampoline_WithFeedbackDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + // TODO(v8:8888): Implement on this platform. + DefaultInitializePlatformSpecific(data, 4); +} + +void CallWithArrayLike_WithFeedbackDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + // TODO(v8:8888): Implement on this platform. + DefaultInitializePlatformSpecific(data, 4); +} + +void CallWithSpread_WithFeedbackDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + // TODO(v8:8888): Implement on this platform. + DefaultInitializePlatformSpecific(data, 4); +} + +void ConstructWithArrayLike_WithFeedbackDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + // TODO(v8:8888): Implement on this platform. + DefaultInitializePlatformSpecific(data, 4); +} + +void ConstructWithSpread_WithFeedbackDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + // TODO(v8:8888): Implement on this platform. + DefaultInitializePlatformSpecific(data, 4); +} + +void Compare_WithFeedbackDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + // TODO(v8:8888): Implement on this platform. + DefaultInitializePlatformSpecific(data, 4); +} + +void UnaryOp_WithFeedbackDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + // TODO(v8:8888): Implement on this platform. + DefaultInitializePlatformSpecific(data, 3); +} + +} // namespace internal +} // namespace v8 + +#endif // V8_TARGET_ARCH_RISCV64 Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/codegen/riscv64/macro-assembler-riscv64.cc =================================================================== --- /dev/null +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/codegen/riscv64/macro-assembler-riscv64.cc @@ -0,0 +1,4336 @@ +// 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_RISCV64 + +#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/macro-assembler.h" +#include "src/codegen/register-configuration.h" +#include "src/debug/debug.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/embedded/embedded-data.h" +#include "src/snapshot/snapshot.h" +#include "src/wasm/wasm-code-manager.h" + +// Satisfy cpplint check, but don't include platform-specific header. It is +// included recursively via macro-assembler.h. +#if 0 +#include "src/codegen/riscv64/macro-assembler-riscv64.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 = 0; + if (exclusion1 != no_reg) { + exclusions |= exclusion1.bit(); + if (exclusion2 != no_reg) { + exclusions |= exclusion2.bit(); + if (exclusion3 != no_reg) { + exclusions |= exclusion3.bit(); + } + } + } + + RegList list = kJSCallerSaved & ~exclusions; + bytes += NumRegs(list) * kPointerSize; + + if (fp_mode == kSaveFPRegs) { + bytes += NumRegs(kCallerSavedFPU) * kDoubleSize; + } + + return bytes; +} + +int TurboAssembler::PushCallerSaved(SaveFPRegsMode fp_mode, Register exclusion1, + Register exclusion2, Register exclusion3) { + int bytes = 0; + RegList exclusions = 0; + if (exclusion1 != no_reg) { + exclusions |= exclusion1.bit(); + if (exclusion2 != no_reg) { + exclusions |= exclusion2.bit(); + if (exclusion3 != no_reg) { + exclusions |= exclusion3.bit(); + } + } + } + + RegList list = kJSCallerSaved & ~exclusions; + MultiPush(list); + bytes += NumRegs(list) * kPointerSize; + + if (fp_mode == kSaveFPRegs) { + MultiPushFPU(kCallerSavedFPU); + bytes += NumRegs(kCallerSavedFPU) * kDoubleSize; + } + + return bytes; +} + +int TurboAssembler::PopCallerSaved(SaveFPRegsMode fp_mode, Register exclusion1, + Register exclusion2, Register exclusion3) { + int bytes = 0; + if (fp_mode == kSaveFPRegs) { + MultiPopFPU(kCallerSavedFPU); + bytes += NumRegs(kCallerSavedFPU) * kDoubleSize; + } + + RegList exclusions = 0; + if (exclusion1 != no_reg) { + exclusions |= exclusion1.bit(); + if (exclusion2 != no_reg) { + exclusions |= exclusion2.bit(); + if (exclusion3 != no_reg) { + exclusions |= exclusion3.bit(); + } + } + } + + RegList list = kJSCallerSaved & ~exclusions; + MultiPop(list); + bytes += NumRegs(list) * kPointerSize; + + return bytes; +} + +void TurboAssembler::LoadRoot(Register destination, RootIndex index) { + Ld(destination, MemOperand(s6, RootRegisterOffsetForRootIndex(index))); +} + +void TurboAssembler::LoadRoot(Register destination, RootIndex index, + Condition cond, Register src1, + const Operand& src2) { + Label skip; + Branch(&skip, NegateCondition(cond), src1, src2); + Ld(destination, MemOperand(s6, RootRegisterOffsetForRootIndex(index))); + bind(&skip); +} + +void TurboAssembler::PushCommonFrame(Register marker_reg) { + if (marker_reg.is_valid()) { + Push(ra, fp, marker_reg); + Add64(fp, sp, Operand(kPointerSize)); + } else { + Push(ra, fp); + mv(fp, sp); + } +} + +void TurboAssembler::PushStandardFrame(Register function_reg) { + int offset = -StandardFrameConstants::kContextOffset; + if (function_reg.is_valid()) { + Push(ra, fp, cp, function_reg); + offset += kPointerSize; + } else { + Push(ra, fp, cp); + } + Add64(fp, sp, Operand(offset)); +} + +int MacroAssembler::SafepointRegisterStackIndex(int reg_code) { + // The registers are pushed starting with the highest encoding, + // which means that lowest encodings are closest to the stack pointer. + return kSafepointRegisterStackIndexMap[reg_code]; +} + +// 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) { + DCHECK(!AreAliased(value, dst, t5, 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 == INLINE_SMI_CHECK) { + JumpIfSmi(value, &done); + } + + // Although the object register is tagged, the offset is relative to the start + // of the object, so so offset must be a multiple of kPointerSize. + DCHECK(IsAligned(offset, kPointerSize)); + + Add64(dst, object, Operand(offset - kHeapObjectTag)); + if (emit_debug_code()) { + BlockTrampolinePoolScope block_trampoline_pool(this); + Label ok; + And(t5, dst, Operand(kPointerSize - 1)); + Branch(&ok, eq, t5, Operand(zero_reg)); + ebreak(); + bind(&ok); + } + + RecordWrite(object, dst, value, ra_status, save_fp, remembered_set_action, + OMIT_SMI_CHECK); + + bind(&done); + + // Clobber clobbered input registers when running with the debug-code flag + // turned on to provoke errors. + if (emit_debug_code()) { + li(value, Operand(bit_cast(kZapValue + 4))); + li(dst, Operand(bit_cast(kZapValue + 8))); + } +} + +void TurboAssembler::SaveRegisters(RegList registers) { + DCHECK_GT(NumRegs(registers), 0); + RegList regs = 0; + for (int i = 0; i < Register::kNumRegisters; ++i) { + if ((registers >> i) & 1u) { + regs |= Register::from_code(i).bit(); + } + } + MultiPush(regs); +} + +void TurboAssembler::RestoreRegisters(RegList registers) { + DCHECK_GT(NumRegs(registers), 0); + RegList regs = 0; + for (int i = 0; i < Register::kNumRegisters; ++i) { + if ((registers >> i) & 1u) { + regs |= Register::from_code(i).bit(); + } + } + MultiPop(regs); +} + +void TurboAssembler::CallEphemeronKeyBarrier(Register object, Register address, + SaveFPRegsMode fp_mode) { + EphemeronKeyBarrierDescriptor descriptor; + RegList registers = descriptor.allocatable_registers(); + + SaveRegisters(registers); + + Register object_parameter( + descriptor.GetRegisterParameter(EphemeronKeyBarrierDescriptor::kObject)); + Register slot_parameter(descriptor.GetRegisterParameter( + EphemeronKeyBarrierDescriptor::kSlotAddress)); + Register fp_mode_parameter( + descriptor.GetRegisterParameter(EphemeronKeyBarrierDescriptor::kFPMode)); + + Push(object); + Push(address); + + Pop(slot_parameter); + Pop(object_parameter); + + Move(fp_mode_parameter, Smi::FromEnum(fp_mode)); + Call(isolate()->builtins()->builtin_handle(Builtins::kEphemeronKeyBarrier), + RelocInfo::CODE_TARGET); + RestoreRegisters(registers); +} + +void TurboAssembler::CallRecordWriteStub( + Register object, Register address, + RememberedSetAction remembered_set_action, SaveFPRegsMode fp_mode) { + CallRecordWriteStub( + object, address, remembered_set_action, fp_mode, + isolate()->builtins()->builtin_handle(Builtins::kRecordWrite), + kNullAddress); +} + +void TurboAssembler::CallRecordWriteStub( + Register object, Register address, + RememberedSetAction remembered_set_action, SaveFPRegsMode fp_mode, + Address wasm_target) { + CallRecordWriteStub(object, address, remembered_set_action, fp_mode, + Handle::null(), wasm_target); +} + +void TurboAssembler::CallRecordWriteStub( + Register object, Register address, + RememberedSetAction remembered_set_action, SaveFPRegsMode fp_mode, + Handle code_target, Address wasm_target) { + DCHECK_NE(code_target.is_null(), wasm_target == kNullAddress); + // TODO(albertnetymk): For now we ignore remembered_set_action and fp_mode, + // i.e. always emit remember set and save FP registers in RecordWriteStub. If + // large performance regression is observed, we should use these values to + // avoid unnecessary work. + + RecordWriteDescriptor descriptor; + RegList registers = descriptor.allocatable_registers(); + + SaveRegisters(registers); + Register object_parameter( + descriptor.GetRegisterParameter(RecordWriteDescriptor::kObject)); + Register slot_parameter( + descriptor.GetRegisterParameter(RecordWriteDescriptor::kSlot)); + Register remembered_set_parameter( + descriptor.GetRegisterParameter(RecordWriteDescriptor::kRememberedSet)); + Register fp_mode_parameter( + descriptor.GetRegisterParameter(RecordWriteDescriptor::kFPMode)); + + Push(object); + Push(address); + + Pop(slot_parameter); + Pop(object_parameter); + + Move(remembered_set_parameter, Smi::FromEnum(remembered_set_action)); + Move(fp_mode_parameter, Smi::FromEnum(fp_mode)); + if (code_target.is_null()) { + Call(wasm_target, RelocInfo::WASM_STUB_CALL); + } else { + Call(code_target, RelocInfo::CODE_TARGET); + } + + RestoreRegisters(registers); +} + +// 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, t5)); + DCHECK(!AreAliased(object, address, value, t6)); + + if (emit_debug_code()) { + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + Ld(scratch, MemOperand(address)); + Assert(eq, AbortReason::kWrongAddressOrValuePassedToRecordWrite, scratch, + Operand(value)); + } + + if ((remembered_set_action == OMIT_REMEMBERED_SET && + !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 == INLINE_SMI_CHECK) { + 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); + } + CallRecordWriteStub(object, 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 (emit_debug_code()) { + li(address, Operand(bit_cast(kZapValue + 12))); + li(value, Operand(bit_cast(kZapValue + 16))); + } +} + +// --------------------------------------------------------------------------- +// Instruction macros. + +void TurboAssembler::Add32(Register rd, Register rs, const Operand& rt) { + if (rt.is_reg()) { + addw(rd, rs, rt.rm()); + } else { + if (is_int12(rt.immediate()) && !MustUseReg(rt.rmode())) { + addiw(rd, rs, static_cast(rt.immediate())); + } else { + // li handles the relocation. + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + DCHECK(rs != scratch); + RV_li(scratch, rt.immediate()); + addw(rd, rs, scratch); + } + } +} + +void TurboAssembler::Add64(Register rd, Register rs, const Operand& rt) { + if (rt.is_reg()) { + add(rd, rs, rt.rm()); + } else { + if (is_int12(rt.immediate()) && !MustUseReg(rt.rmode())) { + addi(rd, rs, static_cast(rt.immediate())); + } else { + // li handles the relocation. + UseScratchRegisterScope temps(this); + // Register scratch = temps.Acquire(); + BlockTrampolinePoolScope block_trampoline_pool(this); + Register scratch = temps.hasAvailable() ? temps.Acquire() : t5; + DCHECK(rs != scratch); + RV_li(scratch, rt.immediate()); + add(rd, rs, scratch); + } + } +} + +void TurboAssembler::Sub32(Register rd, Register rs, const Operand& rt) { + if (rt.is_reg()) { + subw(rd, rs, rt.rm()); + } else { + DCHECK(is_int32(rt.immediate())); + if (is_int12(-rt.immediate()) && !MustUseReg(rt.rmode())) { + addiw(rd, rs, + static_cast( + -rt.immediate())); // No subiw instr, use addiw(x, y, -imm). + } else { + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + DCHECK(rs != scratch); + if (-rt.immediate() >> 12 == 0 && !MustUseReg(rt.rmode())) { + // Use load -imm and addu when loading -imm generates one instruction. + RV_li(scratch, -rt.immediate()); + addw(rd, rs, scratch); + } else { + // li handles the relocation. + RV_li(scratch, rt.immediate()); + subw(rd, rs, scratch); + } + } + } +} + +void TurboAssembler::Sub64(Register rd, Register rs, const Operand& rt) { + if (rt.is_reg()) { + sub(rd, rs, rt.rm()); + } else if (is_int12(-rt.immediate()) && !MustUseReg(rt.rmode())) { + addi(rd, rs, + static_cast( + -rt.immediate())); // No subi instr, use addi(x, y, -imm). + } else { + DCHECK(rs != t3); + int li_count = InstrCountForLi64Bit(rt.immediate()); + int li_neg_count = InstrCountForLi64Bit(-rt.immediate()); + if (li_neg_count < li_count && !MustUseReg(rt.rmode())) { + // Use load -imm and add when loading -imm generates one instruction. + DCHECK(rt.immediate() != std::numeric_limits::min()); + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + RV_li(scratch, -rt.immediate()); + add(rd, rs, scratch); + } else { + // li handles the relocation. + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + RV_li(scratch, rt.immediate()); + sub(rd, rs, scratch); + } + } +} + +void TurboAssembler::Mul32(Register rd, Register rs, const Operand& rt) { + if (rt.is_reg()) { + mulw(rd, rs, rt.rm()); + } else { + // li handles the relocation. + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + DCHECK(rs != scratch); + RV_li(scratch, rt.immediate()); + mulw(rd, rs, scratch); + } +} + +void TurboAssembler::Mulh32(Register rd, Register rs, const Operand& rt) { + if (rt.is_reg()) { + mul(rd, rs, rt.rm()); + } else { + // li handles the relocation. + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + DCHECK(rs != scratch); + RV_li(scratch, rt.immediate()); + mul(rd, rs, scratch); + } + srai(rd, rd, 32); +} + +void TurboAssembler::Mulhu32(Register rd, Register rs, const Operand& rt, + Register rsz, Register rtz) { + slli(rsz, rs, 32); + if (rt.is_reg()) + slli(rtz, rt.rm(), 32); + else + RV_li(rtz, rt.immediate() << 32); + mulhu(rd, rsz, rtz); + srai(rd, rd, 32); +} + +void TurboAssembler::Mul64(Register rd, Register rs, const Operand& rt) { + if (rt.is_reg()) { + mul(rd, rs, rt.rm()); + } else { + // li handles the relocation. + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + DCHECK(rs != scratch); + RV_li(scratch, rt.immediate()); + mul(rd, rs, scratch); + } +} + +void TurboAssembler::Mulh64(Register rd, Register rs, const Operand& rt) { + if (rt.is_reg()) { + mulh(rd, rs, rt.rm()); + } else { + // li handles the relocation. + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + DCHECK(rs != scratch); + RV_li(scratch, rt.immediate()); + mulh(rd, rs, scratch); + } +} + +void TurboAssembler::Div32(Register res, Register rs, const Operand& rt) { + if (rt.is_reg()) { + divw(res, rs, rt.rm()); + } else { + // li handles the relocation. + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + DCHECK(rs != scratch); + RV_li(scratch, rt.immediate()); + divw(res, rs, scratch); + } +} + +void TurboAssembler::Mod32(Register rd, Register rs, const Operand& rt) { + if (rt.is_reg()) { + remw(rd, rs, rt.rm()); + } else { + // li handles the relocation. + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + DCHECK(rs != scratch); + RV_li(scratch, rt.immediate()); + remw(rd, rs, scratch); + } +} + +void TurboAssembler::Modu32(Register rd, Register rs, const Operand& rt) { + if (rt.is_reg()) { + remuw(rd, rs, rt.rm()); + } else { + // li handles the relocation. + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + DCHECK(rs != scratch); + RV_li(scratch, rt.immediate()); + remuw(rd, rs, scratch); + } +} + +void TurboAssembler::Div64(Register rd, Register rs, const Operand& rt) { + if (rt.is_reg()) { + div(rd, rs, rt.rm()); + } else { + // li handles the relocation. + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + DCHECK(rs != scratch); + RV_li(scratch, rt.immediate()); + div(rd, rs, scratch); + } +} + +void TurboAssembler::Divu32(Register res, Register rs, const Operand& rt) { + if (rt.is_reg()) { + divuw(res, rs, rt.rm()); + } else { + // li handles the relocation. + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + DCHECK(rs != scratch); + RV_li(scratch, rt.immediate()); + divuw(res, rs, scratch); + } +} + +void TurboAssembler::Divu64(Register res, Register rs, const Operand& rt) { + if (rt.is_reg()) { + divu(res, rs, rt.rm()); + } else { + // li handles the relocation. + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + DCHECK(rs != scratch); + RV_li(scratch, rt.immediate()); + divu(res, rs, scratch); + } +} + +void TurboAssembler::Mod64(Register rd, Register rs, const Operand& rt) { + if (rt.is_reg()) { + rem(rd, rs, rt.rm()); + } else { + // li handles the relocation. + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + DCHECK(rs != scratch); + RV_li(scratch, rt.immediate()); + rem(rd, rs, scratch); + } +} + +void TurboAssembler::Modu64(Register rd, Register rs, const Operand& rt) { + if (rt.is_reg()) { + remu(rd, rs, rt.rm()); + } else { + // li handles the relocation. + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + DCHECK(rs != scratch); + RV_li(scratch, rt.immediate()); + remu(rd, rs, scratch); + } +} + +void TurboAssembler::And(Register rd, Register rs, const Operand& rt) { + if (rt.is_reg()) { + and_(rd, rs, rt.rm()); + } else { + if (is_int12(rt.immediate()) && !MustUseReg(rt.rmode())) { + andi(rd, rs, static_cast(rt.immediate())); + } else { + // li handles the relocation. + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + DCHECK(rs != scratch); + RV_li(scratch, rt.immediate()); + and_(rd, rs, scratch); + } + } +} + +void TurboAssembler::Or(Register rd, Register rs, const Operand& rt) { + if (rt.is_reg()) { + or_(rd, rs, rt.rm()); + } else { + if (is_int12(rt.immediate()) && !MustUseReg(rt.rmode())) { + ori(rd, rs, static_cast(rt.immediate())); + } else { + // li handles the relocation. + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + DCHECK(rs != scratch); + RV_li(scratch, rt.immediate()); + or_(rd, rs, scratch); + } + } +} + +void TurboAssembler::Xor(Register rd, Register rs, const Operand& rt) { + if (rt.is_reg()) { + xor_(rd, rs, rt.rm()); + } else { + if (is_int12(rt.immediate()) && !MustUseReg(rt.rmode())) { + xori(rd, rs, static_cast(rt.immediate())); + } else { + // li handles the relocation. + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + DCHECK(rs != scratch); + RV_li(scratch, rt.immediate()); + xor_(rd, rs, scratch); + } + } +} + +void TurboAssembler::Nor(Register rd, Register rs, const Operand& rt) { + if (rt.is_reg()) { + or_(rd, rs, rt.rm()); + not_(rd, rd); + } else { + Or(rd, rs, rt); + not_(rd, rd); + } +} + +void TurboAssembler::Neg(Register rs, const Operand& rt) { + DCHECK(rt.is_reg()); + neg(rs, rt.rm()); +} + +void TurboAssembler::Seqz(Register rd, const Operand& rt) { + if (rt.is_reg()) { + seqz(rd, rt.rm()); + } else { + li(rd, rt.immediate() == 0); + } +} + +void TurboAssembler::Snez(Register rd, const Operand& rt) { + if (rt.is_reg()) { + snez(rd, rt.rm()); + } else { + li(rd, rt.immediate() != 0); + } +} + +void TurboAssembler::Seq(Register rd, Register rs, const Operand& rt) { + if (rs == zero_reg) { + Seqz(rd, rt); + } else if (IsZero(rt)) { + seqz(rd, rs); + } else { + Sub64(rd, rs, rt); + seqz(rd, rd); + } +} + +void TurboAssembler::Sne(Register rd, Register rs, const Operand& rt) { + if (rs == zero_reg) { + Snez(rd, rt); + } else if (IsZero(rt)) { + snez(rd, rs); + } else { + Sub64(rd, rs, rt); + snez(rd, rd); + } +} + +void TurboAssembler::Slt(Register rd, Register rs, const Operand& rt) { + if (rt.is_reg()) { + slt(rd, rs, rt.rm()); + } else { + if (is_int12(rt.immediate()) && !MustUseReg(rt.rmode())) { + slti(rd, rs, static_cast(rt.immediate())); + } else { + // li handles the relocation. + UseScratchRegisterScope temps(this); + BlockTrampolinePoolScope block_trampoline_pool(this); + Register scratch = temps.hasAvailable() ? temps.Acquire() : t5; + DCHECK(rs != scratch); + RV_li(scratch, rt.immediate()); + slt(rd, rs, scratch); + } + } +} + +void TurboAssembler::Sltu(Register rd, Register rs, const Operand& rt) { + if (rt.is_reg()) { + sltu(rd, rs, rt.rm()); + } else { + if (is_int12(rt.immediate()) && !MustUseReg(rt.rmode())) { + sltiu(rd, rs, static_cast(rt.immediate())); + } else { + // li handles the relocation. + UseScratchRegisterScope temps(this); + BlockTrampolinePoolScope block_trampoline_pool(this); + Register scratch = temps.hasAvailable() ? temps.Acquire() : t5; + DCHECK(rs != scratch); + RV_li(scratch, rt.immediate()); + sltu(rd, rs, scratch); + } + } +} + +void TurboAssembler::Sle(Register rd, Register rs, const Operand& rt) { + if (rt.is_reg()) { + slt(rd, rt.rm(), rs); + } else { + // li handles the relocation. + UseScratchRegisterScope temps(this); + Register scratch = temps.hasAvailable() ? temps.Acquire() : t5; + BlockTrampolinePoolScope block_trampoline_pool(this); + DCHECK(rs != scratch); + RV_li(scratch, rt.immediate()); + slt(rd, scratch, rs); + } + xori(rd, rd, 1); +} + +void TurboAssembler::Sleu(Register rd, Register rs, const Operand& rt) { + if (rt.is_reg()) { + sltu(rd, rt.rm(), rs); + } else { + // li handles the relocation. + UseScratchRegisterScope temps(this); + Register scratch = temps.hasAvailable() ? temps.Acquire() : t5; + BlockTrampolinePoolScope block_trampoline_pool(this); + DCHECK(rs != scratch); + RV_li(scratch, rt.immediate()); + sltu(rd, scratch, rs); + } + xori(rd, rd, 1); +} + +void TurboAssembler::Sge(Register rd, Register rs, const Operand& rt) { + Slt(rd, rs, rt); + xori(rd, rd, 1); +} + +void TurboAssembler::Sgeu(Register rd, Register rs, const Operand& rt) { + Sltu(rd, rs, rt); + xori(rd, rd, 1); +} + +void TurboAssembler::Sgt(Register rd, Register rs, const Operand& rt) { + if (rt.is_reg()) { + slt(rd, rt.rm(), rs); + } else { + // li handles the relocation. + UseScratchRegisterScope temps(this); + Register scratch = temps.hasAvailable() ? temps.Acquire() : t5; + BlockTrampolinePoolScope block_trampoline_pool(this); + DCHECK(rs != scratch); + RV_li(scratch, rt.immediate()); + slt(rd, scratch, rs); + } +} + +void TurboAssembler::Sgtu(Register rd, Register rs, const Operand& rt) { + if (rt.is_reg()) { + sltu(rd, rt.rm(), rs); + } else { + // li handles the relocation. + UseScratchRegisterScope temps(this); + Register scratch = temps.hasAvailable() ? temps.Acquire() : t5; + BlockTrampolinePoolScope block_trampoline_pool(this); + DCHECK(rs != scratch); + RV_li(scratch, rt.immediate()); + sltu(rd, scratch, rs); + } +} + +void TurboAssembler::Sll32(Register rd, Register rs, const Operand& rt) { + if (rt.is_reg()) + sllw(rd, rs, rt.rm()); + else { + uint8_t shamt = static_cast(rt.immediate()); + slliw(rd, rs, shamt); + } +} + +void TurboAssembler::Sra32(Register rd, Register rs, const Operand& rt) { + if (rt.is_reg()) + sraw(rd, rs, rt.rm()); + else { + uint8_t shamt = static_cast(rt.immediate()); + sraiw(rd, rs, shamt); + } +} + +void TurboAssembler::Srl32(Register rd, Register rs, const Operand& rt) { + if (rt.is_reg()) + srlw(rd, rs, rt.rm()); + else { + uint8_t shamt = static_cast(rt.immediate()); + srliw(rd, rs, shamt); + } +} + +void TurboAssembler::Sra64(Register rd, Register rs, const Operand& rt) { + if (rt.is_reg()) + sra(rd, rs, rt.rm()); + else { + uint8_t shamt = static_cast(rt.immediate()); + srai(rd, rs, shamt); + } +} + +void TurboAssembler::Srl64(Register rd, Register rs, const Operand& rt) { + if (rt.is_reg()) + srl(rd, rs, rt.rm()); + else { + uint8_t shamt = static_cast(rt.immediate()); + srli(rd, rs, shamt); + } +} + +void TurboAssembler::Sll64(Register rd, Register rs, const Operand& rt) { + if (rt.is_reg()) + sll(rd, rs, rt.rm()); + else { + uint8_t shamt = static_cast(rt.immediate()); + slli(rd, rs, shamt); + } +} + +void TurboAssembler::Ror(Register rd, Register rs, const Operand& rt) { + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + DCHECK(rs != scratch); + if (rt.is_reg()) { + negw(scratch, rt.rm()); + sllw(scratch, rs, scratch); + srlw(rd, rs, rt.rm()); + or_(rd, scratch, rd); + sext_w(rd, rd); + } else { + int64_t ror_value = rt.immediate() % 32; + if (ror_value == 0) { + mv(rd, rs); + return; + } else if (ror_value < 0) { + ror_value += 32; + } + srliw(scratch, rs, ror_value); + slliw(rd, rs, 32 - ror_value); + or_(rd, scratch, rd); + sext_w(rd, rd); + } +} + +void TurboAssembler::Dror(Register rd, Register rs, const Operand& rt) { + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + DCHECK(rs != scratch); + if (rt.is_reg()) { + negw(scratch, rt.rm()); + sll(scratch, rs, scratch); + srl(rd, rs, rt.rm()); + or_(rd, scratch, rd); + } else { + int64_t dror_value = rt.immediate() % 64; + if (dror_value == 0) { + mv(rd, rs); + return; + } else if (dror_value < 0) { + dror_value += 64; + } + srli(scratch, rs, dror_value); + slli(rd, rs, 64 - dror_value); + or_(rd, scratch, rd); + } +} + +void TurboAssembler::CalcScaledAddress(Register rd, Register rt, Register rs, + uint8_t sa, Register scratch) { + DCHECK(sa >= 1 && sa <= 31); + Register tmp = rd == rt ? scratch : rd; + DCHECK(tmp != rt); + slli(tmp, rs, sa); + Add64(rd, rt, tmp); +} + +// ------------Pseudo-instructions------------- +// Change endianness +void TurboAssembler::ByteSwap(Register rd, Register rs, int operand_size) { + DCHECK(operand_size == 4 || operand_size == 8); + DCHECK(rd != t5 && rd != t6); + if (operand_size == 4) { + // Uint32_t t5 = 0x00FF00FF; + // x = (x << 16 | x >> 16); + // x = (((x & t5) << 8) | ((x & (t5 << 8)) >> 8)); + UseScratchRegisterScope temps(this); + BlockTrampolinePoolScope block_trampoline_pool(this); + Register x = temps.Acquire(); + li(t5, 0x00FF00FF); + slliw(x, rs, 16); + srliw(rd, rs, 16); + or_(x, rd, x); // x <- x << 16 | x >> 16 + and_(t6, x, t5); // t <- x & 0x00FF00FF + slliw(t6, t6, 8); // t <- (x & t5) << 8 + slliw(t5, t5, 8); // t5 <- 0xFF00FF00 + and_(rd, x, t5); // x & 0xFF00FF00 + srliw(rd, rd, 8); + or_(rd, rd, t6); // (((x & t5) << 8) | ((x & (t5 << 8)) >> 8)) + } else { + // uint64_t t5 = 0x0000FFFF0000FFFFl; + // uint64_t t5 = 0x00FF00FF00FF00FFl; + // x = (x << 32 | x >> 32); + // x = (x & t5) << 16 | (x & (t5 << 16)) >> 16; + // x = (x & t5) << 8 | (x & (t5 << 8)) >> 8; + UseScratchRegisterScope temps(this); + BlockTrampolinePoolScope block_trampoline_pool(this); + Register x = temps.Acquire(); + li(t5, 0x0000FFFF0000FFFFl); + slli(x, rs, 32); + srli(rd, rs, 32); + or_(x, rd, x); // x <- x << 32 | x >> 32 + and_(t6, x, t5); // t <- x & 0x0000FFFF0000FFFF + slli(t6, t6, 16); // t <- (x & 0x0000FFFF0000FFFF) << 16 + slli(t5, t5, 16); // t5 <- 0xFFFF0000FFFF0000 + and_(rd, x, t5); // rd <- x & 0xFFFF0000FFFF0000 + srli(rd, rd, 16); // rd <- x & (t5 << 16)) >> 16 + or_(x, rd, t6); // (x & t5) << 16 | (x & (t5 << 16)) >> 16; + li(t5, 0x00FF00FF00FF00FFl); + and_(t6, x, t5); // t <- x & 0x00FF00FF00FF00FF + slli(t6, t6, 8); // t <- (x & t5) << 8 + slli(t5, t5, 8); // t5 <- 0xFF00FF00FF00FF00 + and_(rd, x, t5); + srli(rd, rd, 8); // rd <- (x & (t5 << 8)) >> 8 + or_(rd, rd, t6); // (((x & t5) << 8) | ((x & (t5 << 8)) >> 8)) + } +} + +template +void TurboAssembler::LoadNBytes(Register rd, const MemOperand& rs, + Register scratch) { + DCHECK(rd != rs.rm() && rd != scratch); + DCHECK(NBYTES <= 8); + + // load the most significant byte + if (LOAD_SIGNED) { + lb(rd, rs.rm(), rs.offset() + (NBYTES - 1)); + } else { + lbu(rd, rs.rm(), rs.offset() + (NBYTES - 1)); + } + + // load remaining (nbytes-1) bytes from higher to lower + slli(rd, rd, 8 * (NBYTES - 1)); + for (int i = (NBYTES - 2); i >= 0; i--) { + lbu(scratch, rs.rm(), rs.offset() + i); + if (i) slli(scratch, scratch, i * 8); + or_(rd, rd, scratch); + } +} + +template +void TurboAssembler::LoadNBytesOverwritingBaseReg(const MemOperand& rs, + Register scratch0, + Register scratch1) { + // This function loads nbytes from memory specified by rs and into rs.rm() + DCHECK(rs.rm() != scratch0 && rs.rm() != scratch1 && scratch0 != scratch1); + DCHECK(NBYTES <= 8); + + // load the most significant byte + if (LOAD_SIGNED) { + lb(scratch0, rs.rm(), rs.offset() + (NBYTES - 1)); + } else { + lbu(scratch0, rs.rm(), rs.offset() + (NBYTES - 1)); + } + + // load remaining (nbytes-1) bytes from higher to lower + slli(scratch0, scratch0, 8 * (NBYTES - 1)); + for (int i = (NBYTES - 2); i >= 0; i--) { + lbu(scratch1, rs.rm(), rs.offset() + i); + if (i) { + slli(scratch1, scratch1, i * 8); + or_(scratch0, scratch0, scratch1); + } else { + // write to rs.rm() when processing the last byte + or_(rs.rm(), scratch0, scratch1); + } + } +} + +template +void TurboAssembler::UnalignedLoadHelper(Register rd, const MemOperand& rs) { + BlockTrampolinePoolScope block_trampoline_pool(this); + UseScratchRegisterScope temps(this); + + if (NeedAdjustBaseAndOffset(rs, OffsetAccessType::TWO_ACCESSES, NBYTES - 1)) { + // Adjust offset for two accesses and check if offset + 3 fits into int12. + MemOperand source = rs; + Register scratch_base = temps.Acquire(); + DCHECK(scratch_base != rs.rm()); + AdjustBaseAndOffset(&source, scratch_base, OffsetAccessType::TWO_ACCESSES, + NBYTES - 1); + + // Since source.rm() is scratch_base, assume rd != source.rm() + DCHECK(rd != source.rm()); + Register scratch_other = t5; + LoadNBytes(rd, source, scratch_other); + } else { + // no need to adjust base-and-offset + if (rd != rs.rm()) { + Register scratch = temps.Acquire(); + LoadNBytes(rd, rs, scratch); + } else { // rd == rs.rm() + Register scratch0 = temps.Acquire(); + Register scratch1 = t5; + LoadNBytesOverwritingBaseReg(rs, scratch0, scratch1); + } + } +} + +template +void TurboAssembler::UnalignedFLoadHelper(FPURegister frd, const MemOperand& rs, + Register scratch) { + DCHECK(scratch != rs.rm()); + DCHECK(NBYTES == 4 || NBYTES == 8); + + BlockTrampolinePoolScope block_trampoline_pool(this); + UseScratchRegisterScope temps(this); + MemOperand source = rs; + if (NeedAdjustBaseAndOffset(rs, OffsetAccessType::TWO_ACCESSES, NBYTES - 1)) { + // Adjust offset for two accesses and check if offset + 3 fits into int12. + Register scratch_base = temps.Acquire(); + DCHECK(scratch_base != scratch && scratch_base != rs.rm()); + AdjustBaseAndOffset(&source, scratch_base, OffsetAccessType::TWO_ACCESSES, + NBYTES - 1); + } + + Register scratch_other = temps.hasAvailable() ? temps.Acquire() : t5; + DCHECK(scratch_other != scratch && scratch_other != rs.rm()); + LoadNBytes(scratch, source, scratch_other); + if (NBYTES == 4) + fmv_w_x(frd, scratch); + else + fmv_d_x(frd, scratch); +} + +template +void TurboAssembler::UnalignedStoreHelper(Register rd, const MemOperand& rs, + Register scratch_other) { + DCHECK(scratch_other != rs.rm()); + DCHECK(NBYTES <= 8); + + UseScratchRegisterScope temps(this); + MemOperand source = rs; + // Adjust offset for two accesses and check if offset + 3 fits into int12. + if (NeedAdjustBaseAndOffset(rs, OffsetAccessType::TWO_ACCESSES, NBYTES - 1)) { + Register scratch_base = temps.Acquire(); + DCHECK(scratch_base != rd && scratch_base != rs.rm()); + AdjustBaseAndOffset(&source, scratch_base, OffsetAccessType::TWO_ACCESSES, + NBYTES - 1); + } + + BlockTrampolinePoolScope block_trampoline_pool(this); + if (scratch_other == no_reg) + scratch_other = temps.hasAvailable() ? temps.Acquire() : t5; + + DCHECK(scratch_other != rd && scratch_other != rs.rm() && + scratch_other != source.rm()); + + sb(rd, source.rm(), source.offset()); + for (size_t i = 1; i <= (NBYTES - 1); i++) { + srli(scratch_other, rd, i * 8); + sb(scratch_other, source.rm(), source.offset() + i); + } +} + +template +void TurboAssembler::UnalignedFStoreHelper(FPURegister frd, + const MemOperand& rs, + Register scratch) { + DCHECK(scratch != rs.rm()); + DCHECK(NBYTES == 8 || NBYTES == 4); + + if (NBYTES == 4) { + fmv_x_w(scratch, frd); + } else { + fmv_x_d(scratch, frd); + } + UnalignedStoreHelper(scratch, rs); +} + +template +void TurboAssembler::AlignedLoadHelper(Reg_T target, const MemOperand& rs, + Func generator) { + MemOperand source = rs; + UseScratchRegisterScope temps(this); + BlockTrampolinePoolScope block_trampoline_pool(this); + if (NeedAdjustBaseAndOffset(source)) { + Register scratch = temps.hasAvailable() ? temps.Acquire() : t5; + DCHECK(scratch != rs.rm()); + AdjustBaseAndOffset(&source, scratch); + } + generator(target, source); +} + +template +void TurboAssembler::AlignedStoreHelper(Reg_T value, const MemOperand& rs, + Func generator) { + MemOperand source = rs; + UseScratchRegisterScope temps(this); + BlockTrampolinePoolScope block_trampoline_pool(this); + if (NeedAdjustBaseAndOffset(source)) { + Register scratch = temps.hasAvailable() ? temps.Acquire() : t5; + // make sure scratch does not overwrite value + if (std::is_same::value) + DCHECK(scratch.code() != value.code()); + DCHECK(scratch != rs.rm()); + AdjustBaseAndOffset(&source, scratch); + } + generator(value, source); +} + +void TurboAssembler::Ulw(Register rd, const MemOperand& rs) { + UnalignedLoadHelper<4, true>(rd, rs); +} + +void TurboAssembler::Ulwu(Register rd, const MemOperand& rs) { + UnalignedLoadHelper<4, false>(rd, rs); +} + +void TurboAssembler::Usw(Register rd, const MemOperand& rs) { + UnalignedStoreHelper<4>(rd, rs); +} + +void TurboAssembler::Ulh(Register rd, const MemOperand& rs) { + UnalignedLoadHelper<2, true>(rd, rs); +} + +void TurboAssembler::Ulhu(Register rd, const MemOperand& rs) { + UnalignedLoadHelper<2, false>(rd, rs); +} + +void TurboAssembler::Ush(Register rd, const MemOperand& rs) { + UnalignedStoreHelper<2>(rd, rs); +} + +void TurboAssembler::Uld(Register rd, const MemOperand& rs) { + UnalignedLoadHelper<8, true>(rd, rs); +} + +// Load consequent 32-bit word pair in 64-bit reg. and put first word in low +// bits, +// second word in high bits. +void MacroAssembler::LoadWordPair(Register rd, const MemOperand& rs, + Register scratch) { + Lwu(rd, rs); + Lw(scratch, MemOperand(rs.rm(), rs.offset() + kPointerSize / 2)); + slli(scratch, scratch, 32); + Add64(rd, rd, scratch); +} + +void TurboAssembler::Usd(Register rd, const MemOperand& rs) { + UnalignedStoreHelper<8>(rd, rs); +} + +// Do 64-bit store as two consequent 32-bit stores to unaligned address. +void MacroAssembler::StoreWordPair(Register rd, const MemOperand& rs, + Register scratch) { + Sw(rd, rs); + srai(scratch, rd, 32); + Sw(scratch, MemOperand(rs.rm(), rs.offset() + kPointerSize / 2)); +} + +void TurboAssembler::ULoadFloat(FPURegister fd, const MemOperand& rs, + Register scratch) { + UnalignedFLoadHelper<4>(fd, rs, scratch); +} + +void TurboAssembler::UStoreFloat(FPURegister fd, const MemOperand& rs, + Register scratch) { + UnalignedFStoreHelper<4>(fd, rs, scratch); +} + +void TurboAssembler::ULoadDouble(FPURegister fd, const MemOperand& rs, + Register scratch) { + UnalignedFLoadHelper<8>(fd, rs, scratch); +} + +void TurboAssembler::UStoreDouble(FPURegister fd, const MemOperand& rs, + Register scratch) { + UnalignedFStoreHelper<8>(fd, rs, scratch); +} + +void TurboAssembler::Lb(Register rd, const MemOperand& rs) { + auto fn = [this](Register target, const MemOperand& source) { + this->lb(target, source.rm(), source.offset()); + }; + AlignedLoadHelper(rd, rs, fn); +} + +void TurboAssembler::Lbu(Register rd, const MemOperand& rs) { + auto fn = [this](Register target, const MemOperand& source) { + this->lbu(target, source.rm(), source.offset()); + }; + AlignedLoadHelper(rd, rs, fn); +} + +void TurboAssembler::Sb(Register rd, const MemOperand& rs) { + auto fn = [this](Register value, const MemOperand& source) { + this->sb(value, source.rm(), source.offset()); + }; + AlignedStoreHelper(rd, rs, fn); +} + +void TurboAssembler::Lh(Register rd, const MemOperand& rs) { + auto fn = [this](Register target, const MemOperand& source) { + this->lh(target, source.rm(), source.offset()); + }; + AlignedLoadHelper(rd, rs, fn); +} + +void TurboAssembler::Lhu(Register rd, const MemOperand& rs) { + auto fn = [this](Register target, const MemOperand& source) { + this->lhu(target, source.rm(), source.offset()); + }; + AlignedLoadHelper(rd, rs, fn); +} + +void TurboAssembler::Sh(Register rd, const MemOperand& rs) { + auto fn = [this](Register value, const MemOperand& source) { + this->sh(value, source.rm(), source.offset()); + }; + AlignedStoreHelper(rd, rs, fn); +} + +void TurboAssembler::Lw(Register rd, const MemOperand& rs) { + auto fn = [this](Register target, const MemOperand& source) { + this->lw(target, source.rm(), source.offset()); + }; + AlignedLoadHelper(rd, rs, fn); +} + +void TurboAssembler::Lwu(Register rd, const MemOperand& rs) { + auto fn = [this](Register target, const MemOperand& source) { + this->lwu(target, source.rm(), source.offset()); + }; + AlignedLoadHelper(rd, rs, fn); +} + +void TurboAssembler::Sw(Register rd, const MemOperand& rs) { + auto fn = [this](Register value, const MemOperand& source) { + this->sw(value, source.rm(), source.offset()); + }; + AlignedStoreHelper(rd, rs, fn); +} + +void TurboAssembler::Ld(Register rd, const MemOperand& rs) { + auto fn = [this](Register target, const MemOperand& source) { + this->ld(target, source.rm(), source.offset()); + }; + AlignedLoadHelper(rd, rs, fn); +} + +void TurboAssembler::Sd(Register rd, const MemOperand& rs) { + auto fn = [this](Register value, const MemOperand& source) { + this->sd(value, source.rm(), source.offset()); + }; + AlignedStoreHelper(rd, rs, fn); +} + +void TurboAssembler::LoadFloat(FPURegister fd, const MemOperand& src) { + auto fn = [this](FPURegister target, const MemOperand& source) { + this->flw(target, source.rm(), source.offset()); + }; + AlignedLoadHelper(fd, src, fn); +} + +void TurboAssembler::StoreFloat(FPURegister fs, const MemOperand& src) { + auto fn = [this](FPURegister value, const MemOperand& source) { + this->fsw(value, source.rm(), source.offset()); + }; + AlignedStoreHelper(fs, src, fn); +} + +void TurboAssembler::LoadDouble(FPURegister fd, const MemOperand& src) { + auto fn = [this](FPURegister target, const MemOperand& source) { + this->fld(target, source.rm(), source.offset()); + }; + AlignedLoadHelper(fd, src, fn); +} + +void TurboAssembler::StoreDouble(FPURegister fs, const MemOperand& src) { + auto fn = [this](FPURegister value, const MemOperand& source) { + this->fsd(value, source.rm(), source.offset()); + }; + AlignedStoreHelper(fs, src, fn); +} + +void TurboAssembler::Ll(Register rd, const MemOperand& rs) { + bool is_one_instruction = rs.offset() == 0; + if (is_one_instruction) { + lr_w(false, false, rd, rs.rm()); + } else { + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + Add64(scratch, rs.rm(), rs.offset()); + lr_w(false, false, rd, scratch); + } +} + +void TurboAssembler::Lld(Register rd, const MemOperand& rs) { + bool is_one_instruction = rs.offset() == 0; + if (is_one_instruction) { + lr_d(false, false, rd, rs.rm()); + } else { + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + Add64(scratch, rs.rm(), rs.offset()); + lr_d(false, false, rd, scratch); + } +} + +void TurboAssembler::Sc(Register rd, const MemOperand& rs) { + bool is_one_instruction = rs.offset() == 0; + if (is_one_instruction) { + sc_w(false, false, rd, rs.rm(), rd); + } else { + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + Add64(scratch, rs.rm(), rs.offset()); + sc_w(false, false, rd, scratch, rd); + } +} + +void TurboAssembler::Scd(Register rd, const MemOperand& rs) { + bool is_one_instruction = rs.offset() == 0; + if (is_one_instruction) { + sc_d(false, false, rd, rs.rm(), rd); + } else { + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + Add64(scratch, rs.rm(), rs.offset()); + sc_d(false, false, rd, scratch, rd); + } +} + +void TurboAssembler::li(Register dst, Handle value, LiFlags mode) { + // TODO(jgruber,v8:8887): Also consider a root-relative load when generating + // non-isolate-independent code. In many cases it might be cheaper than + // embedding the relocatable value. + 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) { + // TODO(jgruber,v8:8887): Also consider a root-relative load when generating + // non-isolate-independent code. In many cases it might be cheaper than + // embedding the relocatable value. + 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) { + int64_t Hi20 = ((value + 0x800) >> 12); + int64_t Lo12 = value << 52 >> 52; + if (Hi20 == 0 || Lo12 == 0) { + return 1; + } + return 2; +} + +int TurboAssembler::InstrCountForLi64Bit(int64_t value) { + if (is_int32(value)) { + return InstrCountForLiLower32Bit(value); + } else { + return li_count(value); + } + UNREACHABLE(); + return INT_MAX; +} + +void TurboAssembler::li_optimized(Register rd, Operand j, LiFlags mode) { + DCHECK(!j.is_reg()); + DCHECK(!MustUseReg(j.rmode())); + DCHECK(mode == OPTIMIZE_SIZE); + RV_li(rd, j.immediate()); +} + +void TurboAssembler::li(Register rd, Operand j, LiFlags mode) { + DCHECK(!j.is_reg()); + BlockTrampolinePoolScope block_trampoline_pool(this); + if (!MustUseReg(j.rmode()) && mode == OPTIMIZE_SIZE) { + RV_li(rd, j.immediate()); + } 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); + // FIXME(RISC_V): Does this case need to be constant size? + li_constant(rd, immediate); + } 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 8 instructions. + RecordRelocInfo(RelocInfo::INTERNAL_REFERENCE_ENCODED); + li_constant(rd, j.immediate()); + } else { // mode == CONSTANT_SIZE - always emit the same instruction + // sequence. + li_constant(rd, j.immediate()); + } +} + +static RegList t_regs = Register::ListOf(t0, t1, t2, t3, t4, t5, t6); +static RegList a_regs = Register::ListOf(a0, a1, a2, a3, a4, a5, a6, a7); +static RegList s_regs = + Register::ListOf(s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11); + +void TurboAssembler::MultiPush(RegList regs) { + int16_t num_to_push = base::bits::CountPopulation(regs); + int16_t stack_offset = num_to_push * kPointerSize; + +#define TEST_AND_PUSH_REG(reg) \ + if ((regs & reg.bit()) != 0) { \ + stack_offset -= kPointerSize; \ + Sd(reg, MemOperand(sp, stack_offset)); \ + regs &= ~reg.bit(); \ + } + +#define T_REGS(V) V(t6) V(t5) V(t4) V(t3) V(t2) V(t1) V(t0) +#define A_REGS(V) V(a7) V(a6) V(a5) V(a4) V(a3) V(a2) V(a1) V(a0) +#define S_REGS(V) \ + V(s11) V(s10) V(s9) V(s8) V(s7) V(s6) V(s5) V(s4) V(s3) V(s2) V(s1) + + Sub64(sp, sp, Operand(stack_offset)); + + // Certain usage of MultiPush requires that registers are pushed onto the + // stack in a particular: ra, fp, sp, gp, .... (basically in the decreasing + // order of register numbers according to MIPS register numbers) + TEST_AND_PUSH_REG(ra); + TEST_AND_PUSH_REG(fp); + TEST_AND_PUSH_REG(sp); + TEST_AND_PUSH_REG(gp); + TEST_AND_PUSH_REG(tp); + if ((regs & s_regs) != 0) { + S_REGS(TEST_AND_PUSH_REG) + } + if ((regs & a_regs) != 0) { + A_REGS(TEST_AND_PUSH_REG) + } + if ((regs & t_regs) != 0) { + T_REGS(TEST_AND_PUSH_REG) + } + + DCHECK(regs == 0); + +#undef TEST_AND_PUSH_REG +#undef T_REGS +#undef A_REGS +#undef S_REGS +} + +void TurboAssembler::MultiPop(RegList regs) { + int16_t stack_offset = 0; + +#define TEST_AND_POP_REG(reg) \ + if ((regs & reg.bit()) != 0) { \ + Ld(reg, MemOperand(sp, stack_offset)); \ + stack_offset += kPointerSize; \ + regs &= ~reg.bit(); \ + } + +#define T_REGS(V) V(t0) V(t1) V(t2) V(t3) V(t4) V(t5) V(t6) +#define A_REGS(V) V(a0) V(a1) V(a2) V(a3) V(a4) V(a5) V(a6) V(a7) +#define S_REGS(V) \ + V(s1) V(s2) V(s3) V(s4) V(s5) V(s6) V(s7) V(s8) V(s9) V(s10) V(s11) + + // MultiPop pops from the stack in reverse order as MultiPush + if ((regs & t_regs) != 0) { + T_REGS(TEST_AND_POP_REG) + } + if ((regs & a_regs) != 0) { + A_REGS(TEST_AND_POP_REG) + } + if ((regs & s_regs) != 0) { + S_REGS(TEST_AND_POP_REG) + } + TEST_AND_POP_REG(tp); + TEST_AND_POP_REG(gp); + TEST_AND_POP_REG(sp); + TEST_AND_POP_REG(fp); + TEST_AND_POP_REG(ra); + + DCHECK(regs == 0); + + addi(sp, sp, stack_offset); + +#undef TEST_AND_POP_REG +#undef T_REGS +#undef S_REGS +#undef A_REGS +} + +void TurboAssembler::MultiPushFPU(RegList regs) { + int16_t num_to_push = base::bits::CountPopulation(regs); + int16_t stack_offset = num_to_push * kDoubleSize; + + Sub64(sp, sp, Operand(stack_offset)); + for (int16_t i = kNumRegisters - 1; i >= 0; i--) { + if ((regs & (1 << i)) != 0) { + stack_offset -= kDoubleSize; + StoreDouble(FPURegister::from_code(i), MemOperand(sp, stack_offset)); + } + } +} + +void TurboAssembler::MultiPopFPU(RegList regs) { + int16_t stack_offset = 0; + + for (int16_t i = 0; i < kNumRegisters; i++) { + if ((regs & (1 << i)) != 0) { + LoadDouble(FPURegister::from_code(i), MemOperand(sp, stack_offset)); + stack_offset += kDoubleSize; + } + } + addi(sp, sp, stack_offset); +} + +void TurboAssembler::ExtractBits(Register rt, Register rs, uint16_t pos, + uint16_t size, bool sign_extend) { + DCHECK(pos < 64 && 0 < size && size <= 64 && 0 < pos + size && + pos + size <= 64); + slli(rt, rs, 64 - (pos + size)); + if (sign_extend) { + srai(rt, rt, 64 - size); + } else { + srli(rt, rt, 64 - size); + } +} + +void TurboAssembler::InsertBits(Register dest, Register source, Register pos, + int size) { + DCHECK(size < 64); + UseScratchRegisterScope temps(this); + Register mask = temps.Acquire(); + BlockTrampolinePoolScope block_trampoline_pool(this); + Register source_ = temps.hasAvailable() ? temps.Acquire() : t5; + // Create a mask of the length=size. + li(mask, 1); + slli(mask, mask, size); + addi(mask, mask, -1); + and_(source_, mask, source); + sll(source_, source_, pos); + // Make a mask containing 0's. 0's start at "pos" with length=size. + sll(mask, mask, pos); + not_(mask, mask); + // cut area for insertion of source. + and_(dest, mask, dest); + // insert source + or_(dest, dest, source_); +} + +void TurboAssembler::Neg_s(FPURegister fd, FPURegister fs) { fneg_s(fd, fs); } + +void TurboAssembler::Neg_d(FPURegister fd, FPURegister fs) { fneg_d(fd, fs); } + +void TurboAssembler::Cvt_d_uw(FPURegister fd, Register rs) { + // Convert rs to a FP value in fd. + fcvt_d_wu(fd, rs); +} + +void TurboAssembler::Cvt_d_w(FPURegister fd, Register rs) { + // Convert rs to a FP value in fd. + fcvt_d_w(fd, rs); +} + +void TurboAssembler::Cvt_d_ul(FPURegister fd, Register rs) { + // Convert rs to a FP value in fd. + fcvt_d_lu(fd, rs); +} + +void TurboAssembler::Cvt_s_uw(FPURegister fd, Register rs) { + // Convert rs to a FP value in fd. + fcvt_s_wu(fd, rs); +} + +void TurboAssembler::Cvt_s_w(FPURegister fd, Register rs) { + // Convert rs to a FP value in fd. + fcvt_s_w(fd, rs); +} + +void TurboAssembler::Cvt_s_ul(FPURegister fd, Register rs) { + // Convert rs to a FP value in fd. + fcvt_s_lu(fd, rs); +} + +template +void TurboAssembler::RoundFloatingPointToInteger(Register rd, FPURegister fs, + Register result, + CvtFunc fcvt_generator) { + // Save csr_fflags to scratch & clear exception flags + if (result.is_valid()) { + BlockTrampolinePoolScope block_trampoline_pool(this); + UseScratchRegisterScope temps(this); + Register scratch = temps.hasAvailable() ? temps.Acquire() : t5; + + int exception_flags = kInvalidOperation; + csrrci(scratch, csr_fflags, exception_flags); + + // actual conversion instruction + fcvt_generator(this, rd, fs); + + // check kInvalidOperation flag (out-of-range, NaN) + // set result to 1 if normal, otherwise set result to 0 for abnormal + frflags(result); + andi(result, result, exception_flags); + seqz(result, result); // result <-- 1 (normal), result <-- 0 (abnormal) + + // restore csr_fflags + csrw(csr_fflags, scratch); + } else { + // actual conversion instruction + fcvt_generator(this, rd, fs); + } +} + +void TurboAssembler::Trunc_uw_d(Register rd, FPURegister fs, Register result) { + RoundFloatingPointToInteger( + rd, fs, result, [](TurboAssembler* tasm, Register dst, FPURegister src) { + tasm->fcvt_wu_d(dst, src, RTZ); + }); +} + +void TurboAssembler::Trunc_w_d(Register rd, FPURegister fs, Register result) { + RoundFloatingPointToInteger( + rd, fs, result, [](TurboAssembler* tasm, Register dst, FPURegister src) { + tasm->fcvt_w_d(dst, src, RTZ); + }); +} + +void TurboAssembler::Trunc_uw_s(Register rd, FPURegister fs, Register result) { + RoundFloatingPointToInteger( + rd, fs, result, [](TurboAssembler* tasm, Register dst, FPURegister src) { + tasm->fcvt_wu_s(dst, src, RTZ); + }); +} + +void TurboAssembler::Trunc_w_s(Register rd, FPURegister fs, Register result) { + RoundFloatingPointToInteger( + rd, fs, result, [](TurboAssembler* tasm, Register dst, FPURegister src) { + tasm->fcvt_w_s(dst, src, RTZ); + }); +} + +void TurboAssembler::Trunc_ul_d(Register rd, FPURegister fs, Register result) { + RoundFloatingPointToInteger( + rd, fs, result, [](TurboAssembler* tasm, Register dst, FPURegister src) { + tasm->fcvt_lu_d(dst, src, RTZ); + }); +} + +void TurboAssembler::Trunc_l_d(Register rd, FPURegister fs, Register result) { + RoundFloatingPointToInteger( + rd, fs, result, [](TurboAssembler* tasm, Register dst, FPURegister src) { + tasm->fcvt_l_d(dst, src, RTZ); + }); +} + +void TurboAssembler::Trunc_ul_s(Register rd, FPURegister fs, Register result) { + RoundFloatingPointToInteger( + rd, fs, result, [](TurboAssembler* tasm, Register dst, FPURegister src) { + tasm->fcvt_lu_s(dst, src, RTZ); + }); +} + +void TurboAssembler::Trunc_l_s(Register rd, FPURegister fs, Register result) { + RoundFloatingPointToInteger( + rd, fs, result, [](TurboAssembler* tasm, Register dst, FPURegister src) { + tasm->fcvt_l_s(dst, src, RTZ); + }); +} + +void TurboAssembler::Round_w_s(Register rd, FPURegister fs, Register result) { + RoundFloatingPointToInteger( + rd, fs, result, [](TurboAssembler* tasm, Register dst, FPURegister src) { + tasm->fcvt_w_s(dst, src, RNE); + }); +} + +void TurboAssembler::Round_w_d(Register rd, FPURegister fs, Register result) { + RoundFloatingPointToInteger( + rd, fs, result, [](TurboAssembler* tasm, Register dst, FPURegister src) { + tasm->fcvt_w_d(dst, src, RNE); + }); +} + +void TurboAssembler::Ceil_w_s(Register rd, FPURegister fs, Register result) { + RoundFloatingPointToInteger( + rd, fs, result, [](TurboAssembler* tasm, Register dst, FPURegister src) { + tasm->fcvt_w_s(dst, src, RUP); + }); +} + +void TurboAssembler::Ceil_w_d(Register rd, FPURegister fs, Register result) { + RoundFloatingPointToInteger( + rd, fs, result, [](TurboAssembler* tasm, Register dst, FPURegister src) { + tasm->fcvt_w_d(dst, src, RUP); + }); +} + +void TurboAssembler::Floor_w_s(Register rd, FPURegister fs, Register result) { + RoundFloatingPointToInteger( + rd, fs, result, [](TurboAssembler* tasm, Register dst, FPURegister src) { + tasm->fcvt_w_s(dst, src, RDN); + }); +} + +void TurboAssembler::Floor_w_d(Register rd, FPURegister fs, Register result) { + RoundFloatingPointToInteger( + rd, fs, result, [](TurboAssembler* tasm, Register dst, FPURegister src) { + tasm->fcvt_w_d(dst, src, RDN); + }); +} + +// According to JS ECMA specification, for floating-point round operations, if +// the input is NaN, +/-infinity, or +/-0, the same input is returned as the +// rounded result; this differs from behavior of RISCV fcvt instructions (which +// round out-of-range values to the nearest max or min value), therefore special +// handling is needed by NaN, +/-Infinity, +/-0 +template +void TurboAssembler::RoundHelper(FPURegister dst, FPURegister src, + FPURegister fpu_scratch, RoundingMode frm) { + BlockTrampolinePoolScope block_trampoline_pool(this); + UseScratchRegisterScope temps(this); + Register scratch = temps.hasAvailable() ? temps.Acquire() : t5; + + DCHECK((std::is_same::value) || (std::is_same::value)); + // Need at least two FPRs, so check against dst == src == fpu_scratch + DCHECK(!(dst == src && dst == fpu_scratch)); + + const int kFloat32ExponentBias = 127; + const int kFloat32MantissaBits = 23; + const int kFloat32ExponentBits = 8; + const int kFloat64ExponentBias = 1023; + const int kFloat64MantissaBits = 52; + const int kFloat64ExponentBits = 11; + const int kFloatMantissaBits = + sizeof(F) == 4 ? kFloat32MantissaBits : kFloat64MantissaBits; + const int kFloatExponentBits = + sizeof(F) == 4 ? kFloat32ExponentBits : kFloat64ExponentBits; + const int kFloatExponentBias = + sizeof(F) == 4 ? kFloat32ExponentBias : kFloat64ExponentBias; + + Label done; + + // extract exponent value of the source floating-point to t6 + if (std::is_same::value) { + fmv_x_d(scratch, src); + } else { + fmv_x_w(scratch, src); + } + ExtractBits(t6, scratch, kFloatMantissaBits, kFloatExponentBits); + + // if src is NaN/+-Infinity/+-Zero or if the exponent is larger than # of bits + // in mantissa, the result is the same as src, so move src to dest (to avoid + // generating another branch) + if (dst != src) { + if (std::is_same::value) { + fmv_d(dst, src); + } else { + fmv_s(dst, src); + } + } + + // If real exponent (i.e., t6 - kFloatExponentBias) is greater than + // kFloat32MantissaBits, it means the floating-point value has no fractional + // part, thus the input is already rounded, jump to done. Note that, NaN and + // Infinity in floating-point representation sets maximal exponent value, so + // they also satisfy (t6-kFloatExponentBias >= kFloatMantissaBits), and JS + // round semantics specify that rounding of NaN (Infinity) returns NaN + // (Infinity), so NaN and Infinity are considered rounded value too. + Branch(&done, greater_equal, t6, + Operand(kFloatExponentBias + kFloatMantissaBits)); + + // Actual rounding is needed along this path + + // old_src holds the original input, needed for the case of src == dst + FPURegister old_src = src; + if (src == dst) { + DCHECK(fpu_scratch != dst); + Move(fpu_scratch, src); + old_src = fpu_scratch; + } + + // Since only input whose real exponent value is less than kMantissaBits + // (i.e., 23 or 52-bits) falls into this path, the value range of the input + // falls into that of 23- or 53-bit integers. So we round the input to integer + // values, then convert them back to floating-point. + if (std::is_same::value) { + fcvt_l_d(scratch, src, frm); + fcvt_d_l(dst, scratch, frm); + } else { + fcvt_w_s(scratch, src, frm); + fcvt_s_w(dst, scratch, frm); + } + + // A special handling is needed if the input is a very small positive/negative + // number that rounds to zero. JS semantics requires that the rounded result + // retains the sign of the input, so a very small positive (negative) + // floating-point number should be rounded to positive (negative) 0. + // Therefore, we use sign-bit injection to produce +/-0 correctly. Instead of + // testing for zero w/ a branch, we just insert sign-bit for everyone on this + // path (this is where old_src is needed) + if (std::is_same::value) { + fsgnj_d(dst, dst, old_src); + } else { + fsgnj_s(dst, dst, old_src); + } + + bind(&done); +} + +void TurboAssembler::Floor_d_d(FPURegister dst, FPURegister src, + FPURegister fpu_scratch) { + RoundHelper(dst, src, fpu_scratch, RDN); +} + +void TurboAssembler::Ceil_d_d(FPURegister dst, FPURegister src, + FPURegister fpu_scratch) { + RoundHelper(dst, src, fpu_scratch, RUP); +} + +void TurboAssembler::Trunc_d_d(FPURegister dst, FPURegister src, + FPURegister fpu_scratch) { + RoundHelper(dst, src, fpu_scratch, RTZ); +} + +void TurboAssembler::Round_d_d(FPURegister dst, FPURegister src, + FPURegister fpu_scratch) { + RoundHelper(dst, src, fpu_scratch, RNE); +} + +void TurboAssembler::Floor_s_s(FPURegister dst, FPURegister src, + FPURegister fpu_scratch) { + RoundHelper(dst, src, fpu_scratch, RDN); +} + +void TurboAssembler::Ceil_s_s(FPURegister dst, FPURegister src, + FPURegister fpu_scratch) { + RoundHelper(dst, src, fpu_scratch, RUP); +} + +void TurboAssembler::Trunc_s_s(FPURegister dst, FPURegister src, + FPURegister fpu_scratch) { + RoundHelper(dst, src, fpu_scratch, RTZ); +} + +void TurboAssembler::Round_s_s(FPURegister dst, FPURegister src, + FPURegister fpu_scratch) { + RoundHelper(dst, src, fpu_scratch, RNE); +} + +void MacroAssembler::Madd_s(FPURegister fd, FPURegister fr, FPURegister fs, + FPURegister ft) { + fmadd_s(fd, fs, ft, fr); +} + +void MacroAssembler::Madd_d(FPURegister fd, FPURegister fr, FPURegister fs, + FPURegister ft) { + fmadd_d(fd, fs, ft, fr); +} + +void MacroAssembler::Msub_s(FPURegister fd, FPURegister fr, FPURegister fs, + FPURegister ft) { + fmsub_s(fd, fs, ft, fr); +} + +void MacroAssembler::Msub_d(FPURegister fd, FPURegister fr, FPURegister fs, + FPURegister ft) { + fmsub_d(fd, fs, ft, fr); +} + +void TurboAssembler::CompareF32(Register rd, FPUCondition cc, FPURegister cmp1, + FPURegister cmp2) { + switch (cc) { + case EQ: + feq_s(rd, cmp1, cmp2); + break; + case NE: + feq_s(rd, cmp1, cmp2); + NegateBool(rd, rd); + break; + case LT: + flt_s(rd, cmp1, cmp2); + break; + case GE: + fle_s(rd, cmp2, cmp1); + break; + case LE: + fle_s(rd, cmp1, cmp2); + break; + case GT: + flt_s(rd, cmp2, cmp1); + break; + default: + UNREACHABLE(); + } +} + +void TurboAssembler::CompareF64(Register rd, FPUCondition cc, FPURegister cmp1, + FPURegister cmp2) { + switch (cc) { + case EQ: + feq_d(rd, cmp1, cmp2); + break; + case NE: + feq_d(rd, cmp1, cmp2); + NegateBool(rd, rd); + break; + case LT: + flt_d(rd, cmp1, cmp2); + break; + case GE: + fle_d(rd, cmp2, cmp1); + break; + case LE: + fle_d(rd, cmp1, cmp2); + break; + case GT: + flt_d(rd, cmp2, cmp1); + break; + default: + UNREACHABLE(); + } +} + +void TurboAssembler::CompareIsNanF32(Register rd, FPURegister cmp1, + FPURegister cmp2) { + UseScratchRegisterScope temps(this); + BlockTrampolinePoolScope block_trampoline_pool(this); + Register scratch = temps.hasAvailable() ? temps.Acquire() : t5; + + feq_s(rd, cmp1, cmp1); // rd <- !isNan(cmp1) + feq_s(scratch, cmp2, cmp2); // scratch <- !isNaN(cmp2) + And(rd, rd, scratch); // rd <- !isNan(cmp1) && !isNan(cmp2) + Xor(rd, rd, 1); // rd <- isNan(cmp1) || isNan(cmp2) +} + +void TurboAssembler::CompareIsNanF64(Register rd, FPURegister cmp1, + FPURegister cmp2) { + UseScratchRegisterScope temps(this); + BlockTrampolinePoolScope block_trampoline_pool(this); + Register scratch = temps.hasAvailable() ? temps.Acquire() : t5; + + feq_d(rd, cmp1, cmp1); // rd <- !isNan(cmp1) + feq_d(scratch, cmp2, cmp2); // scratch <- !isNaN(cmp2) + And(rd, rd, scratch); // rd <- !isNan(cmp1) && !isNan(cmp2) + Xor(rd, rd, 1); // rd <- isNan(cmp1) || isNan(cmp2) +} + +void TurboAssembler::BranchTrueShortF(Register rs, Label* target) { + Branch(target, not_equal, rs, Operand(zero_reg)); +} + +void TurboAssembler::BranchFalseShortF(Register rs, Label* target) { + Branch(target, equal, rs, Operand(zero_reg)); +} + +void TurboAssembler::BranchTrueF(Register rs, Label* target) { + bool long_branch = + target->is_bound() ? !is_near(target) : is_trampoline_emitted(); + if (long_branch) { + Label skip; + BranchFalseShortF(rs, &skip); + BranchLong(target); + bind(&skip); + } else { + BranchTrueShortF(rs, target); + } +} + +void TurboAssembler::BranchFalseF(Register rs, Label* target) { + bool long_branch = + target->is_bound() ? !is_near(target) : is_trampoline_emitted(); + if (long_branch) { + Label skip; + BranchTrueShortF(rs, &skip); + BranchLong(target); + bind(&skip); + } else { + BranchFalseShortF(rs, target); + } +} + +void TurboAssembler::InsertHighWordF64(FPURegister dst, Register src_high) { + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + BlockTrampolinePoolScope block_trampoline_pool(this); + + DCHECK(src_high != t5 && src_high != scratch); + + fmv_x_d(scratch, dst); + slli(t5, src_high, 32); + slli(scratch, scratch, 32); + srli(scratch, scratch, 32); + or_(scratch, scratch, t5); + fmv_d_x(dst, scratch); +} + +void TurboAssembler::InsertLowWordF64(FPURegister dst, Register src_low) { + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + UseScratchRegisterScope block_trampoline_pool(this); + + DCHECK(src_low != scratch && src_low != t5); + fmv_x_d(scratch, dst); + slli(t5, src_low, 32); + srli(t5, t5, 32); + srli(scratch, scratch, 32); + slli(scratch, scratch, 32); + or_(scratch, scratch, t5); + fmv_d_x(dst, scratch); +} + +void TurboAssembler::LoadFPRImmediate(FPURegister dst, uint32_t src) { + // Handle special values first. + if (src == bit_cast(0.0f) && has_single_zero_reg_set_) { + if (dst != kDoubleRegZero) fmv_s(dst, kDoubleRegZero); + } else if (src == bit_cast(-0.0f) && has_single_zero_reg_set_) { + Neg_s(dst, kDoubleRegZero); + } else { + if (dst == kDoubleRegZero) { + DCHECK(src == bit_cast(0.0f)); + fmv_w_x(dst, zero_reg); + has_single_zero_reg_set_ = true; + has_double_zero_reg_set_ = false; + } else { + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + li(scratch, Operand(static_cast(src))); + fmv_w_x(dst, scratch); + } + } +} + +void TurboAssembler::LoadFPRImmediate(FPURegister dst, uint64_t src) { + // Handle special values first. + if (src == bit_cast(0.0) && has_double_zero_reg_set_) { + if (dst != kDoubleRegZero) fmv_d(dst, kDoubleRegZero); + } else if (src == bit_cast(-0.0) && has_double_zero_reg_set_) { + Neg_d(dst, kDoubleRegZero); + } else { + if (dst == kDoubleRegZero) { + DCHECK(src == bit_cast(0.0)); + fmv_d_x(dst, zero_reg); + has_double_zero_reg_set_ = true; + has_single_zero_reg_set_ = false; + } else { + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + li(scratch, Operand(src)); + fmv_d_x(dst, scratch); + } + } +} + +void TurboAssembler::CompareI(Register rd, Register rs, const Operand& rt, + Condition cond) { + switch (cond) { + case eq: + Seq(rd, rs, rt); + break; + case ne: + Sne(rd, rs, rt); + break; + + // Signed comparison. + case greater: + Sgt(rd, rs, rt); + break; + case greater_equal: + Sge(rd, rs, rt); // rs >= rt + break; + case less: + Slt(rd, rs, rt); // rs < rt + break; + case less_equal: + Sle(rd, rs, rt); // rs <= rt + break; + + // Unsigned comparison. + case Ugreater: + Sgtu(rd, rs, rt); // rs > rt + break; + case Ugreater_equal: + Sgeu(rd, rs, rt); // rs >= rt + break; + case Uless: + Sltu(rd, rs, rt); // rs < rt + break; + case Uless_equal: + Sleu(rd, rs, rt); // rs <= rt + break; + case cc_always: + UNREACHABLE(); + break; + default: + UNREACHABLE(); + } +} + +// dest <- (condition != 0 ? zero : dest) +void TurboAssembler::LoadZeroIfConditionNotZero(Register dest, + Register condition) { + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + seqz(scratch, condition); + // neg + and may be more efficient than mul(dest, dest, scratch) + neg(scratch, scratch); // 0 is still 0, 1 becomes all 1s + and_(dest, dest, scratch); +} + +// dest <- (condition == 0 ? 0 : dest) +void TurboAssembler::LoadZeroIfConditionZero(Register dest, + Register condition) { + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + snez(scratch, condition); + // neg + and may be more efficient than mul(dest, dest, scratch); + neg(scratch, scratch); // 0 is still 0, 1 becomes all 1s + and_(dest, dest, scratch); +} + +void TurboAssembler::Clz32(Register rd, Register xx) { + // 32 bit unsigned in lower word: count number of leading zeros. + // int n = 32; + // unsigned y; + + // y = x >>16; if (y != 0) { n = n -16; x = y; } + // y = x >> 8; if (y != 0) { n = n - 8; x = y; } + // y = x >> 4; if (y != 0) { n = n - 4; x = y; } + // y = x >> 2; if (y != 0) { n = n - 2; x = y; } + // y = x >> 1; if (y != 0) {rd = n - 2; return;} + // rd = n - x; + + Label L0, L1, L2, L3, L4; + DCHECK(xx != t5 && xx != t6); + UseScratchRegisterScope temps(this); + UseScratchRegisterScope block_trampoline_pool(this); + Register x = rd; + Register y = t5; + Register n = t6; + Move(x, xx); + li(n, Operand(32)); + srliw(y, x, 16); + Branch(&L0, eq, y, Operand(zero_reg)); + Move(x, y); + addiw(n, n, -16); + bind(&L0); + srliw(y, x, 8); + Branch(&L1, eq, y, Operand(zero_reg)); + addiw(n, n, -8); + Move(x, y); + bind(&L1); + srliw(y, x, 4); + Branch(&L2, eq, y, Operand(zero_reg)); + addiw(n, n, -4); + Move(x, y); + bind(&L2); + srliw(y, x, 2); + Branch(&L3, eq, y, Operand(zero_reg)); + addiw(n, n, -2); + Move(x, y); + bind(&L3); + srliw(y, x, 1); + subw(rd, n, x); + Branch(&L4, eq, y, Operand(zero_reg)); + addiw(rd, n, -2); + bind(&L4); +} + +void TurboAssembler::Clz64(Register rd, Register xx) { + // 64 bit: count number of leading zeros. + // int n = 64; + // unsigned y; + + // y = x >>32; if (y != 0) { n = n - 32; x = y; } + // y = x >>16; if (y != 0) { n = n - 16; x = y; } + // y = x >> 8; if (y != 0) { n = n - 8; x = y; } + // y = x >> 4; if (y != 0) { n = n - 4; x = y; } + // y = x >> 2; if (y != 0) { n = n - 2; x = y; } + // y = x >> 1; if (y != 0) {rd = n - 2; return;} + // rd = n - x; + + DCHECK(xx != t5 && xx != t6); + Label L0, L1, L2, L3, L4, L5; + UseScratchRegisterScope temps(this); + UseScratchRegisterScope block_trampoline_pool(this); + Register x = rd; + Register y = t5; + Register n = t6; + Move(x, xx); + li(n, Operand(64)); + srli(y, x, 32); + Branch(&L0, eq, y, Operand(zero_reg)); + addiw(n, n, -32); + Move(x, y); + bind(&L0); + srli(y, x, 16); + Branch(&L1, eq, y, Operand(zero_reg)); + addiw(n, n, -16); + Move(x, y); + bind(&L1); + srli(y, x, 8); + Branch(&L2, eq, y, Operand(zero_reg)); + addiw(n, n, -8); + Move(x, y); + bind(&L2); + srli(y, x, 4); + Branch(&L3, eq, y, Operand(zero_reg)); + addiw(n, n, -4); + Move(x, y); + bind(&L3); + srli(y, x, 2); + Branch(&L4, eq, y, Operand(zero_reg)); + addiw(n, n, -2); + Move(x, y); + bind(&L4); + srli(y, x, 1); + subw(rd, n, x); + Branch(&L5, eq, y, Operand(zero_reg)); + addiw(rd, n, -2); + bind(&L5); +} + +void TurboAssembler::Ctz32(Register rd, Register rs) { + // Convert trailing zeroes to trailing ones, and bits to their left + // to zeroes. + UseScratchRegisterScope temps(this); + UseScratchRegisterScope block_trampoline_pool(this); + Register scratch = temps.hasAvailable() ? temps.Acquire() : t5; + Add64(scratch, rs, -1); + Xor(rd, scratch, rs); + And(rd, rd, scratch); + // Count number of leading zeroes. + Clz32(rd, rd); + // Subtract number of leading zeroes from 32 to get number of trailing + // ones. Remember that the trailing ones were formerly trailing zeroes. + li(scratch, 32); + Sub32(rd, scratch, rd); +} + +void TurboAssembler::Ctz64(Register rd, Register rs) { + // Convert trailing zeroes to trailing ones, and bits to their left + // to zeroes. + UseScratchRegisterScope temps(this); + UseScratchRegisterScope block_trampoline_pool(this); + Register scratch = temps.hasAvailable() ? temps.Acquire() : t5; + Add64(scratch, rs, -1); + Xor(rd, scratch, rs); + And(rd, rd, scratch); + // Count number of leading zeroes. + Clz64(rd, rd); + // Subtract number of leading zeroes from 64 to get number of trailing + // ones. Remember that the trailing ones were formerly trailing zeroes. + li(scratch, 64); + Sub64(rd, scratch, rd); +} + +void TurboAssembler::Popcnt32(Register rd, Register rs) { + // https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel + // + // A generalization of the best bit counting method to integers of + // bit-widths up to 128 (parameterized by type T) is this: + // + // v = v - ((v >> 1) & (T)~(T)0/3); // temp + // v = (v & (T)~(T)0/15*3) + ((v >> 2) & (T)~(T)0/15*3); // temp + // v = (v + (v >> 4)) & (T)~(T)0/255*15; // temp + // c = (T)(v * ((T)~(T)0/255)) >> (sizeof(T) - 1) * BITS_PER_BYTE; //count + // + // There are algorithms which are faster in the cases where very few + // bits are set but the algorithm here attempts to minimize the total + // number of instructions executed even when a large number of bits + // are set. + // The number of instruction is 20. + // uint32_t B0 = 0x55555555; // (T)~(T)0/3 + // uint32_t B1 = 0x33333333; // (T)~(T)0/15*3 + // uint32_t B2 = 0x0F0F0F0F; // (T)~(T)0/255*15 + // uint32_t value = 0x01010101; // (T)~(T)0/255 + + DCHECK(rd != t5 && rd != t6 && rs != t5 && rs != t6); + uint32_t shift = 24; + UseScratchRegisterScope temps(this); + BlockTrampolinePoolScope block_trampoline_pool(this); + Register scratch = temps.Acquire(); + Register scratch2 = t5; + Register value = t6; + li(value, 0x01010101); // value = 0x01010101; + li(scratch2, 0x55555555); // B0 = 0x55555555; + Srl32(scratch, rs, 1); + And(scratch, scratch, scratch2); + Sub32(scratch, rs, scratch); + li(scratch2, 0x33333333); // B1 = 0x33333333; + slli(rd, scratch2, 4); + or_(scratch2, scratch2, rd); + And(rd, scratch, scratch2); + Srl32(scratch, scratch, 2); + And(scratch, scratch, scratch2); + Add32(scratch, rd, scratch); + srliw(rd, scratch, 4); + Add32(rd, rd, scratch); + li(scratch2, 0xF); + Mul32(scratch2, value, scratch2); // B2 = 0x0F0F0F0F; + And(rd, rd, scratch2); + Mul32(rd, rd, value); + Srl32(rd, rd, shift); +} + +void TurboAssembler::Popcnt64(Register rd, Register rs) { + // uint64_t B0 = 0x5555555555555555l; // (T)~(T)0/3 + // uint64_t B1 = 0x3333333333333333l; // (T)~(T)0/15*3 + // uint64_t B2 = 0x0F0F0F0F0F0F0F0Fl; // (T)~(T)0/255*15 + // uint64_t value = 0x0101010101010101l; // (T)~(T)0/255 + // uint64_t shift = 24; // (sizeof(T) - 1) * BITS_PER_BYTE + + DCHECK(rd != t5 && rd != t6 && rs != t5 && rs != t6); + uint64_t shift = 24; + UseScratchRegisterScope temps(this); + BlockTrampolinePoolScope block_trampoline_pool(this); + Register scratch = temps.Acquire(); + Register scratch2 = t5; + Register value = t6; + li(value, 0x1111111111111111l); // value = 0x1111111111111111l; + li(scratch2, 5); + Mul64(scratch2, value, scratch2); // B0 = 0x5555555555555555l; + Srl64(scratch, rs, 1); + And(scratch, scratch, scratch2); + Sub64(scratch, rs, scratch); + li(scratch2, 3); + Mul64(scratch2, value, scratch2); // B1 = 0x3333333333333333l; + And(rd, scratch, scratch2); + Srl64(scratch, scratch, 2); + And(scratch, scratch, scratch2); + Add64(scratch, rd, scratch); + Srl64(rd, scratch, 4); + Add64(rd, rd, scratch); + li(scratch2, 0xF); + li(value, 0x0101010101010101l); // value = 0x0101010101010101l; + Mul64(scratch2, value, scratch2); // B2 = 0x0F0F0F0F0F0F0F0Fl; + And(rd, rd, scratch2); + Mul64(rd, rd, value); + srli(rd, rd, 32 + shift); +} + +void TurboAssembler::TryInlineTruncateDoubleToI(Register result, + DoubleRegister double_input, + Label* done) { + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + // if scratch == 1, exception happens during truncation + Trunc_w_d(result, double_input, scratch); + // If we had no exceptions (i.e., scratch==1) we are done. + Branch(done, eq, scratch, Operand(1)); +} + +void TurboAssembler::TruncateDoubleToI(Isolate* isolate, Zone* zone, + Register result, + DoubleRegister double_input, + StubCallMode stub_mode) { + Label done; + + TryInlineTruncateDoubleToI(result, double_input, &done); + + // If we fell through then inline version didn't succeed - call stub + // instead. + push(ra); + Sub64(sp, sp, Operand(kDoubleSize)); // Put input on stack. + fsd(double_input, sp, 0); + + if (stub_mode == StubCallMode::kCallWasmRuntimeStub) { + Call(wasm::WasmCode::kDoubleToI, RelocInfo::WASM_STUB_CALL); + } else { + Call(BUILTIN_CODE(isolate, DoubleToI), RelocInfo::CODE_TARGET); + } + ld(result, sp, 0); + + Add64(sp, sp, Operand(kDoubleSize)); + pop(ra); + + bind(&done); +} + +// 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) { + DCHECK(is_int21(offset)); + BranchShort(offset); +} + +void TurboAssembler::Branch(int32_t offset, Condition cond, Register rs, + const Operand& rt) { + bool is_near = BranchShortCheck(offset, nullptr, cond, rs, rt); + DCHECK(is_near); + USE(is_near); +} + +void TurboAssembler::Branch(Label* L) { + 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) { + 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) { + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + LoadRoot(scratch, index); + Branch(L, cond, rs, Operand(scratch)); +} + +void TurboAssembler::BranchShortHelper(int32_t offset, Label* L) { + DCHECK(L == nullptr || offset == 0); + offset = GetOffset(offset, L, OffsetSize::kOffset21); + j(offset); +} + +void TurboAssembler::BranchShort(int32_t offset) { + DCHECK(is_int21(offset)); + BranchShortHelper(offset, nullptr); +} + +void TurboAssembler::BranchShort(Label* L) { BranchShortHelper(0, L); } + +int32_t TurboAssembler::GetOffset(int32_t offset, Label* L, OffsetSize bits) { + if (L) { + offset = branch_offset_helper(L, bits); + } else { + DCHECK(is_intn(offset, bits)); + } + return offset; +} + +Register TurboAssembler::GetRtAsRegisterHelper(const Operand& rt, + Register scratch) { + 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) { + 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) { + 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) { + DCHECK(L == nullptr || offset == 0); + UseScratchRegisterScope temps(this); + BlockTrampolinePoolScope block_trampoline_pool(this); + Register scratch = temps.hasAvailable() ? temps.Acquire() : t5; + + { + BlockTrampolinePoolScope block_trampoline_pool(this); + switch (cond) { + case cc_always: + if (!CalculateOffset(L, &offset, OffsetSize::kOffset21)) return false; + j(offset); + break; + case eq: + // rs == rt + if (rt.is_reg() && rs == rt.rm()) { + if (!CalculateOffset(L, &offset, OffsetSize::kOffset21)) return false; + j(offset); + } else { + if (!CalculateOffset(L, &offset, OffsetSize::kOffset13, &scratch, rt)) + return false; + DCHECK(rs != scratch); + beq(rs, scratch, offset); + } + break; + case ne: + // rs != rt + if (rt.is_reg() && rs == rt.rm()) { + break; // No code needs to be emitted + } else { + if (!CalculateOffset(L, &offset, OffsetSize::kOffset13, &scratch, rt)) + return false; + DCHECK(rs != scratch); + bne(rs, scratch, offset); + } + break; + + // Signed comparison. + case greater: + // rs > rt + if (rt.is_reg() && rs == rt.rm()) { + break; // No code needs to be emitted. + } else { + if (!CalculateOffset(L, &offset, OffsetSize::kOffset13, &scratch, rt)) + return false; + DCHECK(rs != scratch); + bgt(rs, scratch, offset); + } + break; + case greater_equal: + // rs >= rt + if (rt.is_reg() && rs == rt.rm()) { + if (!CalculateOffset(L, &offset, OffsetSize::kOffset21)) return false; + j(offset); + } else { + if (!CalculateOffset(L, &offset, OffsetSize::kOffset13, &scratch, rt)) + return false; + DCHECK(rs != scratch); + bge(rs, scratch, offset); + } + break; + case less: + // rs < rt + if (rt.is_reg() && rs == rt.rm()) { + break; // No code needs to be emitted. + } else { + if (!CalculateOffset(L, &offset, OffsetSize::kOffset13, &scratch, rt)) + return false; + DCHECK(rs != scratch); + blt(rs, scratch, offset); + } + break; + case less_equal: + // rs <= rt + if (rt.is_reg() && rs == rt.rm()) { + if (!CalculateOffset(L, &offset, OffsetSize::kOffset21)) return false; + j(offset); + } else { + if (!CalculateOffset(L, &offset, OffsetSize::kOffset13, &scratch, rt)) + return false; + DCHECK(rs != scratch); + ble(rs, scratch, offset); + } + break; + + // Unsigned comparison. + case Ugreater: + // rs > rt + if (rt.is_reg() && rs == rt.rm()) { + break; // No code needs to be emitted. + } else { + if (!CalculateOffset(L, &offset, OffsetSize::kOffset13, &scratch, rt)) + return false; + DCHECK(rs != scratch); + bgtu(rs, scratch, offset); + } + break; + case Ugreater_equal: + // rs >= rt + if (rt.is_reg() && rs == rt.rm()) { + if (!CalculateOffset(L, &offset, OffsetSize::kOffset21)) return false; + j(offset); + } else { + if (!CalculateOffset(L, &offset, OffsetSize::kOffset13, &scratch, rt)) + return false; + DCHECK(rs != scratch); + bgeu(rs, scratch, offset); + } + break; + case Uless: + // rs < rt + if (rt.is_reg() && rs == rt.rm()) { + break; // No code needs to be emitted. + } else { + if (!CalculateOffset(L, &offset, OffsetSize::kOffset13, &scratch, rt)) + return false; + DCHECK(rs != scratch); + bltu(rs, scratch, offset); + } + break; + case Uless_equal: + // rs <= rt + if (rt.is_reg() && rs == rt.rm()) { + if (!CalculateOffset(L, &offset, OffsetSize::kOffset21)) return false; + j(offset); + } else { + if (!CalculateOffset(L, &offset, OffsetSize::kOffset13, &scratch, rt)) + return false; + DCHECK(rs != scratch); + bleu(rs, scratch, offset); + } + break; + default: + UNREACHABLE(); + } + } + + CheckTrampolinePoolQuick(1); + return true; +} + +bool TurboAssembler::BranchShortCheck(int32_t offset, Label* L, Condition cond, + Register rs, const Operand& rt) { + BRANCH_ARGS_CHECK(cond, rs, rt); + + if (!L) { + DCHECK(is_int13(offset)); + return BranchShortHelper(offset, nullptr, cond, rs, rt); + } else { + DCHECK_EQ(offset, 0); + return BranchShortHelper(0, L, cond, rs, rt); + } + return false; +} + +void TurboAssembler::BranchShort(int32_t offset, Condition cond, Register rs, + const Operand& rt) { + BranchShortCheck(offset, nullptr, cond, rs, rt); +} + +void TurboAssembler::BranchShort(Label* L, Condition cond, Register rs, + const Operand& rt) { + BranchShortCheck(0, L, cond, rs, rt); +} + +void TurboAssembler::BranchAndLink(int32_t offset) { + BranchAndLinkShort(offset); +} + +void TurboAssembler::BranchAndLink(int32_t offset, Condition cond, Register rs, + const Operand& rt) { + bool is_near = BranchAndLinkShortCheck(offset, nullptr, cond, rs, rt); + DCHECK(is_near); + USE(is_near); +} + +void TurboAssembler::BranchAndLink(Label* L) { + 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) { + 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) { + DCHECK(L == nullptr || offset == 0); + offset = GetOffset(offset, L, OffsetSize::kOffset21); + jal(offset); +} + +void TurboAssembler::BranchAndLinkShort(int32_t offset) { + DCHECK(is_int21(offset)); + BranchAndLinkShortHelper(offset, nullptr); +} + +void TurboAssembler::BranchAndLinkShort(Label* L) { + BranchAndLinkShortHelper(0, L); +} + +// Pre r6 we need to use a bgezal or bltzal, but they can't be used directly +// with the slt instructions. We could use sub or add instead but we would miss +// overflow cases, so we keep slt and add an intermediate third instruction. +bool TurboAssembler::BranchAndLinkShortHelper(int32_t offset, Label* L, + Condition cond, Register rs, + const Operand& rt) { + DCHECK(L == nullptr || offset == 0); + if (!is_near(L, OffsetSize::kOffset21)) return false; + + Register scratch = t5; + BlockTrampolinePoolScope block_trampoline_pool(this); + + if (cond == cc_always) { + offset = GetOffset(offset, L, OffsetSize::kOffset21); + jal(offset); + } else { + Branch(kInstrSize * 2, NegateCondition(cond), rs, + Operand(GetRtAsRegisterHelper(rt, scratch))); + offset = GetOffset(offset, L, OffsetSize::kOffset21); + jal(offset); + } + + return true; +} + +bool TurboAssembler::BranchAndLinkShortCheck(int32_t offset, Label* L, + Condition cond, Register rs, + const Operand& rt) { + 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); + } + return false; +} + +void TurboAssembler::LoadFromConstantsTable(Register destination, + int constant_index) { + DCHECK(RootsTable::IsImmortalImmovable(RootIndex::kBuiltinsConstantsTable)); + LoadRoot(destination, RootIndex::kBuiltinsConstantsTable); + Ld(destination, + FieldMemOperand(destination, + FixedArray::kHeaderSize + constant_index * kPointerSize)); +} + +void TurboAssembler::LoadRootRelative(Register destination, int32_t offset) { + Ld(destination, MemOperand(kRootRegister, offset)); +} + +void TurboAssembler::LoadRootRegisterOffset(Register destination, + intptr_t offset) { + if (offset == 0) { + Move(destination, kRootRegister); + } else { + Add64(destination, kRootRegister, Operand(offset)); + } +} + +void TurboAssembler::Jump(Register target, Condition cond, Register rs, + const Operand& rt) { + BlockTrampolinePoolScope block_trampoline_pool(this); + if (cond == cc_always) { + jr(target); + } else { + BRANCH_ARGS_CHECK(cond, rs, rt); + Branch(kInstrSize * 2, NegateCondition(cond), rs, rt); + jr(target); + } +} + +void TurboAssembler::Jump(intptr_t target, RelocInfo::Mode rmode, + Condition cond, Register rs, const Operand& rt) { + Label skip; + if (cond != cc_always) { + Branch(&skip, NegateCondition(cond), rs, rt); + } + { + BlockTrampolinePoolScope block_trampoline_pool(this); + li(t6, Operand(target, rmode)); + Jump(t6, al, zero_reg, Operand(zero_reg)); + bind(&skip); + } +} + +void TurboAssembler::Jump(Address target, RelocInfo::Mode rmode, Condition cond, + Register rs, const Operand& rt) { + 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) { + DCHECK(RelocInfo::IsCodeTarget(rmode)); + + BlockTrampolinePoolScope block_trampoline_pool(this); + if (root_array_available_ && options().isolate_independent_code) { + IndirectLoadConstant(t6, code); + Add64(t6, t6, Operand(Code::kHeaderSize - kHeapObjectTag)); + Jump(t6, cond, rs, rt); + return; + } else if (options().inline_offheap_trampolines) { + int builtin_index = Builtins::kNoBuiltinId; + if (isolate()->builtins()->IsBuiltinHandle(code, &builtin_index) && + Builtins::IsIsolateIndependent(builtin_index)) { + // Inline the trampoline. + RecordCommentForOffHeapTrampoline(builtin_index); + CHECK_NE(builtin_index, Builtins::kNoBuiltinId); + EmbeddedData d = EmbeddedData::FromBlob(); + Address entry = d.InstructionStartOfBuiltin(builtin_index); + li(t6, Operand(entry, RelocInfo::OFF_HEAP_TARGET)); + Jump(t6, cond, rs, rt); + return; + } + } + + Jump(static_cast(code.address()), rmode, cond, rs, rt); +} + +void TurboAssembler::Jump(const ExternalReference& reference) { + li(t6, reference); + Jump(t6); +} + +// FIXME (RISCV): the comment does not make sense, where is t6 used? +// Note: To call gcc-compiled C code on riscv64, you must call through t6. +void TurboAssembler::Call(Register target, Condition cond, Register rs, + const Operand& rt) { + BlockTrampolinePoolScope block_trampoline_pool(this); + if (cond == cc_always) { + jalr(ra, target, 0); + } else { + BRANCH_ARGS_CHECK(cond, rs, rt); + Branch(kInstrSize * 2, NegateCondition(cond), rs, rt); + jalr(ra, target, 0); + } +} + +void MacroAssembler::JumpIfIsInRange(Register value, unsigned lower_limit, + unsigned higher_limit, + Label* on_in_range) { + if (lower_limit != 0) { + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + Sub64(scratch, value, Operand(lower_limit)); + Branch(on_in_range, Uless_equal, scratch, + Operand(higher_limit - lower_limit)); + } else { + Branch(on_in_range, Uless_equal, value, + Operand(higher_limit - lower_limit)); + } +} + +void TurboAssembler::Call(Address target, RelocInfo::Mode rmode, Condition cond, + Register rs, const Operand& rt) { + BlockTrampolinePoolScope block_trampoline_pool(this); + li(t6, Operand(static_cast(target), rmode), ADDRESS_LOAD); + Call(t6, 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(t6, code); + Add64(t6, t6, Operand(Code::kHeaderSize - kHeapObjectTag)); + Call(t6, cond, rs, rt); + return; + } else if (options().inline_offheap_trampolines) { + int builtin_index = Builtins::kNoBuiltinId; + if (isolate()->builtins()->IsBuiltinHandle(code, &builtin_index) && + Builtins::IsIsolateIndependent(builtin_index)) { + // Inline the trampoline. + RecordCommentForOffHeapTrampoline(builtin_index); + CHECK_NE(builtin_index, Builtins::kNoBuiltinId); + EmbeddedData d = EmbeddedData::FromBlob(); + Address entry = d.InstructionStartOfBuiltin(builtin_index); + li(t6, Operand(entry, RelocInfo::OFF_HEAP_TARGET)); + Call(t6, cond, rs, rt); + return; + } + } + + DCHECK(RelocInfo::IsCodeTarget(rmode)); + DCHECK(code->IsExecutable()); + Call(code.address(), rmode, cond, rs, rt); +} + +void TurboAssembler::LoadEntryFromBuiltinIndex(Register builtin_index) { + 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); + CalcScaledAddress(builtin_index, kRootRegister, builtin_index, + kSystemPointerSizeLog2); + Ld(builtin_index, + MemOperand(builtin_index, IsolateData::builtin_entry_table_offset())); +} + +void TurboAssembler::CallBuiltinByIndex(Register builtin_index) { + LoadEntryFromBuiltinIndex(builtin_index); + Call(builtin_index); +} + +void TurboAssembler::PatchAndJump(Address target) { + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + auipc(scratch, 0); // Load PC into scratch + Ld(t6, MemOperand(scratch, kInstrSize * 4)); + jr(t6); + nop(); // For alignment + DCHECK_EQ(reinterpret_cast(pc_) % 8, 0); + *reinterpret_cast(pc_) = target; // pc_ should be align. + pc_ += sizeof(uint64_t); +} + +void TurboAssembler::StoreReturnAddressAndCall(Register target) { + // 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 = 5; + Label find_ra; + // Adjust the value in ra to point to the correct return location, one + // instruction past the real call into C code (the jalr(t6)), and push it. + // This is the return address of the exit frame. + auipc(ra, 0); // Set ra the current PC + bind(&find_ra); + addi(ra, ra, + (kNumInstructionsToJump + 1) * + kInstrSize); // Set ra to insn after the call + + // This spot was reserved in EnterExitFrame. + Sd(ra, MemOperand(sp)); + addi(sp, sp, -kCArgsSlotsSize); + // Stack is still aligned. + + // Call the C routine. + mv(t6, target); // Function pointer to t6 to conform to ABI for PIC. + jalr(t6); + // Make sure the stored 'ra' points to this position. + DCHECK_EQ(kNumInstructionsToJump, InstructionsGeneratedSince(&find_ra)); +} + +void TurboAssembler::Ret(Condition cond, Register rs, const Operand& rt) { + Jump(ra, cond, rs, rt); +} + +void TurboAssembler::BranchLong(Label* L) { + // Generate position independent long branch. + BlockTrampolinePoolScope block_trampoline_pool(this); + int64_t imm64; + imm64 = branch_long_offset(L); + DCHECK(is_int32(imm64)); + int32_t Hi20 = (((int32_t)imm64 + 0x800) >> 12); + int32_t Lo12 = (int32_t)imm64 << 20 >> 20; + auipc(t5, Hi20); // Read PC + Hi20 into t5. + jr(t5, Lo12); // jump PC + Hi20 + Lo12 +} + +void TurboAssembler::BranchAndLinkLong(Label* L) { + // Generate position independent long branch and link. + BlockTrampolinePoolScope block_trampoline_pool(this); + int64_t imm64; + imm64 = branch_long_offset(L); + DCHECK(is_int32(imm64)); + int32_t Hi20 = (((int32_t)imm64 + 0x800) >> 12); + int32_t Lo12 = (int32_t)imm64 << 20 >> 20; + auipc(t5, Hi20); // Read PC + Hi20 into t5. + jalr(t5, Lo12); // jump PC + Hi20 + Lo12 and read PC + 4 to ra +} + +void TurboAssembler::DropAndRet(int drop) { + Add64(sp, sp, drop * kPointerSize); + Ret(); +} + +void TurboAssembler::DropAndRet(int drop, Condition cond, Register r1, + const Operand& r2) { + // 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) { + if (count <= 0) { + return; + } + + Label skip; + + if (cond != al) { + Branch(&skip, NegateCondition(cond), reg, op); + } + + Add64(sp, sp, Operand(count * kPointerSize)); + + if (cond != al) { + bind(&skip); + } +} + +void MacroAssembler::Swap(Register reg1, Register reg2, Register scratch) { + if (scratch == no_reg) { + Xor(reg1, reg1, Operand(reg2)); + Xor(reg2, reg2, Operand(reg1)); + Xor(reg1, reg1, Operand(reg2)); + } else { + mv(scratch, reg1); + mv(reg1, reg2); + mv(reg2, scratch); + } +} + +void TurboAssembler::Call(Label* target) { BranchAndLink(target); } + +void TurboAssembler::LoadAddress(Register dst, Label* target) { + uint64_t address = jump_address(target); + li(dst, address, ADDRESS_LOAD); +} + +void TurboAssembler::Push(Smi smi) { + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + li(scratch, Operand(smi)); + push(scratch); +} + +void TurboAssembler::Push(Handle handle) { + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + li(scratch, Operand(handle)); + push(scratch); +} + +void MacroAssembler::MaybeDropFrames() { + // Check whether we need to drop frames to restart a function on the stack. + li(a1, ExternalReference::debug_restart_fp_address(isolate())); + Ld(a1, MemOperand(a1)); + Jump(BUILTIN_CODE(isolate(), FrameDropperTrampoline), RelocInfo::CODE_TARGET, + ne, a1, Operand(zero_reg)); +} + +// --------------------------------------------------------------------------- +// Exception handling. + +void MacroAssembler::PushStackHandler() { + // 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(t2, + ExternalReference::Create(IsolateAddressId::kHandlerAddress, isolate())); + Ld(t1, MemOperand(t2)); + push(t1); + + // Set this new handler as the current one. + Sd(sp, MemOperand(t2)); +} + +void MacroAssembler::PopStackHandler() { + STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0); + pop(a1); + Add64(sp, sp, + Operand( + static_cast(StackHandlerConstants::kSize - kPointerSize))); + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + li(scratch, + ExternalReference::Create(IsolateAddressId::kHandlerAddress, isolate())); + Sd(a1, MemOperand(scratch)); +} + +void TurboAssembler::FPUCanonicalizeNaN(const DoubleRegister dst, + const DoubleRegister src) { + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + Label NotNaN; + + fmv_d(dst, src); + feq_d(scratch, src, src); + bne(scratch, zero_reg, &NotNaN); + RV_li(scratch, 0x7ff8000000000000ULL); // This is the canonical NaN + fmv_d_x(dst, scratch); + bind(&NotNaN); +} + +void TurboAssembler::MovFromFloatResult(const DoubleRegister dst) { + Move(dst, fa0); // Reg fa0 is FP return value. +} + +void TurboAssembler::MovFromFloatParameter(const DoubleRegister dst) { + Move(dst, fa0); // Reg fa0 is FP first argument value. +} + +void TurboAssembler::MovToFloatParameter(DoubleRegister src) { Move(fa0, src); } + +void TurboAssembler::MovToFloatResult(DoubleRegister src) { Move(fa0, src); } + +void TurboAssembler::MovToFloatParameters(DoubleRegister src1, + DoubleRegister src2) { + const DoubleRegister fparg2 = fa1; + if (src2 == fa0) { + DCHECK(src1 != fparg2); + Move(fparg2, src2); + Move(fa0, src1); + } else { + Move(fa0, src1); + Move(fparg2, src2); + } +} + +// ----------------------------------------------------------------------------- +// JavaScript invokes. + +void TurboAssembler::PrepareForTailCall(Register callee_args_count, + Register caller_args_count, + Register scratch0, Register scratch1) { + // Calculate the end of destination area where we will put the arguments + // after we drop current frame. We add kPointerSize to count the receiver + // argument which is not included into formal parameters count. + Register dst_reg = scratch0; + CalcScaledAddress(dst_reg, fp, caller_args_count, kPointerSizeLog2); + Add64(dst_reg, dst_reg, + Operand(StandardFrameConstants::kCallerSPOffset + kPointerSize)); + + Register src_reg = caller_args_count; + // Calculate the end of source area. +kPointerSize is for the receiver. + CalcScaledAddress(src_reg, sp, callee_args_count, kPointerSizeLog2); + Add64(src_reg, src_reg, Operand(kPointerSize)); + + if (FLAG_debug_code) { + Check(Uless, AbortReason::kStackAccessBelowStackPointer, src_reg, + Operand(dst_reg)); + } + + // Restore caller's frame pointer and return address now as they will be + // overwritten by the copying loop. + Ld(ra, MemOperand(fp, StandardFrameConstants::kCallerPCOffset)); + Ld(fp, MemOperand(fp, StandardFrameConstants::kCallerFPOffset)); + + // Now copy callee arguments to the caller frame going backwards to avoid + // callee arguments corruption (source and destination areas could overlap). + + // Both src_reg and dst_reg are pointing to the word after the one to copy, + // so they must be pre-decremented in the loop. + Register tmp_reg = scratch1; + Label loop, entry; + Branch(&entry); + bind(&loop); + Sub64(src_reg, src_reg, Operand(kPointerSize)); + Sub64(dst_reg, dst_reg, Operand(kPointerSize)); + Ld(tmp_reg, MemOperand(src_reg)); + Sd(tmp_reg, MemOperand(dst_reg)); + bind(&entry); + Branch(&loop, ne, sp, Operand(src_reg)); + + // Leave current frame. + mv(sp, dst_reg); +} + +void MacroAssembler::InvokePrologue(Register expected_parameter_count, + Register actual_parameter_count, + Label* done, InvokeFlag flag) { + Label regular_invoke; + + // Check whether the expected and actual arguments count match. The + // registers are set up according to contract with + // ArgumentsAdaptorTrampoline: + // a0: actual arguments count + // a1: function (passed through to callee) + // a2: expected arguments count + + // The code below is made a lot easier because the calling code already sets + // up actual and expected registers according to the contract. + + DCHECK_EQ(actual_parameter_count, a0); + DCHECK_EQ(expected_parameter_count, a2); + + Branch(®ular_invoke, eq, expected_parameter_count, + Operand(actual_parameter_count)); + + Handle adaptor = BUILTIN_CODE(isolate(), ArgumentsAdaptorTrampoline); + if (flag == CALL_FUNCTION) { + Call(adaptor); + Branch(done); + } else { + Jump(adaptor, RelocInfo::CODE_TARGET); + } + + 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())); + Lb(t0, MemOperand(t0)); + Branch(&skip_hook, eq, t0, Operand(zero_reg)); + + { + // Load receiver to pass it later to DebugOnFunctionCall hook. + CalcScaledAddress(t0, sp, actual_parameter_count, kPointerSizeLog2); + Ld(t0, MemOperand(t0)); + FrameScope frame(this, + has_frame() ? StackFrame::NONE : 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, + InvokeFlag flag) { + // You can't call a function without a valid frame. + DCHECK_IMPLIES(flag == CALL_FUNCTION, 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, flag); + // 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; + Ld(code, FieldMemOperand(function, JSFunction::kCodeOffset)); + if (flag == CALL_FUNCTION) { + Add64(code, code, Operand(Code::kHeaderSize - kHeapObjectTag)); + Call(code); + } else { + DCHECK(flag == JUMP_FUNCTION); + Add64(code, code, Operand(Code::kHeaderSize - kHeapObjectTag)); + Jump(code); + } + + // 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, + InvokeFlag flag) { + // You can't call a function without a valid frame. + DCHECK_IMPLIES(flag == CALL_FUNCTION, 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; + Ld(temp_reg, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset)); + Ld(cp, FieldMemOperand(a1, JSFunction::kContextOffset)); + // The argument count is stored as uint16_t + Lhu(expected_parameter_count, + FieldMemOperand(temp_reg, + SharedFunctionInfo::kFormalParameterCountOffset)); + + InvokeFunctionCode(a1, new_target, expected_parameter_count, + actual_parameter_count, flag); +} + +void MacroAssembler::InvokeFunction(Register function, + Register expected_parameter_count, + Register actual_parameter_count, + InvokeFlag flag) { + // You can't call a function without a valid frame. + DCHECK_IMPLIES(flag == CALL_FUNCTION, 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. + Ld(cp, FieldMemOperand(a1, JSFunction::kContextOffset)); + + InvokeFunctionCode(a1, no_reg, expected_parameter_count, + actual_parameter_count, flag); +} + +// --------------------------------------------------------------------------- +// Support functions. + +void MacroAssembler::GetObjectType(Register object, Register map, + Register type_reg) { + LoadMap(map, object); + Lhu(type_reg, FieldMemOperand(map, Map::kInstanceTypeOffset)); +} + +// ----------------------------------------------------------------------------- +// Runtime calls. + +void TurboAssembler::AddOverflow64(Register dst, Register left, + const Operand& right, Register overflow) { + BlockTrampolinePoolScope block_trampoline_pool(this); + Register right_reg = no_reg; + Register scratch = t5; + if (!right.is_reg()) { + li(t3, Operand(right)); + right_reg = t3; + } 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) { + add(scratch, left, right_reg); + xor_(overflow, scratch, left); + xor_(t3, scratch, right_reg); + and_(overflow, overflow, t3); + mv(dst, scratch); + } else { + add(dst, left, right_reg); + xor_(overflow, dst, left); + xor_(t3, dst, right_reg); + and_(overflow, overflow, t3); + } +} + +void TurboAssembler::SubOverflow64(Register dst, Register left, + const Operand& right, Register overflow) { + BlockTrampolinePoolScope block_trampoline_pool(this); + Register right_reg = no_reg; + Register scratch = t5; + if (!right.is_reg()) { + li(t3, Operand(right)); + right_reg = t3; + } 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) { + sub(scratch, left, right_reg); + xor_(overflow, left, scratch); + xor_(t3, left, right_reg); + and_(overflow, overflow, t3); + mv(dst, scratch); + } else { + sub(dst, left, right_reg); + xor_(overflow, left, dst); + xor_(t3, left, right_reg); + and_(overflow, overflow, t3); + } +} + +void TurboAssembler::MulOverflow32(Register dst, Register left, + const Operand& right, Register overflow) { + BlockTrampolinePoolScope block_trampoline_pool(this); + Register right_reg = no_reg; + Register scratch = t5; + if (!right.is_reg()) { + li(t3, Operand(right)); + right_reg = t3; + } else { + right_reg = right.rm(); + } + + DCHECK(left != scratch && right_reg != scratch && dst != scratch && + overflow != scratch); + DCHECK(overflow != left && overflow != right_reg); + sext_w(overflow, left); + sext_w(scratch, right_reg); + + mul(overflow, overflow, scratch); + sext_w(dst, overflow); + xor_(overflow, overflow, dst); +} + +void MacroAssembler::CallRuntime(const Runtime::Function* f, int num_arguments, + SaveFPRegsMode save_doubles) { + // All parameters are on the stack. a0 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) { + 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) { + PrepareCEntryFunction(builtin); + Handle code = CodeFactory::CEntry(isolate(), 1, kDontSaveFPRegs, + kArgvOnStack, builtin_exit_frame); + Jump(code, RelocInfo::CODE_TARGET, al, zero_reg, Operand(zero_reg)); +} + +void MacroAssembler::JumpToInstructionStream(Address entry) { + li(kOffHeapTrampolineRegister, Operand(entry, RelocInfo::OFF_HEAP_TARGET)); + Jump(kOffHeapTrampolineRegister); +} + +void MacroAssembler::LoadWeakValue(Register out, Register in, + Label* target_if_cleared) { + Branch(target_if_cleared, eq, in, Operand(kClearedWeakHeapObjectLower32)); + + And(out, in, Operand(~kWeakHeapObjectMask)); +} + +void MacroAssembler::IncrementCounter(StatsCounter* counter, int value, + Register scratch1, Register scratch2) { + DCHECK_GT(value, 0); + if (FLAG_native_code_counters && counter->Enabled()) { + // 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)); + Lw(scratch1, MemOperand(scratch2)); + Add32(scratch1, scratch1, Operand(value)); + Sw(scratch1, MemOperand(scratch2)); + } +} + +void MacroAssembler::DecrementCounter(StatsCounter* counter, int value, + Register scratch1, Register scratch2) { + DCHECK_GT(value, 0); + if (FLAG_native_code_counters && counter->Enabled()) { + // 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)); + Lw(scratch1, MemOperand(scratch2)); + Sub32(scratch1, scratch1, Operand(value)); + Sw(scratch1, MemOperand(scratch2)); + } +} + +// ----------------------------------------------------------------------------- +// Debugging. + +void TurboAssembler::Trap() { stop(); } +void TurboAssembler::DebugBreak() { stop(); } + +void TurboAssembler::Assert(Condition cc, AbortReason reason, Register rs, + Operand rt) { + if (emit_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) { + Label abort_start; + bind(&abort_start); +#ifdef DEBUG + const char* msg = GetAbortReason(reason); + RecordComment("Abort message: "); + RecordComment(msg); +#endif + + // Avoid emitting call to builtin if requested. + if (trap_on_abort()) { + ebreak(); + return; + } + + if (should_abort_hard()) { + // We don't care if we constructed a frame. Just pretend we did. + FrameScope assume_frame(this, StackFrame::NONE); + 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::NONE); + 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 MacroAssembler::LoadMap(Register destination, Register object) { + Ld(destination, FieldMemOperand(object, HeapObject::kMapOffset)); +} + +void MacroAssembler::LoadNativeContextSlot(int index, Register dst) { + LoadMap(dst, cp); + Ld(dst, + FieldMemOperand(dst, Map::kConstructorOrBackPointerOrNativeContextOffset)); + Ld(dst, MemOperand(dst, Context::SlotOffset(index))); +} + +void TurboAssembler::StubPrologue(StackFrame::Type type) { + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + li(scratch, Operand(StackFrame::TypeToMarker(type))); + PushCommonFrame(scratch); +} + +void TurboAssembler::Prologue() { PushStandardFrame(a1); } + +void TurboAssembler::EnterFrame(StackFrame::Type type) { + BlockTrampolinePoolScope block_trampoline_pool(this); + int stack_offset = -3 * kPointerSize; + const int fp_offset = 1 * kPointerSize; + addi(sp, sp, stack_offset); + stack_offset = -stack_offset - kPointerSize; + Sd(ra, MemOperand(sp, stack_offset)); + stack_offset -= kPointerSize; + Sd(fp, MemOperand(sp, stack_offset)); + stack_offset -= kPointerSize; + li(t6, Operand(StackFrame::TypeToMarker(type))); + Sd(t6, MemOperand(sp, stack_offset)); + // Adjust FP to point to saved FP. + DCHECK_EQ(stack_offset, 0); + Add64(fp, sp, Operand(fp_offset)); +} + +void TurboAssembler::LeaveFrame(StackFrame::Type type) { + addi(sp, fp, 2 * kPointerSize); + Ld(ra, MemOperand(fp, 1 * kPointerSize)); + Ld(fp, MemOperand(fp, 0 * kPointerSize)); +} + +void MacroAssembler::EnterExitFrame(bool save_doubles, int stack_space, + StackFrame::Type frame_type) { + 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. + addi(sp, sp, -2 * kPointerSize - ExitFrameConstants::kFixedFrameSizeFromFp); + Sd(ra, MemOperand(sp, 3 * kPointerSize)); + Sd(fp, MemOperand(sp, 2 * kPointerSize)); + { + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + li(scratch, Operand(StackFrame::TypeToMarker(frame_type))); + Sd(scratch, MemOperand(sp, 1 * kPointerSize)); + } + // Set up new frame pointer. + addi(fp, sp, ExitFrameConstants::kFixedFrameSizeFromFp); + + if (emit_debug_code()) { + Sd(zero_reg, MemOperand(fp, ExitFrameConstants::kSPOffset)); + } + + { + BlockTrampolinePoolScope block_trampoline_pool(this); + // Save the frame pointer and the context in top. + li(t5, ExternalReference::Create(IsolateAddressId::kCEntryFPAddress, + isolate())); + Sd(fp, MemOperand(t5)); + li(t5, + ExternalReference::Create(IsolateAddressId::kContextAddress, isolate())); + Sd(cp, MemOperand(t5)); + } + + 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; + int space = kNumOfSavedRegisters * kDoubleSize; + Sub64(sp, sp, Operand(space)); + for (int i = 0; i < kNumOfSavedRegisters; i++) { + FPURegister reg = FPURegister::from_code(i); + StoreDouble(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); + Sub64(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(); + addi(scratch, sp, kPointerSize); + Sd(scratch, MemOperand(fp, ExitFrameConstants::kSPOffset)); +} + +void MacroAssembler::LeaveExitFrame(bool save_doubles, Register argument_count, + bool do_return, + bool argument_count_is_length) { + 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; + Sub64(t5, fp, + Operand(ExitFrameConstants::kFixedFrameSizeFromFp + + kNumOfSavedRegisters * kDoubleSize)); + for (int i = 0; i < kNumOfSavedRegisters; i++) { + FPURegister reg = FPURegister::from_code(2 * i); + LoadDouble(reg, MemOperand(t5, i * kDoubleSize)); + } + } + + // Clear top frame. + li(t5, + ExternalReference::Create(IsolateAddressId::kCEntryFPAddress, isolate())); + Sd(zero_reg, MemOperand(t5)); + + // Restore current context from top and clear it in debug mode. + li(t5, + ExternalReference::Create(IsolateAddressId::kContextAddress, isolate())); + Ld(cp, MemOperand(t5)); + +#ifdef DEBUG + li(t5, + ExternalReference::Create(IsolateAddressId::kContextAddress, isolate())); + Sd(a3, MemOperand(t5)); +#endif + + // Pop the arguments, restore registers, and return. + mv(sp, fp); // Respect ABI stack constraint. + Ld(fp, MemOperand(sp, ExitFrameConstants::kCallerFPOffset)); + Ld(ra, MemOperand(sp, ExitFrameConstants::kCallerPCOffset)); + + if (argument_count.is_valid()) { + if (argument_count_is_length) { + add(sp, sp, argument_count); + } else { + CalcScaledAddress(sp, sp, argument_count, kPointerSizeLog2, t5); + } + } + + addi(sp, sp, 2 * kPointerSize); + + if (do_return) { + Ret(); + } +} + +int TurboAssembler::ActivationFrameAlignment() { +#if V8_HOST_ARCH_RISCV64 + // 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 RISC-V + // platform for another RISC-V platform with a different alignment. + return base::OS::ActivationFrameAlignment(); +#else // V8_HOST_ARCH_RISCV64 + // 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_RISCV64 +} + +void MacroAssembler::AssertStackIsAligned() { + if (emit_debug_code()) { + 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(); + andi(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. + ebreak(); + bind(&alignment_as_expected); + } + } +} + +void TurboAssembler::SmiUntag(Register dst, const MemOperand& src) { + if (SmiValuesAre32Bits()) { + Lw(dst, MemOperand(src.rm(), SmiWordOffset(src.offset()))); + } else { + DCHECK(SmiValuesAre31Bits()); + Lw(dst, src); + SmiUntag(dst); + } +} + +void TurboAssembler::JumpIfSmi(Register value, Label* smi_label, + Register scratch) { + DCHECK_EQ(0, kSmiTag); + andi(scratch, value, kSmiTagMask); + Branch(smi_label, eq, scratch, Operand(zero_reg)); +} + +void MacroAssembler::JumpIfNotSmi(Register value, Label* not_smi_label, + Register scratch) { + DCHECK_EQ(0, kSmiTag); + andi(scratch, value, kSmiTagMask); + Branch(not_smi_label, ne, scratch, Operand(zero_reg)); +} + +void MacroAssembler::AssertNotSmi(Register object) { + if (emit_debug_code()) { + STATIC_ASSERT(kSmiTag == 0); + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + andi(scratch, object, kSmiTagMask); + Check(ne, AbortReason::kOperandIsASmi, scratch, Operand(zero_reg)); + } +} + +void MacroAssembler::AssertSmi(Register object) { + if (emit_debug_code()) { + STATIC_ASSERT(kSmiTag == 0); + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + andi(scratch, object, kSmiTagMask); + Check(eq, AbortReason::kOperandIsASmi, scratch, Operand(zero_reg)); + } +} + +void MacroAssembler::AssertConstructor(Register object) { + if (emit_debug_code()) { + BlockTrampolinePoolScope block_trampoline_pool(this); + STATIC_ASSERT(kSmiTag == 0); + SmiTst(object, t5); + Check(ne, AbortReason::kOperandIsASmiAndNotAConstructor, t5, + Operand(zero_reg)); + + LoadMap(t5, object); + Lbu(t5, FieldMemOperand(t5, Map::kBitFieldOffset)); + And(t5, t5, Operand(Map::Bits1::IsConstructorBit::kMask)); + Check(ne, AbortReason::kOperandIsNotAConstructor, t5, Operand(zero_reg)); + } +} + +void MacroAssembler::AssertFunction(Register object) { + if (emit_debug_code()) { + BlockTrampolinePoolScope block_trampoline_pool(this); + STATIC_ASSERT(kSmiTag == 0); + SmiTst(object, t5); + Check(ne, AbortReason::kOperandIsASmiAndNotAFunction, t5, + Operand(zero_reg)); + GetObjectType(object, t5, t5); + Check(eq, AbortReason::kOperandIsNotAFunction, t5, + Operand(JS_FUNCTION_TYPE)); + } +} + +void MacroAssembler::AssertBoundFunction(Register object) { + if (emit_debug_code()) { + BlockTrampolinePoolScope block_trampoline_pool(this); + STATIC_ASSERT(kSmiTag == 0); + SmiTst(object, t5); + Check(ne, AbortReason::kOperandIsASmiAndNotABoundFunction, t5, + Operand(zero_reg)); + GetObjectType(object, t5, t5); + Check(eq, AbortReason::kOperandIsNotABoundFunction, t5, + Operand(JS_BOUND_FUNCTION_TYPE)); + } +} + +void MacroAssembler::AssertGeneratorObject(Register object) { + if (!emit_debug_code()) return; + BlockTrampolinePoolScope block_trampoline_pool(this); + STATIC_ASSERT(kSmiTag == 0); + SmiTst(object, t5); + Check(ne, AbortReason::kOperandIsASmiAndNotAGeneratorObject, t5, + Operand(zero_reg)); + + GetObjectType(object, t5, t5); + + Label done; + + // Check if JSGeneratorObject + Branch(&done, eq, t5, Operand(JS_GENERATOR_OBJECT_TYPE)); + + // Check if JSAsyncFunctionObject (See MacroAssembler::CompareInstanceType) + Branch(&done, eq, t5, Operand(JS_ASYNC_FUNCTION_OBJECT_TYPE)); + + // Check if JSAsyncGeneratorObject + Branch(&done, eq, t5, Operand(JS_ASYNC_GENERATOR_OBJECT_TYPE)); + + Abort(AbortReason::kOperandIsNotAGeneratorObject); + + bind(&done); +} + +void MacroAssembler::AssertUndefinedOrAllocationSite(Register object, + Register scratch) { + if (emit_debug_code()) { + 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); + } +} + +template +void TurboAssembler::FloatMinMaxHelper(FPURegister dst, FPURegister src1, + FPURegister src2, MaxMinKind kind) { + DCHECK((std::is_same::value) || + (std::is_same::value)); + + if (src1 == src2 && dst != src1) { + if (std::is_same::value) { + fmv_s(dst, src1); + } else { + fmv_d(dst, src1); + } + return; + } + + Label done, nan; + + // For RISCV, fmin_s returns the other non-NaN operand as result if only one + // operand is NaN; but for JS, if any operand is NaN, result is Nan. The + // following handles the discrepency between handling of NaN between ISA and + // JS semantics + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + if (std::is_same::value) { + CompareIsNanF32(scratch, src1, src2); + } else { + CompareIsNanF64(scratch, src1, src2); + } + BranchTrueF(scratch, &nan); + + if (kind == MaxMinKind::kMax) { + if (std::is_same::value) { + fmax_s(dst, src1, src2); + } else { + fmax_d(dst, src1, src2); + } + } else { + if (std::is_same::value) { + fmin_s(dst, src1, src2); + } else { + fmin_d(dst, src1, src2); + } + } + j(&done); + + bind(&nan); + // if any operand is NaN, return NaN (fadd returns NaN if any operand is NaN) + if (std::is_same::value) { + fadd_s(dst, src1, src2); + } else { + fadd_d(dst, src1, src2); + } + + bind(&done); +} + +void TurboAssembler::Float32Max(FPURegister dst, FPURegister src1, + FPURegister src2) { + FloatMinMaxHelper(dst, src1, src2, MaxMinKind::kMax); +} + +void TurboAssembler::Float32Min(FPURegister dst, FPURegister src1, + FPURegister src2) { + FloatMinMaxHelper(dst, src1, src2, MaxMinKind::kMin); +} + +void TurboAssembler::Float64Max(FPURegister dst, FPURegister src1, + FPURegister src2) { + FloatMinMaxHelper(dst, src1, src2, MaxMinKind::kMax); +} + +void TurboAssembler::Float64Min(FPURegister dst, FPURegister src1, + FPURegister src2) { + FloatMinMaxHelper(dst, src1, src2, MaxMinKind::kMin); +} + +static const int kRegisterPassedArguments = 8; + +int TurboAssembler::CalculateStackPassedDWords(int num_gp_arguments, + int num_fp_arguments) { + int stack_passed_dwords = 0; + + // Up to eight integer arguments are passed in registers a0..a7 and + // up to eight floating point arguments are passed in registers fa0..fa7 + if (num_gp_arguments > kRegisterPassedArguments) { + stack_passed_dwords += num_gp_arguments - kRegisterPassedArguments; + } + if (num_fp_arguments > kRegisterPassedArguments) { + stack_passed_dwords += num_fp_arguments - kRegisterPassedArguments; + } + stack_passed_dwords += kCArgSlotCount; + return stack_passed_dwords; +} + +void TurboAssembler::PrepareCallCFunction(int num_reg_arguments, + int num_double_arguments, + Register scratch) { + int frame_alignment = ActivationFrameAlignment(); + + // Up to eight simple arguments in a0..a7, fa0..fa7. + // Remaining arguments are pushed on the stack (arg slot calculation handled + // by CalculateStackPassedDWords()). + int stack_passed_arguments = + CalculateStackPassedDWords(num_reg_arguments, num_double_arguments); + if (frame_alignment > kPointerSize) { + // Make stack end at alignment and make room for stack arguments and the + // original value of sp. + mv(scratch, sp); + Sub64(sp, sp, Operand((stack_passed_arguments + 1) * kPointerSize)); + DCHECK(base::bits::IsPowerOfTwo(frame_alignment)); + And(sp, sp, Operand(-frame_alignment)); + Sd(scratch, MemOperand(sp, stack_passed_arguments * kPointerSize)); + } else { + Sub64(sp, sp, Operand(stack_passed_arguments * kPointerSize)); + } +} + +void TurboAssembler::PrepareCallCFunction(int num_reg_arguments, + Register scratch) { + PrepareCallCFunction(num_reg_arguments, 0, scratch); +} + +void TurboAssembler::CallCFunction(ExternalReference function, + int num_reg_arguments, + int num_double_arguments) { + BlockTrampolinePoolScope block_trampoline_pool(this); + li(t6, function); + CallCFunctionHelper(t6, num_reg_arguments, num_double_arguments); +} + +void TurboAssembler::CallCFunction(Register function, int num_reg_arguments, + int num_double_arguments) { + 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. + +#if V8_HOST_ARCH_RISCV64 + if (emit_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. + ebreak(); + bind(&alignment_as_expected); + } + } +#endif // V8_HOST_ARCH_RISCV64 + + // 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 != t6) { + mv(t6, function); + function = t6; + } + + // 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)); + + auipc(pc_scratch, 0); + // FIXME(RISCV): Does this need an offset? It seems like this should be the + // PC of the call, but MIPS does not seem to do that. + + // See x64 code for reasoning about how to address the isolate data fields. + if (root_array_available()) { + Sd(pc_scratch, MemOperand(kRootRegister, + IsolateData::fast_c_call_caller_pc_offset())); + Sd(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())); + Sd(pc_scratch, MemOperand(scratch)); + li(scratch, ExternalReference::fast_c_call_caller_fp_address(isolate())); + Sd(fp, MemOperand(scratch)); + } + + Call(function); + + if (isolate() != nullptr) { + // We don't unset the PC; the FP is the source of truth. + Register scratch = t1; + li(scratch, ExternalReference::fast_c_call_caller_fp_address(isolate())); + Sd(zero_reg, MemOperand(scratch)); + } + } + + int stack_passed_arguments = + CalculateStackPassedDWords(num_reg_arguments, num_double_arguments); + + if (base::OS::ActivationFrameAlignment() > kPointerSize) { + Ld(sp, MemOperand(sp, stack_passed_arguments * kPointerSize)); + } else { + Add64(sp, sp, Operand(stack_passed_arguments * kPointerSize)); + } +} + +#undef BRANCH_ARGS_CHECK + +void TurboAssembler::CheckPageFlag(Register object, Register scratch, int mask, + Condition cc, Label* condition_met) { + And(scratch, object, Operand(~kPageAlignmentMask)); + Ld(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 = 0; + if (reg1.is_valid()) regs |= reg1.bit(); + if (reg2.is_valid()) regs |= reg2.bit(); + if (reg3.is_valid()) regs |= reg3.bit(); + if (reg4.is_valid()) regs |= reg4.bit(); + if (reg5.is_valid()) regs |= reg5.bit(); + if (reg6.is_valid()) regs |= reg6.bit(); + + 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 & candidate.bit()) continue; + return candidate; + } + UNREACHABLE(); +} + +void TurboAssembler::ComputeCodeStartAddress(Register dst) { + // This push on ra and the pop below together ensure that we restore the + // register ra, which is needed while computing the code start address. + push(ra); + + auipc(ra, 0); + addi(ra, ra, kInstrSize * 2); // ra = address of li + int pc = pc_offset(); + li(dst, Operand(pc)); + Sub64(dst, ra, dst); + + pop(ra); // Restore ra +} + +void TurboAssembler::ResetSpeculationPoisonRegister() { + li(kSpeculationPoisonRegister, -1); +} + +void TurboAssembler::CallForDeoptimization(Address target, int deopt_id, + Label* exit, DeoptimizeKind kind) { + USE(exit, kind); + NoRootArrayScope no_root_array(this); + + // Save the deopt id in kRootRegister (we don't need the roots array from + // now on). + DCHECK_LE(deopt_id, 0xFFFF); + li(kRootRegister, deopt_id); + Call(target, RelocInfo::RUNTIME_ENTRY); +} + +} // namespace internal +} // namespace v8 + +#endif // V8_TARGET_ARCH_RISCV64 Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/codegen/riscv64/macro-assembler-riscv64.h =================================================================== --- /dev/null +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/codegen/riscv64/macro-assembler-riscv64.h @@ -0,0 +1,1181 @@ +// 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_RISCV_MACRO_ASSEMBLER_RISCV_H_ +#define V8_CODEGEN_RISCV_MACRO_ASSEMBLER_RISCV_H_ + +#include "src/codegen/assembler.h" +#include "src/codegen/riscv64/assembler-riscv64.h" +#include "src/common/globals.h" + +namespace v8 { +namespace internal { + +// Forward declarations. +enum class AbortReason : uint8_t; + +// Reserved Register Usage Summary. +// +// Registers t5, t6, and t3 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. +// +// FIXME(RISCV): Cannot find info about this ABI. We chose t6 for now. +// Per the RISC-V ABI, register t6 must be used for indirect function call +// via 'jalr t6' or 'jr t6' instructions. This is relied upon by gcc when +// trying to update gp register for position-independent-code. Whenever +// RISC-V generated code calls C code, it must be via t6 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 lui/ori/slli + // sequence. A number of other optimizations that emits less than + // maximum number of instructions exists. + OPTIMIZE_SIZE = 0, + // Always use 8 instructions (lui/addi/slliw sequence), even if the + // constant + // could be loaded with just one, so that this value is patchable later. + CONSTANT_SIZE = 1, + // For address loads 8 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 RememberedSetAction { EMIT_REMEMBERED_SET, OMIT_REMEMBERED_SET }; +enum SmiCheck { INLINE_SMI_CHECK, OMIT_SMI_CHECK }; +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. + +#if defined(V8_TARGET_LITTLE_ENDIAN) +#define SmiWordOffset(offset) (offset + kPointerSize / 2) +#else +#define SmiWordOffset(offset) offset +#endif + +// 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 RISC-V. + UNREACHABLE(); + } + void LeaveFrame(StackFrame::Type type); + + // 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. + void jmp(Label* L) { Branch(L); } + + // ------------------------------------------------------------------------- + // Debugging. + + void Trap() override; + void DebugBreak() override; + + // 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 + + inline void NegateBool(Register rd, Register rs) { Xor(rd, rs, 1); } + + // Compare float, if any operand is NaN, result is false except for NE + void CompareF32(Register rd, FPUCondition cc, FPURegister cmp1, + FPURegister cmp2); + // Compare double, if any operand is NaN, result is false except for NE + void CompareF64(Register rd, FPUCondition cc, FPURegister cmp1, + FPURegister cmp2); + void CompareIsNanF32(Register rd, FPURegister cmp1, FPURegister cmp2); + void CompareIsNanF64(Register rd, FPURegister cmp1, FPURegister cmp2); + + // Floating point branches + void BranchTrueShortF(Register rs, Label* target); + void BranchFalseShortF(Register rs, Label* target); + + void BranchTrueF(Register rs, Label* target); + void BranchFalseF(Register rs, Label* target); + + void 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) override; + void LoadRootRegisterOffset(Register destination, intptr_t offset) override; + void LoadRootRelative(Register destination, int32_t offset) override; + +// 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) override; + 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_index); + void CallBuiltinByIndex(Register builtin_index) override; + + void LoadCodeObjectEntry(Register destination, + Register code_object) override { + // TODO(RISCV): Implement. + UNIMPLEMENTED(); + } + void CallCodeObject(Register code_object) override { + // TODO(RISCV): Implement. + UNIMPLEMENTED(); + } + void JumpCodeObject(Register code_object) override { + // TODO(RISCV): Implement. + UNIMPLEMENTED(); + } + + // 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(Address target, int deopt_id, Label* exit, + DeoptimizeKind kind); + + 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)); + + // Trivial case of DropAndRet that only emits 2 instructions. + void DropAndRet(int drop); + + void DropAndRet(int drop, Condition cond, Register reg, const Operand& op); + + void Ld(Register rd, const MemOperand& rs); + void Sd(Register rd, const MemOperand& rs); + + void push(Register src) { + Add64(sp, sp, Operand(-kPointerSize)); + Sd(src, MemOperand(sp, 0)); + } + void Push(Register src) { push(src); } + void Push(Handle handle); + void Push(Smi smi); + + // Push two registers. Pushes leftmost register first (to highest address). + void Push(Register src1, Register src2) { + Sub64(sp, sp, Operand(2 * kPointerSize)); + Sd(src1, MemOperand(sp, 1 * kPointerSize)); + Sd(src2, MemOperand(sp, 0 * kPointerSize)); + } + + // Push three registers. Pushes leftmost register first (to highest address). + void Push(Register src1, Register src2, Register src3) { + Sub64(sp, sp, Operand(3 * kPointerSize)); + Sd(src1, MemOperand(sp, 2 * kPointerSize)); + Sd(src2, MemOperand(sp, 1 * kPointerSize)); + Sd(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) { + Sub64(sp, sp, Operand(4 * kPointerSize)); + Sd(src1, MemOperand(sp, 3 * kPointerSize)); + Sd(src2, MemOperand(sp, 2 * kPointerSize)); + Sd(src3, MemOperand(sp, 1 * kPointerSize)); + Sd(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) { + Sub64(sp, sp, Operand(5 * kPointerSize)); + Sd(src1, MemOperand(sp, 4 * kPointerSize)); + Sd(src2, MemOperand(sp, 3 * kPointerSize)); + Sd(src3, MemOperand(sp, 2 * kPointerSize)); + Sd(src4, MemOperand(sp, 1 * kPointerSize)); + Sd(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)); + Sub64(sp, sp, Operand(kPointerSize)); + Sd(src, MemOperand(sp, 0)); + } + + void SaveRegisters(RegList registers); + void RestoreRegisters(RegList registers); + + void CallRecordWriteStub(Register object, Register address, + RememberedSetAction remembered_set_action, + SaveFPRegsMode fp_mode); + void CallRecordWriteStub(Register object, Register address, + RememberedSetAction remembered_set_action, + SaveFPRegsMode fp_mode, Address wasm_target); + void CallEphemeronKeyBarrier(Register object, Register address, + SaveFPRegsMode fp_mode); + + // 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(RegList 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) { + Ld(dst, MemOperand(sp, 0)); + Add64(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); + Ld(src2, MemOperand(sp, 0 * kPointerSize)); + Ld(src1, MemOperand(sp, 1 * kPointerSize)); + Add64(sp, sp, 2 * kPointerSize); + } + + // Pop three registers. Pops rightmost register first (from lower address). + void Pop(Register src1, Register src2, Register src3) { + Ld(src3, MemOperand(sp, 0 * kPointerSize)); + Ld(src2, MemOperand(sp, 1 * kPointerSize)); + Ld(src1, MemOperand(sp, 2 * kPointerSize)); + Add64(sp, sp, 3 * kPointerSize); + } + + void Pop(uint32_t count = 1) { Add64(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(RegList 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(Add32) + DEFINE_INSTRUCTION(Add64) + DEFINE_INSTRUCTION(Div32) + DEFINE_INSTRUCTION(Divu32) + DEFINE_INSTRUCTION(Divu64) + DEFINE_INSTRUCTION(Mod32) + DEFINE_INSTRUCTION(Modu32) + DEFINE_INSTRUCTION(Div64) + DEFINE_INSTRUCTION(Sub32) + DEFINE_INSTRUCTION(Sub64) + DEFINE_INSTRUCTION(Mod64) + DEFINE_INSTRUCTION(Modu64) + DEFINE_INSTRUCTION(Mul32) + DEFINE_INSTRUCTION(Mulh32) + DEFINE_INSTRUCTION(Mul64) + DEFINE_INSTRUCTION(Mulh64) + DEFINE_INSTRUCTION2(Div32) + DEFINE_INSTRUCTION2(Div64) + DEFINE_INSTRUCTION2(Divu32) + DEFINE_INSTRUCTION2(Divu64) + + DEFINE_INSTRUCTION(And) + DEFINE_INSTRUCTION(Or) + DEFINE_INSTRUCTION(Xor) + DEFINE_INSTRUCTION(Nor) + DEFINE_INSTRUCTION2(Neg) + + DEFINE_INSTRUCTION(Slt) + DEFINE_INSTRUCTION(Sltu) + DEFINE_INSTRUCTION(Sle) + DEFINE_INSTRUCTION(Sleu) + DEFINE_INSTRUCTION(Sgt) + DEFINE_INSTRUCTION(Sgtu) + DEFINE_INSTRUCTION(Sge) + DEFINE_INSTRUCTION(Sgeu) + DEFINE_INSTRUCTION(Seq) + DEFINE_INSTRUCTION(Sne) + + DEFINE_INSTRUCTION(Sll64) + DEFINE_INSTRUCTION(Sra64) + DEFINE_INSTRUCTION(Srl64) + DEFINE_INSTRUCTION(Sll32) + DEFINE_INSTRUCTION(Sra32) + DEFINE_INSTRUCTION(Srl32) + + DEFINE_INSTRUCTION2(Seqz) + DEFINE_INSTRUCTION2(Snez) + + DEFINE_INSTRUCTION(Ror) + DEFINE_INSTRUCTION(Dror) +#undef DEFINE_INSTRUCTION +#undef DEFINE_INSTRUCTION2 +#undef DEFINE_INSTRUCTION3 + + void SmiUntag(Register dst, const MemOperand& src); + void SmiUntag(Register dst, Register src) { + if (SmiValuesAre32Bits()) { + srai(dst, src, kSmiShift); + } else { + DCHECK(SmiValuesAre31Bits()); + sraiw(dst, src, kSmiShift); + } + } + + void SmiUntag(Register reg) { SmiUntag(reg, reg); } + + // Removes current frame and its arguments from the stack preserving + // the arguments and a return address pushed to the stack for the next call. + // Both |callee_args_count| and |caller_args_count| do not include + // receiver. |callee_args_count| is not modified. |caller_args_count| + // is trashed. + void PrepareForTailCall(Register callee_args_count, + Register caller_args_count, Register scratch0, + Register scratch1); + + int CalculateStackPassedDWords(int num_gp_arguments, int num_fp_arguments); + + // Before calling a C-function from generated code, align arguments on stack. + // After aligning the frame, non-register arguments must be stored on the + // stack, 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-8 are placed in registers a0 through a7 respectively. + // Arguments 9..n are stored to stack + + // 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); + + // These functions abstract parameter passing for the three different ways + // we call C functions from generated code. + void MovToFloatParameter(DoubleRegister src); + void MovToFloatParameters(DoubleRegister src1, DoubleRegister src2); + void MovToFloatResult(DoubleRegister 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); + + void CompareI(Register rd, Register rs, const Operand& rt, Condition cond); + + void LoadZeroIfConditionNotZero(Register dest, Register condition); + void LoadZeroIfConditionZero(Register dest, Register condition); + + void SignExtendByte(Register rd, Register rs) { + slli(rd, rs, 64 - 8); + srai(rd, rd, 64 - 8); + } + + void SignExtendShort(Register rd, Register rs) { + slli(rd, rs, 64 - 16); + srai(rd, rd, 64 - 16); + } + + void SignExtendWord(Register rd, Register rs) { sext_w(rd, rs); } + void ZeroExtendWord(Register rd, Register rs) { + slli(rd, rs, 32); + srli(rd, rd, 32); + } + + void Clz32(Register rd, Register rs); + void Clz64(Register rd, Register rs); + void Ctz32(Register rd, Register rs); + void Ctz64(Register rd, Register rs); + void Popcnt32(Register rd, Register rs); + void Popcnt64(Register rd, Register rs); + + // Bit field starts at bit pos and extending for size bits is extracted from + // rs and stored zero/sign-extended and right-justified in rt + void ExtractBits(Register rt, Register rs, uint16_t pos, uint16_t size, + bool sign_extend = false); + void ExtractBits(Register dest, Register source, Register pos, int size, + bool sign_extend = false) { + sra(dest, source, pos); + ExtractBits(dest, dest, 0, size, sign_extend); + } + + // Insert bits [0, size) of source to bits [pos, pos+size) of dest + void InsertBits(Register dest, Register source, Register pos, int size); + + void Neg_s(FPURegister fd, FPURegister fs); + void Neg_d(FPURegister fd, FPURegister fs); + + // Change endianness + void ByteSwap(Register dest, Register src, int operand_size); + + // Convert single to unsigned word. + void Trunc_uw_s(Register rd, FPURegister fs, Register result = no_reg); + + // helper functions for unaligned load/store + template + void UnalignedLoadHelper(Register rd, const MemOperand& rs); + template + void UnalignedStoreHelper(Register rd, const MemOperand& rs, + Register scratch_other = no_reg); + + template + void UnalignedFLoadHelper(FPURegister frd, const MemOperand& rs, + Register scratch); + template + void UnalignedFStoreHelper(FPURegister frd, const MemOperand& rs, + Register scratch); + + template + void AlignedLoadHelper(Reg_T target, const MemOperand& rs, Func generator); + template + void AlignedStoreHelper(Reg_T value, const MemOperand& rs, Func generator); + + template + void LoadNBytes(Register rd, const MemOperand& rs, Register scratch); + template + void LoadNBytesOverwritingBaseReg(const MemOperand& rs, Register scratch0, + Register scratch1); + // load/store macros + void Ulh(Register rd, const MemOperand& rs); + void Ulhu(Register rd, const MemOperand& rs); + void Ush(Register rd, const MemOperand& rs); + + void Ulw(Register rd, const MemOperand& rs); + void Ulwu(Register rd, const MemOperand& rs); + void Usw(Register rd, const MemOperand& rs); + + void Uld(Register rd, const MemOperand& rs); + void Usd(Register rd, const MemOperand& rs); + + void ULoadFloat(FPURegister fd, const MemOperand& rs, Register scratch); + void UStoreFloat(FPURegister fd, const MemOperand& rs, Register scratch); + + void ULoadDouble(FPURegister fd, const MemOperand& rs, Register scratch); + void UStoreDouble(FPURegister fd, const MemOperand& rs, Register scratch); + + void Lb(Register rd, const MemOperand& rs); + void Lbu(Register rd, const MemOperand& rs); + void Sb(Register rd, const MemOperand& rs); + + void Lh(Register rd, const MemOperand& rs); + void Lhu(Register rd, const MemOperand& rs); + void Sh(Register rd, const MemOperand& rs); + + void Lw(Register rd, const MemOperand& rs); + void Lwu(Register rd, const MemOperand& rs); + void Sw(Register rd, const MemOperand& rs); + + void LoadFloat(FPURegister fd, const MemOperand& src); + void StoreFloat(FPURegister fs, const MemOperand& dst); + + void LoadDouble(FPURegister fd, const MemOperand& src); + void StoreDouble(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 Float32Max(FPURegister dst, FPURegister src1, FPURegister src2); + void Float32Min(FPURegister dst, FPURegister src1, FPURegister src2); + void Float64Max(FPURegister dst, FPURegister src1, FPURegister src2); + void Float64Min(FPURegister dst, FPURegister src1, FPURegister src2); + template + void FloatMinMaxHelper(FPURegister dst, FPURegister src1, FPURegister src2, + MaxMinKind kind); + + bool IsDoubleZeroRegSet() { return has_double_zero_reg_set_; } + bool IsSingleZeroRegSet() { return has_single_zero_reg_set_; } + + inline void Move(Register dst, Smi smi) { li(dst, Operand(smi)); } + + inline void Move(Register dst, Register src) { + if (dst != src) { + mv(dst, src); + } + } + + inline void Move(FPURegister dst, FPURegister src) { + if (dst != src) fmv_d(dst, src); + } + + inline void Move(Register dst_low, Register dst_high, FPURegister src) { + fmv_x_d(dst_high, src); + fmv_x_w(dst_low, src); + srli(dst_high, dst_high, 32); + } + + inline void Move(Register dst, FPURegister src) { fmv_x_d(dst, src); } + + inline void Move(FPURegister dst, Register src) { fmv_d_x(dst, src); } + + // Extract sign-extended word from high-half of FPR to GPR + inline void ExtractHighWordFromF64(Register dst_high, FPURegister src) { + fmv_x_d(dst_high, src); + srai(dst_high, dst_high, 32); + } + + // Insert low-word from GPR (src_high) to the high-half of FPR (dst) + void InsertHighWordF64(FPURegister dst, Register src_high); + + // Extract sign-extended word from low-half of FPR to GPR + inline void ExtractLowWordFromF64(Register dst_low, FPURegister src) { + fmv_x_w(dst_low, src); + } + + // Insert low-word from GPR (src_high) to the low-half of FPR (dst) + void InsertLowWordF64(FPURegister dst, Register src_low); + + void LoadFPRImmediate(FPURegister dst, float imm) { + LoadFPRImmediate(dst, bit_cast(imm)); + } + void LoadFPRImmediate(FPURegister dst, double imm) { + LoadFPRImmediate(dst, bit_cast(imm)); + } + void LoadFPRImmediate(FPURegister dst, uint32_t src); + void LoadFPRImmediate(FPURegister dst, uint64_t src); + + // AddOverflow64 sets overflow register to a negative value if + // overflow occured, otherwise it is zero or positive + void AddOverflow64(Register dst, Register left, const Operand& right, + Register overflow); + // SubOverflow64 sets overflow register to a negative value if + // overflow occured, otherwise it is zero or positive + void SubOverflow64(Register dst, Register left, const Operand& right, + Register overflow); + // MulOverflow32 sets overflow register to zero if no overflow occured + void MulOverflow32(Register dst, Register left, const Operand& right, + Register overflow); + + // MIPS-style 32-bit unsigned mulh + void Mulhu32(Register dst, Register left, const Operand& right, + Register left_zero, Register right_zero); + + // Number of instructions needed for calculation of switch table entry address + static const int kSwitchTablePrologueSize = 6; + + // 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) override; + void LoadRoot(Register destination, RootIndex index, Condition cond, + Register src1, const Operand& src2); + + // 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, Register rs); + + // convert signed word to double. + void Cvt_d_w(FPURegister fd, Register rs); + + // Convert unsigned long to double. + void Cvt_d_ul(FPURegister fd, Register rs); + + // Convert unsigned word to float. + void Cvt_s_uw(FPURegister fd, Register rs); + + // convert signed word to float. + void Cvt_s_w(FPURegister fd, Register rs); + + // Convert unsigned long to float. + void Cvt_s_ul(FPURegister fd, Register rs); + + // Convert double to unsigned word. + void Trunc_uw_d(Register rd, FPURegister fs, Register result = no_reg); + + // Convert double to signed word. + void Trunc_w_d(Register rd, FPURegister fs, Register result = no_reg); + + // Convert single to signed word. + void Trunc_w_s(Register rd, FPURegister fs, Register result = no_reg); + + // Convert double to unsigned long. + void Trunc_ul_d(Register rd, FPURegister fs, Register result = no_reg); + + // Convert singled to signed long. + void Trunc_l_d(Register rd, FPURegister fs, Register result = no_reg); + + // Convert single to unsigned long. + void Trunc_ul_s(Register rd, FPURegister fs, Register result = no_reg); + + // Convert singled to signed long. + void Trunc_l_s(Register rd, FPURegister fs, Register result = no_reg); + + // Round single to signed word. + void Round_w_s(Register rd, FPURegister fs, Register result = no_reg); + + // Round double to signed word. + void Round_w_d(Register rd, FPURegister fs, Register result = no_reg); + + // Ceil single to signed word. + void Ceil_w_s(Register rd, FPURegister fs, Register result = no_reg); + + // Ceil double to signed word. + void Ceil_w_d(Register rd, FPURegister fs, Register result = no_reg); + + // Floor single to signed word. + void Floor_w_s(Register rd, FPURegister fs, Register result = no_reg); + + // Floor double to signed word. + void Floor_w_d(Register rd, FPURegister fs, Register result = no_reg); + + // Round double functions + void Trunc_d_d(FPURegister fd, FPURegister fs, FPURegister fpu_scratch); + void Round_d_d(FPURegister fd, FPURegister fs, FPURegister fpu_scratch); + void Floor_d_d(FPURegister fd, FPURegister fs, FPURegister fpu_scratch); + void Ceil_d_d(FPURegister fd, FPURegister fs, FPURegister fpu_scratch); + + // Round float functions + void Trunc_s_s(FPURegister fd, FPURegister fs, FPURegister fpu_scratch); + void Round_s_s(FPURegister fd, FPURegister fs, FPURegister fpu_scratch); + void Floor_s_s(FPURegister fd, FPURegister fs, FPURegister fpu_scratch); + void Ceil_s_s(FPURegister fd, FPURegister fs, FPURegister fpu_scratch); + + // Jump the register contains a smi. + void JumpIfSmi(Register value, Label* smi_label, Register scratch = t3); + + void JumpIfEqual(Register a, int32_t b, Label* dest) { + Branch(dest, eq, a, Operand(b)); + } + + void JumpIfLessThan(Register a, int32_t b, Label* dest) { + Branch(dest, lt, a, Operand(b)); + } + + // 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(); + + // Calculated scaled address (rd) as rt + rs << sa + void CalcScaledAddress(Register rd, Register rs, Register rt, uint8_t sa, + Register scratch = t3); + + // 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); + + void ResetSpeculationPoisonRegister(); + + // 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; + bool has_single_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(RISCV) 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); + bool BranchShortHelper(int32_t offset, Label* L, Condition cond, Register rs, + const Operand& rt); + bool BranchShortCheck(int32_t offset, Label* L, Condition cond, Register rs, + const Operand& rt); + + void 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 BranchAndLinkShortCheck(int32_t offset, Label* L, Condition cond, + Register rs, const Operand& rt); + void BranchLong(Label* L); + void BranchAndLinkLong(Label* L); + + template + void RoundHelper(FPURegister dst, FPURegister src, FPURegister fpu_scratch, + RoundingMode mode); + + template + void RoundFloatingPointToInteger(Register rd, FPURegister fs, Register result, + TruncFunc trunc); + + // Push a fixed frame, consisting of ra, fp. + void PushCommonFrame(Register marker_reg = no_reg); + + void CallRecordWriteStub(Register object, Register address, + RememberedSetAction remembered_set_action, + SaveFPRegsMode fp_mode, Handle code_target, + Address wasm_target); +}; + +// MacroAssembler implements a collection of frequently used macros. +class V8_EXPORT_PRIVATE MacroAssembler : public TurboAssembler { + public: + using TurboAssembler::TurboAssembler; + + 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 = EMIT_REMEMBERED_SET, + SmiCheck smi_check = INLINE_SMI_CHECK); + + // 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 = EMIT_REMEMBERED_SET, + SmiCheck smi_check = INLINE_SMI_CHECK); + + // void Pref(int32_t hint, const MemOperand& rs); + + // --------------------------------------------------------------------------- + // Pseudo-instructions. + + void LoadWordPair(Register rd, const MemOperand& rs, Register scratch = t3); + void StoreWordPair(Register rd, const MemOperand& rs, Register scratch = t3); + + void Madd_s(FPURegister fd, FPURegister fr, FPURegister fs, FPURegister ft); + void Madd_d(FPURegister fd, FPURegister fr, FPURegister fs, FPURegister ft); + void Msub_s(FPURegister fd, FPURegister fr, FPURegister fs, FPURegister ft); + void Msub_d(FPURegister fd, FPURegister fr, FPURegister fs, FPURegister ft); + + // 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); + + void LoadMap(Register destination, Register object); + + // 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(Context::GLOBAL_PROXY_INDEX, dst); + } + + void LoadNativeContextSlot(int index, Register dst); + + // 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, InvokeFlag flag); + + // 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, + InvokeFlag flag); + void InvokeFunction(Register function, Register expected_parameter_count, + Register actual_parameter_count, InvokeFlag flag); + + // Frame restart support. + void MaybeDropFrames(); + + // 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); + + // ------------------------------------------------------------------------- + // Runtime calls. + + // Call a runtime routine. + void CallRuntime(const Runtime::Function* f, int num_arguments, + SaveFPRegsMode save_doubles = kDontSaveFPRegs); + + // Convenience function: Same as above, but takes the fid instead. + void CallRuntime(Runtime::FunctionId fid, + SaveFPRegsMode save_doubles = kDontSaveFPRegs) { + const Runtime::Function* function = Runtime::FunctionForId(fid); + CallRuntime(function, function->nargs, save_doubles); + } + + // Convenience function: Same as above, but takes the fid instead. + void CallRuntime(Runtime::FunctionId fid, int num_arguments, + SaveFPRegsMode save_doubles = kDontSaveFPRegs) { + CallRuntime(Runtime::FunctionForId(fid), num_arguments, save_doubles); + } + + // Convenience function: 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 JumpToInstructionStream(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); + void DecrementCounter(StatsCounter* counter, int value, Register scratch1, + Register scratch2); + + // ------------------------------------------------------------------------- + // Smi utilities. + + void SmiTag(Register dst, Register src) { + STATIC_ASSERT(kSmiTag == 0); + if (SmiValuesAre32Bits()) { + // FIXME(RISCV): do not understand the logic here + slli(dst, src, 32); + } else { + DCHECK(SmiValuesAre31Bits()); + Add32(dst, src, src); + } + } + + void SmiTag(Register reg) { SmiTag(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. + srai(dst, src, (kSmiShift - scale) & 0x3F); + } else { + DCHECK(SmiValuesAre31Bits()); + DCHECK_GE(scale, kSmiTagSize); + slliw(dst, src, scale - kSmiTagSize); + } + } + + // 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, + Register scratch = t3); + + // Abort execution if argument is a smi, enabled via --debug-code. + void AssertNotSmi(Register object); + void AssertSmi(Register object); + + // 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 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) { + ExtractBits(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, + InvokeFlag flag); + + // Compute memory operands for safepoint stack slots. + static int SafepointRegisterStackIndex(int reg_code); + + // Needs access to SafepointRegisterStackIndex for compiled frame + // traversal. + friend class StandardFrame; + + 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); + // Load the address from the jump table at index and jump to it + auipc(scratch, 0); // Load the current PC into scratch + slli(t5, index, kPointerSizeLog2); // t5 = offset of indexth entry + add(t5, t5, scratch); // t5 = (saved PC) + (offset of indexth entry) + ld(t5, t5, 6 * kInstrSize); // Add the size of these 6 instructions to the + // offset, then load + jr(t5); // Jump to the address loaded from the table + nop(); // For 16-byte alignment + for (size_t index = 0; index < case_count; ++index) { + dd(GetLabelFunction(index)); + } +} + +#define ACCESS_MASM(masm) masm-> + +} // namespace internal +} // namespace v8 + +#endif // V8_CODEGEN_RISCV_MACRO_ASSEMBLER_RISCV_H_ Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/codegen/riscv64/register-riscv64.h =================================================================== --- /dev/null +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/codegen/riscv64/register-riscv64.h @@ -0,0 +1,345 @@ +// 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_RISCV_REGISTER_RISCV_H_ +#define V8_CODEGEN_RISCV_REGISTER_RISCV_H_ + +#include "src/codegen/register.h" +#include "src/codegen/reglist.h" +#include "src/codegen/riscv64/constants-riscv64.h" + +namespace v8 { +namespace internal { + +// clang-format off +#define GENERAL_REGISTERS(V) \ + V(zero_reg) V(ra) V(sp) V(gp) V(tp) V(t0) V(t1) V(t2) \ + V(fp) V(s1) V(a0) V(a1) V(a2) V(a3) V(a4) V(a5) \ + V(a6) V(a7) V(s2) V(s3) V(s4) V(s5) V(s6) V(s7) V(s8) V(s9) \ + V(s10) V(s11) V(t3) V(t4) V(t5) V(t6) + +#define ALLOCATABLE_GENERAL_REGISTERS(V) \ + V(a0) V(a1) V(a2) V(a3) \ + V(a4) V(a5) V(a6) V(a7) V(t0) V(t1) V(t2) V(s7) V(t4) + +#define DOUBLE_REGISTERS(V) \ + V(ft0) V(ft1) V(ft2) V(ft3) V(ft4) V(ft5) V(ft6) V(ft7) \ + V(fs0) V(fs1) V(fa0) V(fa1) V(fa2) V(fa3) V(fa4) V(fa5) \ + V(fa6) V(fa7) V(fs2) V(fs3) V(fs4) V(fs5) V(fs6) V(fs7) \ + V(fs8) V(fs9) V(fs10) V(fs11) V(ft8) V(ft9) V(ft10) V(ft11) + +#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) + +#define ALLOCATABLE_DOUBLE_REGISTERS(V) \ + V(ft0) V(ft1) V(ft2) V(ft3) \ + V(ft4) V(ft5) V(ft6) V(ft7) V(fa0) V(fa1) V(fa2) V(fa3) V(fa4) V(fa5) \ + V(fa6) V(fa7) + +// clang-format on + +// Note that the bit values must match those used in actual instruction +// encoding. +const int kNumRegs = 32; + +const RegList kJSCallerSaved = 1 << 5 | // t0 + 1 << 6 | // t1 + 1 << 7 | // t2 + 1 << 10 | // a0 + 1 << 11 | // a1 + 1 << 12 | // a2 + 1 << 13 | // a3 + 1 << 14 | // a4 + 1 << 15 | // a5 + 1 << 16 | // a6 + 1 << 17 | // a7 + 1 << 29; // t4 + +const int kNumJSCallerSaved = 12; + +// Callee-saved registers preserved when switching from C to JavaScript. +const RegList kCalleeSaved = 1 << 8 | // fp/s0 + 1 << 9 | // s1 + 1 << 18 | // s2 + 1 << 19 | // s3 + 1 << 20 | // s4 + 1 << 21 | // s5 + 1 << 22 | // s6 (roots in Javascript code) + 1 << 23 | // s7 (cp in Javascript code) + 1 << 24 | // s8 + 1 << 25 | // s9 + 1 << 26 | // s10 + 1 << 27; // s11 + +const int kNumCalleeSaved = 12; + +const RegList kCalleeSavedFPU = 1 << 8 | // fs0 + 1 << 9 | // fs1 + 1 << 18 | // fs2 + 1 << 19 | // fs3 + 1 << 20 | // fs4 + 1 << 21 | // fs5 + 1 << 22 | // fs6 + 1 << 23 | // fs7 + 1 << 24 | // fs8 + 1 << 25 | // fs9 + 1 << 26 | // fs10 + 1 << 27; // fs11 + +const int kNumCalleeSavedFPU = 12; + +const RegList kCallerSavedFPU = 1 << 0 | // ft0 + 1 << 1 | // ft1 + 1 << 2 | // ft2 + 1 << 3 | // ft3 + 1 << 4 | // ft4 + 1 << 5 | // ft5 + 1 << 6 | // ft6 + 1 << 7 | // ft7 + 1 << 10 | // fa0 + 1 << 11 | // fa1 + 1 << 12 | // fa2 + 1 << 13 | // fa3 + 1 << 14 | // fa4 + 1 << 15 | // fa5 + 1 << 16 | // fa6 + 1 << 17 | // fa7 + 1 << 28 | // ft8 + 1 << 29 | // ft9 + 1 << 30 | // ft10 + 1 << 31; // ft11 + +// Number of registers for which space is reserved in safepoints. Must be a +// multiple of 8. +const int kNumSafepointRegisters = 32; + +// Define the list of registers actually saved at safepoints. +// Note that the number of saved registers may be smaller than the reserved +// space, i.e. kNumSafepointSavedRegisters <= kNumSafepointRegisters. +const RegList kSafepointSavedRegisters = kJSCallerSaved | kCalleeSaved; +const int kNumSafepointSavedRegisters = kNumJSCallerSaved + kNumCalleeSaved; + +const int kUndefIndex = -1; +// Map with indexes on stack that corresponds to codes of saved registers. +const int kSafepointRegisterStackIndexMap[kNumRegs] = {kUndefIndex, // zero_reg + kUndefIndex, // ra + kUndefIndex, // sp + kUndefIndex, // gp + kUndefIndex, // tp + 0, // t0 + 1, // t1 + 2, // t2 + 3, // s0/fp + 4, // s1 + 5, // a0 + 6, // a1 + 7, // a2 + 8, // a3 + 9, // a4 + 10, // a5 + 11, // a6 + 12, // a7 + 13, // s2 + 14, // s3 + 15, // s4 + 16, // s5 + 17, // s6 + 18, // s7 + 19, // s8 + 10, // s9 + 21, // s10 + 22, // s11 + kUndefIndex, // t3 + 23, // t4 + kUndefIndex, // t5 + kUndefIndex}; // t6 +// 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: +#if defined(V8_TARGET_LITTLE_ENDIAN) + static constexpr int kMantissaOffset = 0; + static constexpr int kExponentOffset = 4; +#elif defined(V8_TARGET_BIG_ENDIAN) + static constexpr int kMantissaOffset = 4; + static constexpr int kExponentOffset = 0; +#else +#error Unknown endianness +#endif + + private: + friend class RegisterBase; + explicit constexpr Register(int code) : RegisterBase(code) {} +}; + +// s7: context register +// s3: scratch register +// s4: scratch register 2 +#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); + +constexpr bool kPadArguments = false; +constexpr bool kSimpleFPAliasing = true; +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). + + FPURegister low() const { + // TODO(plind): Create DCHECK for FR=0 mode. This usage suspect for FR=1. + // Find low reg of a Double-reg pair, which is the reg itself. + return FPURegister::from_code(code()); + } + FPURegister high() const { + // TODO(plind): Create DCHECK for FR=0 mode. This usage illegal in FR=1. + // Find high reg of a Doubel-reg pair, which is reg + 1. + return FPURegister::from_code(code() + 1); + } + + private: + friend class RegisterBase; + explicit constexpr FPURegister(int code) : RegisterBase(code) {} +}; + +enum MSARegisterCode { +#define REGISTER_CODE(R) kMsaCode_##R, + SIMD128_REGISTERS(REGISTER_CODE) +#undef REGISTER_CODE + kMsaAfterLast +}; + +// MIPS SIMD (MSA) register +// FIXME (RISCV) +class MSARegister : public RegisterBase { + friend class RegisterBase; + explicit constexpr MSARegister(int code) : RegisterBase(code) {} +}; + +// A few double registers are reserved: one as a scratch register and one to +// hold 0.0. +// fs9: 0.0 +// fs11: scratch register. + +// 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 = MSARegister; + +#define DECLARE_SIMD128_REGISTER(R) \ + constexpr Simd128Register R = Simd128Register::from_code(kMsaCode_##R); +SIMD128_REGISTERS(DECLARE_SIMD128_REGISTER) +#undef DECLARE_SIMD128_REGISTER + +const Simd128Register no_msareg = Simd128Register::no_reg(); + +// Register aliases. +// cp is assumed to be a callee saved register. +constexpr Register kRootRegister = s6; +constexpr Register cp = s7; +constexpr Register kScratchReg = s3; +constexpr Register kScratchReg2 = s4; + +constexpr DoubleRegister kScratchDoubleReg = fs11; + +constexpr DoubleRegister kDoubleRegZero = fs9; + +// Define {RegisterName} methods for the register types. +DEFINE_REGISTER_NAMES(Register, GENERAL_REGISTERS) +DEFINE_REGISTER_NAMES(FPURegister, DOUBLE_REGISTERS) +DEFINE_REGISTER_NAMES(MSARegister, SIMD128_REGISTERS) + +// Give alias names to registers for calling conventions. +constexpr Register kReturnRegister0 = a0; +constexpr Register kReturnRegister1 = a1; +constexpr Register kReturnRegister2 = a2; +constexpr Register kJSFunctionRegister = a1; +constexpr Register kContextRegister = s7; +constexpr Register kAllocateSizeRegister = a1; +constexpr Register kSpeculationPoisonRegister = a7; +constexpr Register kInterpreterAccumulatorRegister = a0; +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 = t3; +constexpr Register kRuntimeCallFunctionRegister = a1; +constexpr Register kRuntimeCallArgCountRegister = a0; +constexpr Register kRuntimeCallArgvRegister = a2; +constexpr Register kWasmInstanceRegister = a0; +constexpr Register kWasmCompileLazyFuncIndexRegister = t0; + +constexpr DoubleRegister kFPReturnRegister0 = fa0; + +} // namespace internal +} // namespace v8 + +#endif // V8_CODEGEN_RISCV_REGISTER_RISCV_H_ Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/common/globals.h =================================================================== --- qtwebengine-everywhere-src-5.15.3.orig/src/3rdparty/chromium/v8/src/common/globals.h +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/common/globals.h @@ -58,6 +58,12 @@ constexpr int GB = MB * 1024; #if (V8_TARGET_ARCH_S390 && !V8_HOST_ARCH_S390) #define USE_SIMULATOR 1 #endif +#if (V8_TARGET_ARCH_RISCV64 && !V8_HOST_ARCH_RISCV64) +#define USE_SIMULATOR 1 +#endif +#if (V8_TARGET_ARCH_RISCV && !V8_HOST_ARCH_RISCV && !V8_HOST_ARCH_RISCV64) +#define USE_SIMULATOR 1 +#endif #endif // Determine whether the architecture uses an embedded constant pool Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/compiler/backend/instruction-codes.h =================================================================== --- qtwebengine-everywhere-src-5.15.3.orig/src/3rdparty/chromium/v8/src/compiler/backend/instruction-codes.h +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/compiler/backend/instruction-codes.h @@ -23,6 +23,10 @@ #include "src/compiler/backend/ppc/instruction-codes-ppc.h" #elif V8_TARGET_ARCH_S390 #include "src/compiler/backend/s390/instruction-codes-s390.h" +#elif V8_TARGET_ARCH_RISCV64 +#include "src/compiler/backend/riscv64/instruction-codes-riscv64.h" +#elif V8_TARGET_ARCH_RISCV +#include "src/compiler/backend/riscv/instruction-codes-riscv.h" #else #define TARGET_ARCH_OPCODE_LIST(V) #define TARGET_ADDRESSING_MODE_LIST(V) Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/compiler/backend/instruction-selector.cc =================================================================== --- qtwebengine-everywhere-src-5.15.3.orig/src/3rdparty/chromium/v8/src/compiler/backend/instruction-selector.cc +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/compiler/backend/instruction-selector.cc @@ -2623,7 +2623,8 @@ void InstructionSelector::VisitWord32Ato #endif // !V8_TARGET_ARCH_IA32 && !V8_TARGET_ARCH_ARM && !V8_TARGET_ARCH_MIPS #if !V8_TARGET_ARCH_X64 && !V8_TARGET_ARCH_ARM64 && !V8_TARGET_ARCH_MIPS64 && \ - !V8_TARGET_ARCH_S390 && !V8_TARGET_ARCH_PPC64 + !V8_TARGET_ARCH_S390 && !V8_TARGET_ARCH_PPC64 && \ + !V8_TARGET_ARCH_RISCV64 && !V8_TARGET_ARCH_RISCV void InstructionSelector::VisitWord64AtomicLoad(Node* node) { UNIMPLEMENTED(); } void InstructionSelector::VisitWord64AtomicStore(Node* node) { @@ -2648,7 +2649,8 @@ void InstructionSelector::VisitWord64Ato UNIMPLEMENTED(); } #endif // !V8_TARGET_ARCH_X64 && !V8_TARGET_ARCH_ARM64 && !V8_TARGET_ARCH_PPC64 - // !V8_TARGET_ARCH_MIPS64 && !V8_TARGET_ARCH_S390 + // !V8_TARGET_ARCH_MIPS64 && !V8_TARGET_ARCH_S390 && + // !V8_TARGET_ARCH_RISCV64 && !V8_TARGET_ARCH_RISCV #if !V8_TARGET_ARCH_IA32 && !V8_TARGET_ARCH_ARM // This is only needed on 32-bit to split the 64-bit value into two operands. Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/compiler/backend/riscv64/code-generator-riscv64.cc =================================================================== --- /dev/null +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/compiler/backend/riscv64/code-generator-riscv64.cc @@ -0,0 +1,2759 @@ +// 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/macro-assembler.h" +#include "src/codegen/optimized-compilation-info.h" +#include "src/codegen/riscv64/constants-riscv64.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" +#include "src/wasm/wasm-code-manager.h" + +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_riscv64: %s at line %d\n", \ + __FUNCTION__, __LINE__) + +// Adds RISC-V-specific methods to convert InstructionOperands. +class RiscvOperandConverter final : public InstructionOperandConverter { + public: + RiscvOperandConverter(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 RISC-V, + // 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 + break; + } + 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), + stub_mode_(stub_mode), + must_save_lr_(!gen->frame_access_state()->has_frame()), + zone_(gen->zone()) {} + + void Generate() final { + if (mode_ > RecordWriteMode::kValueIsPointer) { + __ JumpIfSmi(value_, exit()); + } + __ CheckPageFlag(value_, scratch0_, + MemoryChunk::kPointersToHereAreInterestingMask, eq, + exit()); + __ Add64(scratch1_, object_, index_); + RememberedSetAction const remembered_set_action = + mode_ > RecordWriteMode::kValueIsMap ? EMIT_REMEMBERED_SET + : OMIT_REMEMBERED_SET; + SaveFPRegsMode const save_fp_mode = + frame()->DidAllocateDoubleRegisters() ? kSaveFPRegs : kDontSaveFPRegs; + 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); + } 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. + __ CallRecordWriteStub(object_, scratch1_, remembered_set_action, + save_fp_mode, wasm::WasmCode::kRecordWrite); + } else { + __ CallRecordWriteStub(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_; + StubCallMode const stub_mode_; + bool must_save_lr_; + Zone* zone_; +}; + +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 Uless; + case kUnsignedGreaterThanOrEqual: + return Ugreater_equal; + case kUnsignedLessThanOrEqual: + return Uless_equal; + case kUnsignedGreaterThan: + return Ugreater; + 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 LT; + case kUnsignedGreaterThanOrEqual: + *predicate = false; + return LT; + case kUnsignedLessThanOrEqual: + *predicate = true; + return LE; + case kUnsignedGreaterThan: + *predicate = false; + return LE; + case kUnorderedEqual: + case kUnorderedNotEqual: + *predicate = true; + break; + default: + *predicate = true; + break; + } + UNREACHABLE(); +} + +void EmitWordLoadPoisoningIfNeeded(CodeGenerator* codegen, + InstructionCode opcode, Instruction* instr, + RiscvOperandConverter const& i) { + const MemoryAccessMode access_mode = + static_cast(MiscField::decode(opcode)); + if (access_mode == kMemoryAccessPoisoned) { + Register value = i.OutputRegister(); + codegen->tasm()->And(value, value, kSpeculationPoisonRegister); + } +} + +} // namespace + +#define ASSEMBLE_ATOMIC_LOAD_INTEGER(asm_instr) \ + do { \ + __ asm_instr(i.OutputRegister(), i.MemoryOperand()); \ + __ sync(); \ + } while (0) + +#define ASSEMBLE_ATOMIC_STORE_INTEGER(asm_instr) \ + do { \ + __ sync(); \ + __ asm_instr(i.InputOrZeroRegister(2), i.MemoryOperand()); \ + __ sync(); \ + } while (0) + +#define ASSEMBLE_ATOMIC_BINOP(load_linked, store_conditional, bin_instr) \ + do { \ + Label binop; \ + __ Add64(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1)); \ + __ sync(); \ + __ bind(&binop); \ + __ load_linked(i.OutputRegister(0), MemOperand(i.TempRegister(0), 0)); \ + __ bin_instr(i.TempRegister(1), i.OutputRegister(0), \ + Operand(i.InputRegister(2))); \ + __ store_conditional(i.TempRegister(1), MemOperand(i.TempRegister(0), 0)); \ + __ BranchShort(&binop, ne, i.TempRegister(1), Operand(zero_reg)); \ + __ sync(); \ + } while (0) + +#define ASSEMBLE_ATOMIC_BINOP_EXT(load_linked, store_conditional, sign_extend, \ + size, bin_instr, representation) \ + do { \ + Label binop; \ + __ Add64(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1)); \ + if (representation == 32) { \ + __ And(i.TempRegister(3), i.TempRegister(0), 0x3); \ + } else { \ + DCHECK_EQ(representation, 64); \ + __ And(i.TempRegister(3), i.TempRegister(0), 0x7); \ + } \ + __ Sub64(i.TempRegister(0), i.TempRegister(0), \ + Operand(i.TempRegister(3))); \ + __ Sll32(i.TempRegister(3), i.TempRegister(3), 3); \ + __ sync(); \ + __ bind(&binop); \ + __ load_linked(i.TempRegister(1), MemOperand(i.TempRegister(0), 0)); \ + __ 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); \ + __ store_conditional(i.TempRegister(1), MemOperand(i.TempRegister(0), 0)); \ + __ BranchShort(&binop, ne, i.TempRegister(1), Operand(zero_reg)); \ + __ sync(); \ + } while (0) + +#define ASSEMBLE_ATOMIC_EXCHANGE_INTEGER(load_linked, store_conditional) \ + do { \ + Label exchange; \ + __ sync(); \ + __ bind(&exchange); \ + __ Add64(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1)); \ + __ load_linked(i.OutputRegister(0), MemOperand(i.TempRegister(0), 0)); \ + __ Move(i.TempRegister(1), i.InputRegister(2)); \ + __ store_conditional(i.TempRegister(1), MemOperand(i.TempRegister(0), 0)); \ + __ BranchShort(&exchange, ne, i.TempRegister(1), Operand(zero_reg)); \ + __ sync(); \ + } while (0) + +#define ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT( \ + load_linked, store_conditional, sign_extend, size, representation) \ + do { \ + Label exchange; \ + __ Add64(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1)); \ + if (representation == 32) { \ + __ And(i.TempRegister(1), i.TempRegister(0), 0x3); \ + } else { \ + DCHECK_EQ(representation, 64); \ + __ And(i.TempRegister(1), i.TempRegister(0), 0x7); \ + } \ + __ Sub64(i.TempRegister(0), i.TempRegister(0), \ + Operand(i.TempRegister(1))); \ + __ Sll32(i.TempRegister(1), i.TempRegister(1), 3); \ + __ sync(); \ + __ bind(&exchange); \ + __ load_linked(i.TempRegister(2), MemOperand(i.TempRegister(0), 0)); \ + __ ExtractBits(i.OutputRegister(0), i.TempRegister(2), i.TempRegister(1), \ + size, sign_extend); \ + __ InsertBits(i.TempRegister(2), i.InputRegister(2), i.TempRegister(1), \ + size); \ + __ store_conditional(i.TempRegister(2), MemOperand(i.TempRegister(0), 0)); \ + __ BranchShort(&exchange, ne, i.TempRegister(2), Operand(zero_reg)); \ + __ sync(); \ + } while (0) + +#define ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER(load_linked, \ + store_conditional) \ + do { \ + Label compareExchange; \ + Label exit; \ + __ Add64(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1)); \ + __ sync(); \ + __ bind(&compareExchange); \ + __ load_linked(i.OutputRegister(0), MemOperand(i.TempRegister(0), 0)); \ + __ BranchShort(&exit, ne, i.InputRegister(2), \ + Operand(i.OutputRegister(0))); \ + __ Move(i.TempRegister(2), i.InputRegister(3)); \ + __ store_conditional(i.TempRegister(2), MemOperand(i.TempRegister(0), 0)); \ + __ BranchShort(&compareExchange, ne, i.TempRegister(2), \ + Operand(zero_reg)); \ + __ bind(&exit); \ + __ sync(); \ + } while (0) + +#define ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT( \ + load_linked, store_conditional, sign_extend, size, representation) \ + do { \ + Label compareExchange; \ + Label exit; \ + __ Add64(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1)); \ + if (representation == 32) { \ + __ And(i.TempRegister(1), i.TempRegister(0), 0x3); \ + } else { \ + DCHECK_EQ(representation, 64); \ + __ And(i.TempRegister(1), i.TempRegister(0), 0x7); \ + } \ + __ Sub64(i.TempRegister(0), i.TempRegister(0), \ + Operand(i.TempRegister(1))); \ + __ Sll32(i.TempRegister(1), i.TempRegister(1), 3); \ + __ sync(); \ + __ bind(&compareExchange); \ + __ load_linked(i.TempRegister(2), MemOperand(i.TempRegister(0), 0)); \ + __ ExtractBits(i.OutputRegister(0), i.TempRegister(2), i.TempRegister(1), \ + size, sign_extend); \ + __ ExtractBits(i.InputRegister(2), i.InputRegister(2), i.TempRegister(1), \ + size, sign_extend); \ + __ BranchShort(&exit, ne, i.InputRegister(2), \ + Operand(i.OutputRegister(0))); \ + __ InsertBits(i.TempRegister(2), i.InputRegister(3), i.TempRegister(1), \ + size); \ + __ store_conditional(i.TempRegister(2), MemOperand(i.TempRegister(0), 0)); \ + __ BranchShort(&compareExchange, ne, i.TempRegister(2), \ + Operand(zero_reg)); \ + __ bind(&exit); \ + __ sync(); \ + } 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) + +#define ASSEMBLE_F64X2_ARITHMETIC_BINOP(op) \ + do { \ + __ op(i.OutputSimd128Register(), i.InputSimd128Register(0), \ + i.InputSimd128Register(1)); \ + } while (0) + +void CodeGenerator::AssembleDeconstructFrame() { + __ Move(sp, fp); + __ Pop(ra, fp); +} + +void CodeGenerator::AssemblePrepareTailCall() { + if (frame_access_state()->has_frame()) { + __ Ld(ra, MemOperand(fp, StandardFrameConstants::kCallerPCOffset)); + __ Ld(fp, MemOperand(fp, StandardFrameConstants::kCallerFPOffset)); + } + frame_access_state()->SetFrameAccessToSP(); +} + +void CodeGenerator::AssemblePopArgumentsAdaptorFrame(Register args_reg, + Register scratch1, + Register scratch2, + Register scratch3) { + DCHECK(!AreAliased(args_reg, scratch1, scratch2, scratch3)); + Label done; + + // Check if current frame is an arguments adaptor frame. + __ Ld(scratch3, MemOperand(fp, StandardFrameConstants::kContextOffset)); + __ Branch(&done, ne, scratch3, + Operand(StackFrame::TypeToMarker(StackFrame::ARGUMENTS_ADAPTOR))); + + // Load arguments count from current arguments adaptor frame (note, it + // does not include receiver). + Register caller_args_count_reg = scratch1; + __ Ld(caller_args_count_reg, + MemOperand(fp, ArgumentsAdaptorFrameConstants::kLengthOffset)); + __ SmiUntag(caller_args_count_reg); + + __ PrepareForTailCall(args_reg, caller_args_count_reg, scratch2, scratch3); + __ bind(&done); +} + +namespace { + +void AdjustStackPointerForTailCall(TurboAssembler* tasm, + FrameAccessState* state, + int new_slot_above_sp, + bool allow_shrinkage = true) { + int current_sp_offset = state->GetSPToFPSlotCount() + + StandardFrameConstants::kFixedSlotCountAboveFp; + int stack_slot_delta = new_slot_above_sp - current_sp_offset; + if (stack_slot_delta > 0) { + tasm->Sub64(sp, sp, stack_slot_delta * kSystemPointerSize); + state->IncreaseSPDelta(stack_slot_delta); + } else if (allow_shrinkage && stack_slot_delta < 0) { + tasm->Add64(sp, sp, -stack_slot_delta * kSystemPointerSize); + state->IncreaseSPDelta(stack_slot_delta); + } +} + +} // namespace + +void CodeGenerator::AssembleTailCallBeforeGap(Instruction* instr, + int first_unused_stack_slot) { + AdjustStackPointerForTailCall(tasm(), frame_access_state(), + first_unused_stack_slot, false); +} + +void CodeGenerator::AssembleTailCallAfterGap(Instruction* instr, + int first_unused_stack_slot) { + AdjustStackPointerForTailCall(tasm(), frame_access_state(), + first_unused_stack_slot); +} + +// 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; + __ Ld(kScratchReg, MemOperand(kJavaScriptCallCodeStartRegister, offset)); + __ Lw(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)); +} + +void CodeGenerator::GenerateSpeculationPoisonFromCodeStartRegister() { + // Calculate a mask which has all bits set in the normal case, but has all + // bits cleared if we are speculatively executing the wrong PC. + // difference = (current - expected) | (expected - current) + // poison = ~(difference >> (kBitsPerSystemPointer - 1)) + __ ComputeCodeStartAddress(kScratchReg); + __ Move(kSpeculationPoisonRegister, kScratchReg); + __ Sub32(kSpeculationPoisonRegister, kSpeculationPoisonRegister, + kJavaScriptCallCodeStartRegister); + __ Sub32(kJavaScriptCallCodeStartRegister, kJavaScriptCallCodeStartRegister, + kScratchReg); + __ or_(kSpeculationPoisonRegister, kSpeculationPoisonRegister, + kJavaScriptCallCodeStartRegister); + __ Sra64(kSpeculationPoisonRegister, kSpeculationPoisonRegister, + kBitsPerSystemPointer - 1); + __ Nor(kSpeculationPoisonRegister, kSpeculationPoisonRegister, + kSpeculationPoisonRegister); +} + +void CodeGenerator::AssembleRegisterArgumentPoisoning() { + __ And(kJSFunctionRegister, kJSFunctionRegister, kSpeculationPoisonRegister); + __ And(kContextRegister, kContextRegister, kSpeculationPoisonRegister); + __ And(sp, sp, kSpeculationPoisonRegister); +} + +// Assembles an instruction after register allocation, producing machine code. +CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction( + Instruction* instr) { + RiscvOperandConverter 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( + HasCallDescriptorFlag(instr, CallDescriptor::kFixedTargetRegister), + reg == kJavaScriptCallCodeStartRegister); + __ Add64(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; + } + case kArchCallWasmFunction: { + // FIXME (RISCV): isnt this test deadcode? + if (arch_opcode == kArchTailCallCodeObjectFromJSFunction) { + AssemblePopArgumentsAdaptorFrame(kJavaScriptCallArgCountRegister, + i.TempRegister(0), i.TempRegister(1), + i.TempRegister(2)); + } + 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 { + __ Add64(kScratchReg, i.InputRegister(0), 0); + __ Call(kScratchReg); + } + RecordCallPosition(instr); + frame_access_state()->ClearSPDelta(); + break; + } + case kArchTailCallCodeObjectFromJSFunction: + case kArchTailCallCodeObject: { + if (arch_opcode == kArchTailCallCodeObjectFromJSFunction) { + AssemblePopArgumentsAdaptorFrame(kJavaScriptCallArgCountRegister, + i.TempRegister(0), i.TempRegister(1), + i.TempRegister(2)); + } + if (instr->InputAt(0)->IsImmediate()) { + __ Jump(i.InputCode(0), RelocInfo::CODE_TARGET); + } else { + Register reg = i.InputRegister(0); + DCHECK_IMPLIES( + HasCallDescriptorFlag(instr, CallDescriptor::kFixedTargetRegister), + reg == kJavaScriptCallCodeStartRegister); + __ Add64(reg, reg, Code::kHeaderSize - kHeapObjectTag); + __ Jump(reg); + } + frame_access_state()->ClearSPDelta(); + frame_access_state()->SetFrameAccessToDefault(); + 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 { + __ Add64(kScratchReg, i.InputRegister(0), 0); + __ Jump(kScratchReg); + } + frame_access_state()->ClearSPDelta(); + frame_access_state()->SetFrameAccessToDefault(); + break; + } + case kArchTailCallAddress: { + CHECK(!instr->InputAt(0)->IsImmediate()); + Register reg = i.InputRegister(0); + DCHECK_IMPLIES( + HasCallDescriptorFlag(instr, 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. + __ Ld(kScratchReg, FieldMemOperand(func, JSFunction::kContextOffset)); + __ Assert(eq, AbortReason::kWrongFunctionContext, cp, + Operand(kScratchReg)); + } + static_assert(kJavaScriptCallCodeStartRegister == a2, "ABI mismatch"); + __ Ld(a2, FieldMemOperand(func, JSFunction::kCodeOffset)); + __ Add64(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_ == kDontSaveFPRegs || fp_mode_ == kSaveFPRegs); + // 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_ == kDontSaveFPRegs || fp_mode_ == kSaveFPRegs); + // 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()); + Label start_call; + bool isWasmCapiFunction = + linkage()->GetIncomingDescriptor()->IsWasmCapiFunction(); + // from start_call to return address. + // FIXME (RISC_V): is the same number of instructions generated from + // &start_call to after __CallCFunction()? This code seems quite brittle. + // Better to use label and PC-relative addressing to generate the return + // address + int offset = __ root_array_available() ? 64 : 72; +#if V8_HOST_ARCH_RISCV64 + if (__ emit_debug_code()) { + offset += 16; + } +#endif + if (isWasmCapiFunction) { + // Put the return address in a stack slot. + __ bind(&start_call); + __ auipc(kScratchReg, 0); + __ Add64(kScratchReg, kScratchReg, offset); + __ Sd(kScratchReg, + MemOperand(fp, WasmExitFrameConstants::kCallingPCOffset)); + } + 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 (isWasmCapiFunction) { + CHECK_EQ(offset, __ SizeOfCodeGeneratedSince(&start_call)); + RecordSafepoint(instr->reference_map(), Safepoint::kNoLazyDeopt); + } + + 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-sync 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 kArchAbortCSAAssert: + 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::NONE); + __ Call( + isolate()->builtins()->builtin_handle(Builtins::kAbortCSAAssert), + RelocInfo::CODE_TARGET); + } + __ stop(); + break; + case kArchDebugBreak: + __ DebugBreak(); + 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, OutputFrameStateCombine::Ignore()); + CodeGenResult result = AssembleDeoptimizerCall(exit); + if (result != kSuccess) return result; + break; + } + case kArchRet: + AssembleReturn(instr->InputAt(0)); + break; + case kArchStackPointerGreaterThan: + // Pseudo-instruction used for cmp/branch. No opcode emitted here. + break; + case kArchStackCheckOffset: + __ Move(i.OutputRegister(), Smi::FromInt(GetStackCheckOffset())); + break; + case kArchFramePointer: + __ Move(i.OutputRegister(), fp); + break; + case kArchParentFramePointer: + if (frame_access_state()->has_frame()) { + __ Ld(i.OutputRegister(), MemOperand(fp, 0)); + } else { + __ Move(i.OutputRegister(), fp); + } + break; + case kArchTruncateDoubleToI: + __ TruncateDoubleToI(isolate(), zone(), i.OutputRegister(), + i.InputDoubleRegister(0), DetermineStubCallMode()); + break; + case kArchStoreWithWriteBarrier: { + 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()); + __ Add64(kScratchReg, object, index); + __ Sd(value, MemOperand(kScratchReg)); + __ 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; + __ Add64(i.OutputRegister(), base_reg, Operand(offset.offset())); + int alignment = i.InputInt32(1); + DCHECK(alignment == 0 || alignment == 4 || alignment == 8 || + alignment == 16); + if (FLAG_debug_code && alignment > 0) { + // Verify that the output_register is properly aligned + __ And(kScratchReg, i.OutputRegister(), + Operand(kSystemPointerSize - 1)); + __ Assert(eq, AbortReason::kAllocationIsNotDoubleAligned, kScratchReg, + Operand(zero_reg)); + } + if (alignment == 2 * kSystemPointerSize) { + Label done; + __ Add64(kScratchReg, base_reg, Operand(offset.offset())); + __ And(kScratchReg, kScratchReg, Operand(alignment - 1)); + __ BranchShort(&done, eq, kScratchReg, Operand(zero_reg)); + __ Add64(i.OutputRegister(), i.OutputRegister(), kSystemPointerSize); + __ bind(&done); + } else if (alignment > 2 * kSystemPointerSize) { + Label done; + __ Add64(kScratchReg, base_reg, Operand(offset.offset())); + __ And(kScratchReg, kScratchReg, Operand(alignment - 1)); + __ BranchShort(&done, eq, kScratchReg, Operand(zero_reg)); + __ li(kScratchReg2, alignment); + __ Sub64(kScratchReg2, kScratchReg2, Operand(kScratchReg)); + __ Add64(i.OutputRegister(), i.OutputRegister(), kScratchReg2); + __ bind(&done); + } + + break; + } + case kArchWordPoisonOnSpeculation: + __ And(i.OutputRegister(), i.InputRegister(0), + kSpeculationPoisonRegister); + 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 kRiscvAdd32: + __ Add32(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); + break; + case kRiscvAdd64: + __ Add64(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); + break; + case kRiscvAddOvf64: + __ AddOverflow64(i.OutputRegister(), i.InputRegister(0), + i.InputOperand(1), kScratchReg); + break; + case kRiscvSub32: + __ Sub32(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); + break; + case kRiscvSub64: + __ Sub64(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); + break; + case kRiscvSubOvf64: + __ SubOverflow64(i.OutputRegister(), i.InputRegister(0), + i.InputOperand(1), kScratchReg); + break; + case kRiscvMul32: + __ Mul32(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); + break; + case kRiscvMulOvf32: + __ MulOverflow32(i.OutputRegister(), i.InputRegister(0), + i.InputOperand(1), kScratchReg); + break; + case kRiscvMulHigh32: + __ Mulh32(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); + break; + case kRiscvMulHighU32: + __ Mulhu32(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1), + kScratchReg, kScratchReg2); + break; + case kRiscvMulHigh64: + __ Mulh64(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); + break; + case kRiscvDiv32: { + __ Div32(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); + // Set ouput to zero if divisor == 0 + __ LoadZeroIfConditionZero(i.OutputRegister(), i.InputRegister(1)); + break; + } + case kRiscvDivU32: { + __ Divu32(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); + // Set ouput to zero if divisor == 0 + __ LoadZeroIfConditionZero(i.OutputRegister(), i.InputRegister(1)); + break; + } + case kRiscvMod32: + __ Mod32(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); + break; + case kRiscvModU32: + __ Modu32(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); + break; + case kRiscvMul64: + __ Mul64(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); + break; + case kRiscvDiv64: { + __ Div64(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); + // Set ouput to zero if divisor == 0 + __ LoadZeroIfConditionZero(i.OutputRegister(), i.InputRegister(1)); + break; + } + case kRiscvDivU64: { + __ Divu64(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); + // Set ouput to zero if divisor == 0 + __ LoadZeroIfConditionZero(i.OutputRegister(), i.InputRegister(1)); + break; + } + case kRiscvMod64: + __ Mod64(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); + break; + case kRiscvModU64: + __ Modu64(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); + break; + case kRiscvAnd: + __ And(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); + break; + case kRiscvAnd32: + __ And(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); + __ Sll32(i.OutputRegister(), i.OutputRegister(), 0x0); + break; + case kRiscvOr: + __ Or(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); + break; + case kRiscvOr32: + __ Or(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); + __ Sll32(i.OutputRegister(), i.OutputRegister(), 0x0); + break; + case kRiscvNor: + if (instr->InputAt(1)->IsRegister()) { + __ Nor(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); + } else { + DCHECK_EQ(0, i.InputOperand(1).immediate()); + __ Nor(i.OutputRegister(), i.InputRegister(0), zero_reg); + } + break; + case kRiscvNor32: + if (instr->InputAt(1)->IsRegister()) { + __ Nor(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); + __ Sll32(i.OutputRegister(), i.OutputRegister(), 0x0); + } else { + DCHECK_EQ(0, i.InputOperand(1).immediate()); + __ Nor(i.OutputRegister(), i.InputRegister(0), zero_reg); + __ Sll32(i.OutputRegister(), i.OutputRegister(), 0x0); + } + break; + case kRiscvXor: + __ Xor(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); + break; + case kRiscvXor32: + __ Xor(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); + __ Sll32(i.OutputRegister(), i.OutputRegister(), 0x0); + break; + case kRiscvClz32: + __ Clz32(i.OutputRegister(), i.InputRegister(0)); + break; + case kRiscvClz64: + __ Clz64(i.OutputRegister(), i.InputRegister(0)); + break; + case kRiscvCtz32: { + Register src = i.InputRegister(0); + Register dst = i.OutputRegister(); + __ Ctz32(dst, src); + } break; + case kRiscvCtz64: { + Register src = i.InputRegister(0); + Register dst = i.OutputRegister(); + __ Ctz64(dst, src); + } break; + case kRiscvPopcnt32: { + Register src = i.InputRegister(0); + Register dst = i.OutputRegister(); + __ Popcnt32(dst, src); + } break; + case kRiscvPopcnt64: { + Register src = i.InputRegister(0); + Register dst = i.OutputRegister(); + __ Popcnt64(dst, src); + } break; + case kRiscvShl32: + if (instr->InputAt(1)->IsRegister()) { + __ Sll32(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1)); + } else { + int64_t imm = i.InputOperand(1).immediate(); + __ Sll32(i.OutputRegister(), i.InputRegister(0), + static_cast(imm)); + } + break; + case kRiscvShr32: + if (instr->InputAt(1)->IsRegister()) { + __ Srl32(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1)); + } else { + int64_t imm = i.InputOperand(1).immediate(); + __ Srl32(i.OutputRegister(), i.InputRegister(0), + static_cast(imm)); + } + break; + case kRiscvSar32: + if (instr->InputAt(1)->IsRegister()) { + __ Sra32(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1)); + } else { + int64_t imm = i.InputOperand(1).immediate(); + __ Sra32(i.OutputRegister(), i.InputRegister(0), + static_cast(imm)); + } + break; + case kRiscvZeroExtendWord: { + __ ZeroExtendWord(i.OutputRegister(), i.InputRegister(0)); + break; + } + case kRiscvSignExtendWord: { + __ SignExtendWord(i.OutputRegister(), i.InputRegister(0)); + break; + } + case kRiscvShl64: + __ Sll64(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); + break; + case kRiscvShr64: + __ Srl64(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); + break; + case kRiscvSar64: + __ Sra64(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); + break; + case kRiscvRor32: + __ Ror(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); + break; + case kRiscvRor64: + __ Dror(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); + break; + case kRiscvTst: + __ And(kScratchReg, i.InputRegister(0), i.InputOperand(1)); + // Pseudo-instruction used for cmp/branch. No opcode emitted here. + break; + case kRiscvCmp: + // Pseudo-instruction used for cmp/branch. No opcode emitted here. + break; + case kRiscvMov: + // TODO(plind): Should we combine mov/li like this, or use separate instr? + // - Also see x64 ASSEMBLE_BINOP & RegisterOrOperandType + if (HasRegisterInput(instr, 0)) { + __ Move(i.OutputRegister(), i.InputRegister(0)); + } else { + __ li(i.OutputRegister(), i.InputOperand(0)); + } + break; + + case kRiscvCmpS: { + FPURegister left = i.InputOrZeroSingleRegister(0); + FPURegister right = i.InputOrZeroSingleRegister(1); + bool predicate; + FPUCondition cc = + FlagsConditionToConditionCmpFPU(&predicate, instr->flags_condition()); + + if ((left == kDoubleRegZero || right == kDoubleRegZero) && + !__ IsSingleZeroRegSet()) { + __ LoadFPRImmediate(kDoubleRegZero, 0.0f); + } + // compare result set to kScratchReg + __ CompareF32(kScratchReg, cc, left, right); + } break; + case kRiscvAddS: + // TODO(plind): add special case: combine mult & add. + __ fadd_s(i.OutputDoubleRegister(), i.InputDoubleRegister(0), + i.InputDoubleRegister(1)); + break; + case kRiscvSubS: + __ fsub_s(i.OutputDoubleRegister(), i.InputDoubleRegister(0), + i.InputDoubleRegister(1)); + break; + case kRiscvMulS: + // TODO(plind): add special case: right op is -1.0, see arm port. + __ fmul_s(i.OutputDoubleRegister(), i.InputDoubleRegister(0), + i.InputDoubleRegister(1)); + break; + case kRiscvDivS: + __ fdiv_s(i.OutputDoubleRegister(), i.InputDoubleRegister(0), + i.InputDoubleRegister(1)); + break; + case kRiscvModS: { + // 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 kRiscvAbsS: + __ fabs_s(i.OutputSingleRegister(), i.InputSingleRegister(0)); + break; + case kRiscvNegS: + __ Neg_s(i.OutputSingleRegister(), i.InputSingleRegister(0)); + break; + case kRiscvSqrtS: { + __ fsqrt_s(i.OutputDoubleRegister(), i.InputDoubleRegister(0)); + break; + } + case kRiscvMaxS: + __ fmax_s(i.OutputDoubleRegister(), i.InputDoubleRegister(0), + i.InputDoubleRegister(1)); + break; + case kRiscvMinS: + __ fmin_s(i.OutputDoubleRegister(), i.InputDoubleRegister(0), + i.InputDoubleRegister(1)); + break; + case kRiscvCmpD: { + FPURegister left = i.InputOrZeroDoubleRegister(0); + FPURegister right = i.InputOrZeroDoubleRegister(1); + bool predicate; + FPUCondition cc = + FlagsConditionToConditionCmpFPU(&predicate, instr->flags_condition()); + if ((left == kDoubleRegZero || right == kDoubleRegZero) && + !__ IsDoubleZeroRegSet()) { + __ LoadFPRImmediate(kDoubleRegZero, 0.0); + } + // compare result set to kScratchReg + __ CompareF64(kScratchReg, cc, left, right); + } break; + case kRiscvAddD: + // TODO(plind): add special case: combine mult & add. + __ fadd_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0), + i.InputDoubleRegister(1)); + break; + case kRiscvSubD: + __ fsub_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0), + i.InputDoubleRegister(1)); + break; + case kRiscvMulD: + // TODO(plind): add special case: right op is -1.0, see arm port. + __ fmul_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0), + i.InputDoubleRegister(1)); + break; + case kRiscvDivD: + __ fdiv_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0), + i.InputDoubleRegister(1)); + break; + case kRiscvModD: { + // 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 kRiscvAbsD: + __ fabs_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0)); + break; + case kRiscvNegD: + __ Neg_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0)); + break; + case kRiscvSqrtD: { + __ fsqrt_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0)); + break; + } + case kRiscvMaxD: + __ fmax_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0), + i.InputDoubleRegister(1)); + break; + case kRiscvMinD: + __ fmin_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0), + i.InputDoubleRegister(1)); + break; + case kRiscvFloat64RoundDown: { + __ Floor_d_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0), + kScratchDoubleReg); + break; + } + case kRiscvFloat32RoundDown: { + __ Floor_s_s(i.OutputSingleRegister(), i.InputSingleRegister(0), + kScratchDoubleReg); + break; + } + case kRiscvFloat64RoundTruncate: { + __ Trunc_d_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0), + kScratchDoubleReg); + break; + } + case kRiscvFloat32RoundTruncate: { + __ Trunc_s_s(i.OutputSingleRegister(), i.InputSingleRegister(0), + kScratchDoubleReg); + break; + } + case kRiscvFloat64RoundUp: { + __ Ceil_d_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0), + kScratchDoubleReg); + break; + } + case kRiscvFloat32RoundUp: { + __ Ceil_s_s(i.OutputSingleRegister(), i.InputSingleRegister(0), + kScratchDoubleReg); + break; + } + case kRiscvFloat64RoundTiesEven: { + __ Round_d_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0), + kScratchDoubleReg); + break; + } + case kRiscvFloat32RoundTiesEven: { + __ Round_s_s(i.OutputSingleRegister(), i.InputSingleRegister(0), + kScratchDoubleReg); + break; + } + case kRiscvFloat32Max: { + __ Float32Max(i.OutputSingleRegister(), i.InputSingleRegister(0), + i.InputSingleRegister(1)); + break; + } + case kRiscvFloat64Max: { + __ Float64Max(i.OutputSingleRegister(), i.InputSingleRegister(0), + i.InputSingleRegister(1)); + break; + } + case kRiscvFloat32Min: { + __ Float32Min(i.OutputSingleRegister(), i.InputSingleRegister(0), + i.InputSingleRegister(1)); + break; + } + case kRiscvFloat64Min: { + __ Float64Min(i.OutputSingleRegister(), i.InputSingleRegister(0), + i.InputSingleRegister(1)); + break; + } + case kRiscvFloat64SilenceNaN: + __ FPUCanonicalizeNaN(i.OutputDoubleRegister(), i.InputDoubleRegister(0)); + break; + case kRiscvCvtSD: + __ fcvt_s_d(i.OutputSingleRegister(), i.InputDoubleRegister(0)); + break; + case kRiscvCvtDS: + __ fcvt_d_s(i.OutputDoubleRegister(), i.InputSingleRegister(0)); + break; + case kRiscvCvtDW: { + __ fcvt_d_w(i.OutputDoubleRegister(), i.InputRegister(0)); + break; + } + case kRiscvCvtSW: { + __ fcvt_s_w(i.OutputDoubleRegister(), i.InputRegister(0)); + break; + } + case kRiscvCvtSUw: { + __ Cvt_s_uw(i.OutputDoubleRegister(), i.InputRegister(0)); + break; + } + case kRiscvCvtSL: { + __ fcvt_s_l(i.OutputDoubleRegister(), i.InputRegister(0)); + break; + } + case kRiscvCvtDL: { + __ fcvt_d_l(i.OutputDoubleRegister(), i.InputRegister(0)); + break; + } + case kRiscvCvtDUw: { + __ Cvt_d_uw(i.OutputDoubleRegister(), i.InputRegister(0)); + break; + } + case kRiscvCvtDUl: { + __ Cvt_d_ul(i.OutputDoubleRegister(), i.InputRegister(0)); + break; + } + case kRiscvCvtSUl: { + __ Cvt_s_ul(i.OutputDoubleRegister(), i.InputRegister(0)); + break; + } + case kRiscvFloorWD: { + Register result = instr->OutputCount() > 1 ? i.OutputRegister(1) : no_reg; + __ Floor_w_d(i.OutputRegister(), i.InputDoubleRegister(0), result); + break; + } + case kRiscvCeilWD: { + Register result = instr->OutputCount() > 1 ? i.OutputRegister(1) : no_reg; + __ Ceil_w_d(i.OutputRegister(), i.InputDoubleRegister(0), result); + break; + } + case kRiscvRoundWD: { + Register result = instr->OutputCount() > 1 ? i.OutputRegister(1) : no_reg; + __ Round_w_d(i.OutputRegister(), i.InputDoubleRegister(0), result); + break; + } + case kRiscvTruncWD: { + Register result = instr->OutputCount() > 1 ? i.OutputRegister(1) : no_reg; + __ Trunc_w_d(i.OutputRegister(), i.InputDoubleRegister(0), result); + break; + } + case kRiscvFloorWS: { + Register result = instr->OutputCount() > 1 ? i.OutputRegister(1) : no_reg; + __ Floor_w_s(i.OutputRegister(), i.InputDoubleRegister(0), result); + break; + } + case kRiscvCeilWS: { + Register result = instr->OutputCount() > 1 ? i.OutputRegister(1) : no_reg; + __ Ceil_w_s(i.OutputRegister(), i.InputDoubleRegister(0), result); + break; + } + case kRiscvRoundWS: { + Register result = instr->OutputCount() > 1 ? i.OutputRegister(1) : no_reg; + __ Round_w_s(i.OutputRegister(), i.InputDoubleRegister(0), result); + break; + } + case kRiscvTruncWS: { + Label done; + Register result = instr->OutputCount() > 1 ? i.OutputRegister(1) : no_reg; + __ Trunc_w_s(i.OutputRegister(), i.InputDoubleRegister(0), result); + + // On RISCV, if the input value exceeds INT32_MAX, the result of fcvt + // is INT32_MAX. Note that, since INT32_MAX means the lower 31-bits are + // all 1s, INT32_MAX cannot be represented precisely as a float, so an + // fcvt result of INT32_MAX always indicate overflow. + // + // In wasm_compiler, to detect overflow in converting a FP value, fval, to + // integer, V8 checks whether I2F(F2I(fval)) equals fval. However, if fval + // == INT32_MAX+1, the value of I2F(F2I(fval)) happens to be fval. So, + // INT32_MAX is not a good value to indicate overflow. Instead, we will + // use INT32_MIN as the converted result of an out-of-range FP value, + // exploiting the fact that INT32_MAX+1 is INT32_MIN. + // + // If the result of conversion overflow, the result will be set to + // INT32_MIN. Here we detect overflow by testing whether output + 1 < + // output (i.e., kScratchReg < output) + __ Add32(kScratchReg, i.OutputRegister(), 1); + __ Branch(&done, lt, i.OutputRegister(), Operand(kScratchReg)); + __ Move(i.OutputRegister(), kScratchReg); + __ bind(&done); + break; + } + case kRiscvTruncLS: { + Register result = instr->OutputCount() > 1 ? i.OutputRegister(1) : no_reg; + __ Trunc_l_s(i.OutputRegister(), i.InputDoubleRegister(0), result); + break; + } + case kRiscvTruncLD: { + Register result = instr->OutputCount() > 1 ? i.OutputRegister(1) : no_reg; + __ Trunc_l_d(i.OutputRegister(), i.InputDoubleRegister(0), result); + break; + } + case kRiscvTruncUwD: { + Register result = instr->OutputCount() > 1 ? i.OutputRegister(1) : no_reg; + __ Trunc_uw_d(i.OutputRegister(), i.InputDoubleRegister(0), result); + break; + } + case kRiscvTruncUwS: { + Register result = instr->OutputCount() > 1 ? i.OutputRegister(1) : no_reg; + __ Trunc_uw_s(i.OutputRegister(), i.InputDoubleRegister(0), result); + + // On RISCV, if the input value exceeds UINT32_MAX, the result of fcvt + // is UINT32_MAX. Note that, since UINT32_MAX means all 32-bits are 1s, + // UINT32_MAX cannot be represented precisely as float, so an fcvt result + // of UINT32_MAX always indicates overflow. + // + // In wasm_compiler.cc, to detect overflow in converting a FP value, fval, + // to integer, V8 checks whether I2F(F2I(fval)) equals fval. However, if + // fval == UINT32_MAX+1, the value of I2F(F2I(fval)) happens to be fval. + // So, UINT32_MAX is not a good value to indicate overflow. Instead, we + // will use 0 as the converted result of an out-of-range FP value, + // exploiting the fact that UINT32_MAX+1 is 0. + __ Add32(kScratchReg, i.OutputRegister(), 1); + // Set ouput to zero if result overflows (i.e., UINT32_MAX) + __ LoadZeroIfConditionZero(i.OutputRegister(), kScratchReg); + break; + } + case kRiscvTruncUlS: { + Register result = instr->OutputCount() > 1 ? i.OutputRegister(1) : no_reg; + __ Trunc_ul_s(i.OutputRegister(), i.InputDoubleRegister(0), result); + break; + } + case kRiscvTruncUlD: { + Register result = instr->OutputCount() > 1 ? i.OutputRegister(1) : no_reg; + __ Trunc_ul_d(i.OutputRegister(0), i.InputDoubleRegister(0), result); + break; + } + case kRiscvBitcastDL: + __ fmv_x_d(i.OutputRegister(), i.InputDoubleRegister(0)); + break; + case kRiscvBitcastLD: + __ fmv_d_x(i.OutputDoubleRegister(), i.InputRegister(0)); + break; + case kRiscvBitcastInt32ToFloat32: + __ fmv_w_x(i.OutputDoubleRegister(), i.InputRegister(0)); + break; + case kRiscvBitcastFloat32ToInt32: + __ fmv_x_w(i.OutputRegister(), i.InputDoubleRegister(0)); + break; + case kRiscvFloat64ExtractLowWord32: + __ ExtractLowWordFromF64(i.OutputRegister(), i.InputDoubleRegister(0)); + break; + case kRiscvFloat64ExtractHighWord32: + __ ExtractHighWordFromF64(i.OutputRegister(), i.InputDoubleRegister(0)); + break; + case kRiscvFloat64InsertLowWord32: + __ InsertLowWordF64(i.OutputDoubleRegister(), i.InputRegister(1)); + break; + case kRiscvFloat64InsertHighWord32: + __ InsertHighWordF64(i.OutputDoubleRegister(), i.InputRegister(1)); + break; + // ... more basic instructions ... + + case kRiscvSignExtendByte: + __ SignExtendByte(i.OutputRegister(), i.InputRegister(0)); + break; + case kRiscvSignExtendShort: + __ SignExtendShort(i.OutputRegister(), i.InputRegister(0)); + break; + case kRiscvLbu: + __ Lbu(i.OutputRegister(), i.MemoryOperand()); + EmitWordLoadPoisoningIfNeeded(this, opcode, instr, i); + break; + case kRiscvLb: + __ Lb(i.OutputRegister(), i.MemoryOperand()); + EmitWordLoadPoisoningIfNeeded(this, opcode, instr, i); + break; + case kRiscvSb: + __ Sb(i.InputOrZeroRegister(2), i.MemoryOperand()); + break; + case kRiscvLhu: + __ Lhu(i.OutputRegister(), i.MemoryOperand()); + EmitWordLoadPoisoningIfNeeded(this, opcode, instr, i); + break; + case kRiscvUlhu: + __ Ulhu(i.OutputRegister(), i.MemoryOperand()); + EmitWordLoadPoisoningIfNeeded(this, opcode, instr, i); + break; + case kRiscvLh: + __ Lh(i.OutputRegister(), i.MemoryOperand()); + EmitWordLoadPoisoningIfNeeded(this, opcode, instr, i); + break; + case kRiscvUlh: + __ Ulh(i.OutputRegister(), i.MemoryOperand()); + EmitWordLoadPoisoningIfNeeded(this, opcode, instr, i); + break; + case kRiscvSh: + __ Sh(i.InputOrZeroRegister(2), i.MemoryOperand()); + break; + case kRiscvUsh: + __ Ush(i.InputOrZeroRegister(2), i.MemoryOperand()); + break; + case kRiscvLw: + __ Lw(i.OutputRegister(), i.MemoryOperand()); + EmitWordLoadPoisoningIfNeeded(this, opcode, instr, i); + break; + case kRiscvUlw: + __ Ulw(i.OutputRegister(), i.MemoryOperand()); + EmitWordLoadPoisoningIfNeeded(this, opcode, instr, i); + break; + case kRiscvLwu: + __ Lwu(i.OutputRegister(), i.MemoryOperand()); + EmitWordLoadPoisoningIfNeeded(this, opcode, instr, i); + break; + case kRiscvUlwu: + __ Ulwu(i.OutputRegister(), i.MemoryOperand()); + EmitWordLoadPoisoningIfNeeded(this, opcode, instr, i); + break; + case kRiscvLd: + __ Ld(i.OutputRegister(), i.MemoryOperand()); + EmitWordLoadPoisoningIfNeeded(this, opcode, instr, i); + break; + case kRiscvUld: + __ Uld(i.OutputRegister(), i.MemoryOperand()); + EmitWordLoadPoisoningIfNeeded(this, opcode, instr, i); + break; + case kRiscvSw: + __ Sw(i.InputOrZeroRegister(2), i.MemoryOperand()); + break; + case kRiscvUsw: + __ Usw(i.InputOrZeroRegister(2), i.MemoryOperand()); + break; + case kRiscvSd: + __ Sd(i.InputOrZeroRegister(2), i.MemoryOperand()); + break; + case kRiscvUsd: + __ Usd(i.InputOrZeroRegister(2), i.MemoryOperand()); + break; + case kRiscvLoadFloat: { + __ LoadFloat(i.OutputSingleRegister(), i.MemoryOperand()); + break; + } + case kRiscvULoadFloat: { + __ ULoadFloat(i.OutputSingleRegister(), i.MemoryOperand(), kScratchReg); + break; + } + case kRiscvStoreFloat: { + size_t index = 0; + MemOperand operand = i.MemoryOperand(&index); + FPURegister ft = i.InputOrZeroSingleRegister(index); + if (ft == kDoubleRegZero && !__ IsSingleZeroRegSet()) { + __ LoadFPRImmediate(kDoubleRegZero, 0.0f); + } + __ StoreFloat(ft, operand); + break; + } + case kRiscvUStoreFloat: { + size_t index = 0; + MemOperand operand = i.MemoryOperand(&index); + FPURegister ft = i.InputOrZeroSingleRegister(index); + if (ft == kDoubleRegZero && !__ IsSingleZeroRegSet()) { + __ LoadFPRImmediate(kDoubleRegZero, 0.0f); + } + __ UStoreFloat(ft, operand, kScratchReg); + break; + } + case kRiscvLoadDouble: + __ LoadDouble(i.OutputDoubleRegister(), i.MemoryOperand()); + break; + case kRiscvULoadDouble: + __ ULoadDouble(i.OutputDoubleRegister(), i.MemoryOperand(), kScratchReg); + break; + case kRiscvStoreDouble: { + FPURegister ft = i.InputOrZeroDoubleRegister(2); + if (ft == kDoubleRegZero && !__ IsDoubleZeroRegSet()) { + __ LoadFPRImmediate(kDoubleRegZero, 0.0); + } + __ StoreDouble(ft, i.MemoryOperand()); + break; + } + case kRiscvUStoreDouble: { + FPURegister ft = i.InputOrZeroDoubleRegister(2); + if (ft == kDoubleRegZero && !__ IsDoubleZeroRegSet()) { + __ LoadFPRImmediate(kDoubleRegZero, 0.0); + } + __ UStoreDouble(ft, i.MemoryOperand(), kScratchReg); + break; + } + case kRiscvSync: { + __ sync(); + break; + } + case kRiscvPush: + if (instr->InputAt(0)->IsFPRegister()) { + __ StoreDouble(i.InputDoubleRegister(0), MemOperand(sp, -kDoubleSize)); + __ Sub32(sp, sp, Operand(kDoubleSize)); + frame_access_state()->IncreaseSPDelta(kDoubleSize / kSystemPointerSize); + } else { + __ Push(i.InputRegister(0)); + frame_access_state()->IncreaseSPDelta(1); + } + break; + case kRiscvPeek: { + // The incoming value is 0-based, but we need a 1-based value. + int reverse_slot = i.InputInt32(0) + 1; + int offset = + FrameSlotToFPOffset(frame()->GetTotalFrameSlotCount() - reverse_slot); + if (instr->OutputAt(0)->IsFPRegister()) { + LocationOperand* op = LocationOperand::cast(instr->OutputAt(0)); + if (op->representation() == MachineRepresentation::kFloat64) { + __ LoadDouble(i.OutputDoubleRegister(), MemOperand(fp, offset)); + } else { + DCHECK_EQ(op->representation(), MachineRepresentation::kFloat32); + __ LoadFloat( + i.OutputSingleRegister(0), + MemOperand(fp, offset + kLessSignificantWordInDoublewordOffset)); + } + } else { + __ Ld(i.OutputRegister(0), MemOperand(fp, offset)); + } + break; + } + case kRiscvStackClaim: { + __ Sub64(sp, sp, Operand(i.InputInt32(0))); + frame_access_state()->IncreaseSPDelta(i.InputInt32(0) / + kSystemPointerSize); + break; + } + case kRiscvStoreToStackSlot: { + if (instr->InputAt(0)->IsFPRegister()) { + if (instr->InputAt(0)->IsSimd128Register()) { + UNREACHABLE(); + } else { + __ StoreDouble(i.InputDoubleRegister(0), + MemOperand(sp, i.InputInt32(1))); + } + } else { + __ Sd(i.InputRegister(0), MemOperand(sp, i.InputInt32(1))); + } + break; + } + case kRiscvByteSwap64: { + __ ByteSwap(i.OutputRegister(0), i.InputRegister(0), 8); + break; + } + case kRiscvByteSwap32: { + __ ByteSwap(i.OutputRegister(0), i.InputRegister(0), 4); + break; + } + case kWord32AtomicLoadInt8: + ASSEMBLE_ATOMIC_LOAD_INTEGER(Lb); + break; + case kWord32AtomicLoadUint8: + ASSEMBLE_ATOMIC_LOAD_INTEGER(Lbu); + break; + case kWord32AtomicLoadInt16: + ASSEMBLE_ATOMIC_LOAD_INTEGER(Lh); + break; + case kWord32AtomicLoadUint16: + ASSEMBLE_ATOMIC_LOAD_INTEGER(Lhu); + break; + case kWord32AtomicLoadWord32: + ASSEMBLE_ATOMIC_LOAD_INTEGER(Lw); + break; + case kRiscvWord64AtomicLoadUint8: + ASSEMBLE_ATOMIC_LOAD_INTEGER(Lbu); + break; + case kRiscvWord64AtomicLoadUint16: + ASSEMBLE_ATOMIC_LOAD_INTEGER(Lhu); + break; + case kRiscvWord64AtomicLoadUint32: + ASSEMBLE_ATOMIC_LOAD_INTEGER(Lwu); + break; + case kRiscvWord64AtomicLoadUint64: + ASSEMBLE_ATOMIC_LOAD_INTEGER(Ld); + break; + case kWord32AtomicStoreWord8: + ASSEMBLE_ATOMIC_STORE_INTEGER(Sb); + break; + case kWord32AtomicStoreWord16: + ASSEMBLE_ATOMIC_STORE_INTEGER(Sh); + break; + case kWord32AtomicStoreWord32: + ASSEMBLE_ATOMIC_STORE_INTEGER(Sw); + break; + case kRiscvWord64AtomicStoreWord8: + ASSEMBLE_ATOMIC_STORE_INTEGER(Sb); + break; + case kRiscvWord64AtomicStoreWord16: + ASSEMBLE_ATOMIC_STORE_INTEGER(Sh); + break; + case kRiscvWord64AtomicStoreWord32: + ASSEMBLE_ATOMIC_STORE_INTEGER(Sw); + break; + case kRiscvWord64AtomicStoreWord64: + ASSEMBLE_ATOMIC_STORE_INTEGER(Sd); + break; + case kWord32AtomicExchangeInt8: + ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(Ll, Sc, true, 8, 32); + break; + case kWord32AtomicExchangeUint8: + ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(Ll, Sc, false, 8, 32); + break; + case kWord32AtomicExchangeInt16: + ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(Ll, Sc, true, 16, 32); + break; + case kWord32AtomicExchangeUint16: + ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(Ll, Sc, false, 16, 32); + break; + case kWord32AtomicExchangeWord32: + ASSEMBLE_ATOMIC_EXCHANGE_INTEGER(Ll, Sc); + break; + case kRiscvWord64AtomicExchangeUint8: + ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(Lld, Scd, false, 8, 64); + break; + case kRiscvWord64AtomicExchangeUint16: + ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(Lld, Scd, false, 16, 64); + break; + case kRiscvWord64AtomicExchangeUint32: + ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(Lld, Scd, false, 32, 64); + break; + case kRiscvWord64AtomicExchangeUint64: + ASSEMBLE_ATOMIC_EXCHANGE_INTEGER(Lld, Scd); + break; + case kWord32AtomicCompareExchangeInt8: + ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT(Ll, Sc, true, 8, 32); + break; + case kWord32AtomicCompareExchangeUint8: + ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT(Ll, Sc, false, 8, 32); + break; + case kWord32AtomicCompareExchangeInt16: + ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT(Ll, Sc, true, 16, 32); + break; + case kWord32AtomicCompareExchangeUint16: + ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT(Ll, Sc, false, 16, 32); + break; + case kWord32AtomicCompareExchangeWord32: + __ Sll32(i.InputRegister(2), i.InputRegister(2), 0); + ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER(Ll, Sc); + break; + case kRiscvWord64AtomicCompareExchangeUint8: + ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT(Lld, Scd, false, 8, 64); + break; + case kRiscvWord64AtomicCompareExchangeUint16: + ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT(Lld, Scd, false, 16, 64); + break; + case kRiscvWord64AtomicCompareExchangeUint32: + ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT(Lld, Scd, false, 32, 64); + break; + case kRiscvWord64AtomicCompareExchangeUint64: + ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER(Lld, Scd); + break; +#define ATOMIC_BINOP_CASE(op, inst) \ + case kWord32Atomic##op##Int8: \ + ASSEMBLE_ATOMIC_BINOP_EXT(Ll, Sc, true, 8, inst, 32); \ + break; \ + case kWord32Atomic##op##Uint8: \ + ASSEMBLE_ATOMIC_BINOP_EXT(Ll, Sc, false, 8, inst, 32); \ + break; \ + case kWord32Atomic##op##Int16: \ + ASSEMBLE_ATOMIC_BINOP_EXT(Ll, Sc, true, 16, inst, 32); \ + break; \ + case kWord32Atomic##op##Uint16: \ + ASSEMBLE_ATOMIC_BINOP_EXT(Ll, Sc, false, 16, inst, 32); \ + break; \ + case kWord32Atomic##op##Word32: \ + ASSEMBLE_ATOMIC_BINOP(Ll, Sc, inst); \ + break; + ATOMIC_BINOP_CASE(Add, Add32) + ATOMIC_BINOP_CASE(Sub, Sub32) + ATOMIC_BINOP_CASE(And, And) + ATOMIC_BINOP_CASE(Or, Or) + ATOMIC_BINOP_CASE(Xor, Xor) +#undef ATOMIC_BINOP_CASE +#define ATOMIC_BINOP_CASE(op, inst) \ + case kRiscvWord64Atomic##op##Uint8: \ + ASSEMBLE_ATOMIC_BINOP_EXT(Lld, Scd, false, 8, inst, 64); \ + break; \ + case kRiscvWord64Atomic##op##Uint16: \ + ASSEMBLE_ATOMIC_BINOP_EXT(Lld, Scd, false, 16, inst, 64); \ + break; \ + case kRiscvWord64Atomic##op##Uint32: \ + ASSEMBLE_ATOMIC_BINOP_EXT(Lld, Scd, false, 32, inst, 64); \ + break; \ + case kRiscvWord64Atomic##op##Uint64: \ + ASSEMBLE_ATOMIC_BINOP(Lld, Scd, inst); \ + break; + ATOMIC_BINOP_CASE(Add, Add64) + ATOMIC_BINOP_CASE(Sub, Sub64) + ATOMIC_BINOP_CASE(And, And) + ATOMIC_BINOP_CASE(Or, Or) + ATOMIC_BINOP_CASE(Xor, Xor) +#undef ATOMIC_BINOP_CASE + case kRiscvAssertEqual: + __ Assert(eq, static_cast(i.InputOperand(2).immediate()), + i.InputRegister(0), Operand(i.InputRegister(1))); + break; + + default: + UNIMPLEMENTED(); + } + return kSuccess; +} // NOLINT(readability/fn_size) + +#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-> + RiscvOperandConverter i(gen, instr); + + Condition cc = kNoCondition; + // RISC-V does not have condition code flags, so compare and branch are + // implemented differently than on the other arch's. The compare operations + // emit riscv64 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() == kRiscvTst) { + cc = FlagsConditionToConditionTst(condition); + __ Branch(tlabel, cc, kScratchReg, Operand(zero_reg)); + } else if (instr->arch_opcode() == kRiscvAdd64 || + instr->arch_opcode() == kRiscvSub64) { + cc = FlagsConditionToConditionOvf(condition); + __ Sra64(kScratchReg, i.OutputRegister(), 32); + __ Sra64(kScratchReg2, i.OutputRegister(), 31); + __ Branch(tlabel, cc, kScratchReg2, Operand(kScratchReg)); + } else if (instr->arch_opcode() == kRiscvAddOvf64 || + instr->arch_opcode() == kRiscvSubOvf64) { + 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); + break; + } + } else if (instr->arch_opcode() == kRiscvMulOvf32) { + // 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(kRiscvMulOvf32, condition); + break; + } + } else if (instr->arch_opcode() == kRiscvCmp) { + cc = FlagsConditionToConditionCmp(condition); + __ Branch(tlabel, cc, i.InputRegister(0), i.InputOperand(1)); + } else if (instr->arch_opcode() == kArchStackPointerGreaterThan) { + cc = FlagsConditionToConditionCmp(condition); + Register lhs_register = sp; + uint32_t offset; + if (gen->ShouldApplyOffsetToStackCheck(instr, &offset)) { + lhs_register = i.TempRegister(0); + __ Sub64(lhs_register, sp, offset); + } + __ Branch(tlabel, cc, lhs_register, Operand(i.InputRegister(0))); + } else if (instr->arch_opcode() == kRiscvCmpS || + instr->arch_opcode() == kRiscvCmpD) { + bool predicate; + FlagsConditionToConditionCmpFPU(&predicate, condition); + // floating-point compare result is set in kScratchReg + if (predicate) { + __ BranchTrueF(kScratchReg, tlabel); + } else { + __ BranchFalseF(kScratchReg, 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); +} + +void CodeGenerator::AssembleBranchPoisoning(FlagsCondition condition, + Instruction* instr) { + // TODO(jarin) Handle float comparisons (kUnordered[Not]Equal). + if (condition == kUnorderedEqual || condition == kUnorderedNotEqual) { + return; + } + + RiscvOperandConverter i(this, instr); + condition = NegateFlagsCondition(condition); + + switch (instr->arch_opcode()) { + case kRiscvCmp: { + __ CompareI(kScratchReg, i.InputRegister(0), i.InputOperand(1), + FlagsConditionToConditionCmp(condition)); + __ LoadZeroIfConditionNotZero(kSpeculationPoisonRegister, kScratchReg); + } + return; + case kRiscvTst: { + switch (condition) { + case kEqual: + __ LoadZeroIfConditionZero(kSpeculationPoisonRegister, kScratchReg); + break; + case kNotEqual: + __ LoadZeroIfConditionNotZero(kSpeculationPoisonRegister, + kScratchReg); + break; + default: + UNREACHABLE(); + } + } + return; + case kRiscvAdd64: + case kRiscvSub64: { + // Check for overflow creates 1 or 0 for result. + __ Srl64(kScratchReg, i.OutputRegister(), 63); + __ Srl32(kScratchReg2, i.OutputRegister(), 31); + __ Xor(kScratchReg2, kScratchReg, kScratchReg2); + switch (condition) { + case kOverflow: + __ LoadZeroIfConditionNotZero(kSpeculationPoisonRegister, + kScratchReg2); + break; + case kNotOverflow: + __ LoadZeroIfConditionZero(kSpeculationPoisonRegister, kScratchReg2); + break; + default: + UNSUPPORTED_COND(instr->arch_opcode(), condition); + } + } + return; + case kRiscvAddOvf64: + case kRiscvSubOvf64: { + // Overflow occurs if overflow register is negative + __ Slt(kScratchReg2, kScratchReg, zero_reg); + switch (condition) { + case kOverflow: + __ LoadZeroIfConditionNotZero(kSpeculationPoisonRegister, + kScratchReg2); + break; + case kNotOverflow: + __ LoadZeroIfConditionZero(kSpeculationPoisonRegister, kScratchReg2); + break; + default: + UNSUPPORTED_COND(instr->arch_opcode(), condition); + } + } + return; + case kRiscvMulOvf32: { + // Overflow occurs if overflow register is not zero + switch (condition) { + case kOverflow: + __ LoadZeroIfConditionNotZero(kSpeculationPoisonRegister, + kScratchReg); + break; + case kNotOverflow: + __ LoadZeroIfConditionZero(kSpeculationPoisonRegister, kScratchReg); + break; + default: + UNSUPPORTED_COND(instr->arch_opcode(), condition); + } + } + return; + case kRiscvCmpS: + case kRiscvCmpD: { + bool predicate; + FlagsConditionToConditionCmpFPU(&predicate, condition); + if (predicate) { + __ LoadZeroIfConditionNotZero(kSpeculationPoisonRegister, kScratchReg); + } else { + __ LoadZeroIfConditionZero(kSpeculationPoisonRegister, kScratchReg); + } + } + return; + default: + UNREACHABLE(); + } +} + +#undef UNSUPPORTED_COND + +void CodeGenerator::AssembleArchDeoptBranch(Instruction* instr, + BranchInfo* branch) { + AssembleArchBranch(instr, branch); +} + +void CodeGenerator::AssembleArchJump(RpoNumber target) { + if (!IsNextInAssemblyOrder(target)) __ Branch(GetLabel(target)); +} + +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 { + RiscvOperandConverter 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->StackParameterCount()); + 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, Safepoint::kNoLazyDeopt); + if (FLAG_debug_code) { + __ stop(); + } + } + } + Instruction* instr_; + CodeGenerator* gen_; + }; + auto ool = zone()->New(this, instr); + Label* tlabel = ool->entry(); + AssembleBranchToLabels(this, tasm(), instr, condition, tlabel, nullptr, true); +} + +// Assembles boolean materializations after an instruction. +void CodeGenerator::AssembleArchBoolean(Instruction* instr, + FlagsCondition condition) { + RiscvOperandConverter 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; + // RISC-V does not have condition code flags, so compare and branch are + // implemented differently than on the other arch's. The compare operations + // emit riscv64 pseudo-instructions, which are checked and handled here. + + if (instr->arch_opcode() == kRiscvTst) { + cc = FlagsConditionToConditionTst(condition); + if (cc == eq) { + __ Sltu(result, kScratchReg, 1); + } else { + __ Sltu(result, zero_reg, kScratchReg); + } + return; + } else if (instr->arch_opcode() == kRiscvAdd64 || + instr->arch_opcode() == kRiscvSub64) { + cc = FlagsConditionToConditionOvf(condition); + // Check for overflow creates 1 or 0 for result. + __ Srl64(kScratchReg, i.OutputRegister(), 63); + __ Srl32(kScratchReg2, i.OutputRegister(), 31); + __ Xor(result, kScratchReg, kScratchReg2); + if (cc == eq) // Toggle result for not overflow. + __ Xor(result, result, 1); + return; + } else if (instr->arch_opcode() == kRiscvAddOvf64 || + instr->arch_opcode() == kRiscvSubOvf64) { + // Overflow occurs if overflow register is negative + __ Slt(result, kScratchReg, zero_reg); + } else if (instr->arch_opcode() == kRiscvMulOvf32) { + // Overflow occurs if overflow register is not zero + __ Sgtu(result, kScratchReg, zero_reg); + } else if (instr->arch_opcode() == kRiscvCmp) { + 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_int12(-right.immediate())) { + if (right.immediate() == 0) { + if (cc == eq) { + __ Sltu(result, left, 1); + } else { + __ Sltu(result, zero_reg, left); + } + } else { + __ Add64(result, left, Operand(-right.immediate())); + if (cc == eq) { + __ Sltu(result, result, 1); + } else { + __ Sltu(result, zero_reg, result); + } + } + } else { + if (is_uint12(right.immediate())) { + __ Xor(result, left, right); + } else { + __ li(kScratchReg, right); + __ Xor(result, left, kScratchReg); + } + if (cc == eq) { + __ Sltu(result, result, 1); + } else { + __ Sltu(result, zero_reg, result); + } + } + } else { + __ Xor(result, left, right); + if (cc == eq) { + __ Sltu(result, result, 1); + } else { + __ Sltu(result, zero_reg, result); + } + } + } break; + case lt: + case ge: { + Register left = i.InputRegister(0); + Operand right = i.InputOperand(1); + __ Slt(result, left, right); + if (cc == ge) { + __ Xor(result, result, 1); + } + } break; + case gt: + case le: { + Register left = i.InputRegister(1); + Operand right = i.InputOperand(0); + __ Slt(result, left, right); + if (cc == le) { + __ Xor(result, result, 1); + } + } break; + case Uless: + case Ugreater_equal: { + Register left = i.InputRegister(0); + Operand right = i.InputOperand(1); + __ Sltu(result, left, right); + if (cc == Ugreater_equal) { + __ Xor(result, result, 1); + } + } break; + case Ugreater: + case Uless_equal: { + Register left = i.InputRegister(1); + Operand right = i.InputOperand(0); + __ Sltu(result, left, right); + if (cc == Uless_equal) { + __ Xor(result, result, 1); + } + } break; + default: + UNREACHABLE(); + } + return; + } else if (instr->arch_opcode() == kRiscvCmpD || + instr->arch_opcode() == kRiscvCmpS) { + FPURegister left = i.InputOrZeroDoubleRegister(0); + FPURegister right = i.InputOrZeroDoubleRegister(1); + if ((instr->arch_opcode() == kRiscvCmpD) && + (left == kDoubleRegZero || right == kDoubleRegZero) && + !__ IsDoubleZeroRegSet()) { + __ LoadFPRImmediate(kDoubleRegZero, 0.0); + } else if ((instr->arch_opcode() == kRiscvCmpS) && + (left == kDoubleRegZero || right == kDoubleRegZero) && + !__ IsSingleZeroRegSet()) { + __ LoadFPRImmediate(kDoubleRegZero, 0.0f); + } + bool predicate; + FlagsConditionToConditionCmpFPU(&predicate, condition); + // RISCV compare returns 0 or 1, do nothing when predicate; otherwise + // toggle kScratchReg (i.e., 0 -> 1, 1 -> 0) + if (predicate) { + __ Move(result, kScratchReg); + } else { + __ Xor(result, kScratchReg, 1); + } + return; + } else { + PrintF("AssembleArchBranch Unimplemented arch_opcode is : %d\n", + instr->arch_opcode()); + TRACE_UNIMPL(); + UNIMPLEMENTED(); + } +} + +void CodeGenerator::AssembleArchBinarySearchSwitch(Instruction* instr) { + RiscvOperandConverter 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) { + RiscvOperandConverter i(this, instr); + Register input = i.InputRegister(0); + size_t const case_count = instr->InputCount() - 2; + + __ Branch(GetLabel(i.InputRpo(1)), Ugreater_equal, input, + Operand(case_count)); + __ GenerateSwitchTable(input, case_count, [&i, this](size_t index) { + return GetLabel(i.InputRpo(index + 2)); + }); +} + +void CodeGenerator::FinishFrame(Frame* frame) { + auto call_descriptor = linkage()->GetIncomingDescriptor(); + + const RegList saves_fpu = call_descriptor->CalleeSavedFPRegisters(); + if (saves_fpu != 0) { + int count = base::bits::CountPopulation(saves_fpu); + DCHECK_EQ(kNumCalleeSavedFPU, count); + frame->AllocateSavedCalleeRegisterSlots(count * + (kDoubleSize / kSystemPointerSize)); + } + + const RegList saves = call_descriptor->CalleeSavedRegisters(); + if (saves != 0) { + int count = base::bits::CountPopulation(saves); + DCHECK_EQ(kNumCalleeSaved, count + 1); + frame->AllocateSavedCalleeRegisterSlots(count); + } +} + +void CodeGenerator::AssembleConstructFrame() { + auto call_descriptor = linkage()->GetIncomingDescriptor(); + + if (frame_access_state()->has_frame()) { + if (call_descriptor->IsCFunctionCall()) { + if (info()->GetOutputStackFrameType() == StackFrame::C_WASM_ENTRY) { + __ StubPrologue(StackFrame::C_WASM_ENTRY); + // Reserve stack space for saving the c_entry_fp later. + __ Sub64(sp, sp, Operand(kSystemPointerSize)); + } else { + __ Push(ra, fp); + __ Move(fp, sp); + } + } else if (call_descriptor->IsJSFunctionCall()) { + __ Prologue(); + } else { + __ StubPrologue(info()->GetOutputStackFrameType()); + if (call_descriptor->IsWasmFunctionCall()) { + __ Push(kWasmInstanceRegister); + } else if (call_descriptor->IsWasmImportWrapper() || + call_descriptor->IsWasmCapiFunction()) { + // Wasm import wrappers are passed a tuple in the place of the instance. + // Unpack the tuple into the instance and the target callable. + // This must be done here in the codegen because it cannot be expressed + // properly in the graph. + __ Ld(kJSFunctionRegister, + FieldMemOperand(kWasmInstanceRegister, Tuple2::kValue2Offset)); + __ Ld(kWasmInstanceRegister, + FieldMemOperand(kWasmInstanceRegister, Tuple2::kValue1Offset)); + __ Push(kWasmInstanceRegister); + if (call_descriptor->IsWasmCapiFunction()) { + // Reserve space for saving the PC later. + __ Sub64(sp, sp, Operand(kSystemPointerSize)); + } + } + } + } + + 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. + if (FLAG_code_comments) __ RecordComment("-- OSR entrypoint --"); + osr_pc_offset_ = __ pc_offset(); + required_slots -= osr_helper()->UnoptimizedFrameSlots(); + ResetSpeculationPoison(); + } + + const RegList saves = call_descriptor->CalleeSavedRegisters(); + const RegList saves_fpu = call_descriptor->CalleeSavedFPRegisters(); + + if (required_slots > 0) { + DCHECK(frame_access_state()->has_frame()); + if (info()->IsWasm() && required_slots > 128) { + // For WebAssembly functions with big frames we have to do the stack + // overflow check before we construct the frame. Otherwise we may not + // have enough space on the stack to call the runtime for the stack + // overflow. + Label done; + + // If the frame is bigger than the stack, we throw the stack overflow + // exception unconditionally. Thereby we can avoid the integer overflow + // check in the condition code. + if ((required_slots * kSystemPointerSize) < (FLAG_stack_size * 1024)) { + __ Ld( + kScratchReg, + FieldMemOperand(kWasmInstanceRegister, + WasmInstanceObject::kRealStackLimitAddressOffset)); + __ Ld(kScratchReg, MemOperand(kScratchReg)); + __ Add64(kScratchReg, kScratchReg, + Operand(required_slots * kSystemPointerSize)); + __ Branch(&done, uge, sp, Operand(kScratchReg)); + } + + __ Call(wasm::WasmCode::kWasmStackOverflow, RelocInfo::WASM_STUB_CALL); + // We come from WebAssembly, there are no references for the GC. + ReferenceMap* reference_map = zone()->New(zone()); + RecordSafepoint(reference_map, Safepoint::kNoLazyDeopt); + if (FLAG_debug_code) { + __ stop(); + } + + __ bind(&done); + } + } + + const int returns = frame()->GetReturnSlotCount(); + + // Skip callee-saved and return slots, which are pushed below. + required_slots -= base::bits::CountPopulation(saves); + required_slots -= base::bits::CountPopulation(saves_fpu); + required_slots -= returns; + if (required_slots > 0) { + __ Sub64(sp, sp, Operand(required_slots * kSystemPointerSize)); + } + + if (saves_fpu != 0) { + // Save callee-saved FPU registers. + __ MultiPushFPU(saves_fpu); + DCHECK_EQ(kNumCalleeSavedFPU, base::bits::CountPopulation(saves_fpu)); + } + + if (saves != 0) { + // Save callee-saved registers. + __ MultiPush(saves); + DCHECK_EQ(kNumCalleeSaved, base::bits::CountPopulation(saves) + 1); + } + + if (returns != 0) { + // Create space for returns. + __ Sub64(sp, sp, Operand(returns * kSystemPointerSize)); + } +} + +void CodeGenerator::AssembleReturn(InstructionOperand* pop) { + auto call_descriptor = linkage()->GetIncomingDescriptor(); + + const int returns = frame()->GetReturnSlotCount(); + if (returns != 0) { + __ Add64(sp, sp, Operand(returns * kSystemPointerSize)); + } + + // Restore GP registers. + const RegList saves = call_descriptor->CalleeSavedRegisters(); + if (saves != 0) { + __ MultiPop(saves); + } + + // Restore FPU registers. + const RegList saves_fpu = call_descriptor->CalleeSavedFPRegisters(); + if (saves_fpu != 0) { + __ MultiPopFPU(saves_fpu); + } + + RiscvOperandConverter g(this, nullptr); + 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 (pop->IsImmediate() && g.ToConstant(pop).ToInt32() == 0) { + if (return_label_.is_bound()) { + __ Branch(&return_label_); + return; + } else { + __ bind(&return_label_); + AssembleDeconstructFrame(); + } + } else { + AssembleDeconstructFrame(); + } + } + int pop_count = static_cast(call_descriptor->StackParameterCount()); + if (pop->IsImmediate()) { + pop_count += g.ToConstant(pop).ToInt32(); + } else { + Register pop_reg = g.ToRegister(pop); + __ Sll64(pop_reg, pop_reg, kSystemPointerSizeLog2); + __ Add64(sp, sp, pop_reg); + } + if (pop_count != 0) { + __ DropAndRet(pop_count); + } else { + __ Ret(); + } +} + +void CodeGenerator::FinishCode() {} + +void CodeGenerator::PrepareForDeoptimizationExits(int deopt_count) {} + +void CodeGenerator::AssembleMove(InstructionOperand* source, + InstructionOperand* destination) { + RiscvOperandConverter 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()) { + __ Move(g.ToRegister(destination), src); + } else { + __ Sd(src, g.ToMemOperand(destination)); + } + } else if (source->IsStackSlot()) { + DCHECK(destination->IsRegister() || destination->IsStackSlot()); + MemOperand src = g.ToMemOperand(source); + if (destination->IsRegister()) { + __ Ld(g.ToRegister(destination), src); + } else { + Register temp = kScratchReg; + __ Ld(temp, src); + __ Sd(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 (RelocInfo::IsWasmReference(src.rmode())) { + __ li(dst, Operand(src.ToInt64(), src.rmode())); + } else { + __ 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 + break; + } + if (destination->IsStackSlot()) __ Sd(dst, g.ToMemOperand(destination)); + } else if (src.type() == Constant::kFloat32) { + if (destination->IsFPStackSlot()) { + MemOperand dst = g.ToMemOperand(destination); + if (bit_cast(src.ToFloat32()) == 0) { + __ Sw(zero_reg, dst); + } else { + __ li(kScratchReg, Operand(bit_cast(src.ToFloat32()))); + __ Sw(kScratchReg, dst); + } + } else { + DCHECK(destination->IsFPRegister()); + FloatRegister dst = g.ToSingleRegister(destination); + __ LoadFPRImmediate(dst, src.ToFloat32()); + } + } else { + DCHECK_EQ(Constant::kFloat64, src.type()); + DoubleRegister dst = destination->IsFPRegister() + ? g.ToDoubleRegister(destination) + : kScratchDoubleReg; + __ LoadFPRImmediate(dst, src.ToFloat64().value()); + if (destination->IsFPStackSlot()) { + __ StoreDouble(dst, g.ToMemOperand(destination)); + } + } + } else if (source->IsFPRegister()) { + MachineRepresentation rep = LocationOperand::cast(source)->representation(); + if (rep == MachineRepresentation::kSimd128) { + UNIMPLEMENTED(); + } else { + FPURegister src = g.ToDoubleRegister(source); + if (destination->IsFPRegister()) { + FPURegister dst = g.ToDoubleRegister(destination); + __ Move(dst, src); + } else { + DCHECK(destination->IsFPStackSlot()); + if (rep == MachineRepresentation::kFloat32) { + __ StoreFloat(src, g.ToMemOperand(destination)); + } else { + DCHECK_EQ(rep, MachineRepresentation::kFloat64); + __ StoreDouble(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) { + UNIMPLEMENTED(); + } else { + if (destination->IsFPRegister()) { + if (rep == MachineRepresentation::kFloat32) { + __ LoadFloat(g.ToDoubleRegister(destination), src); + } else { + DCHECK_EQ(rep, MachineRepresentation::kFloat64); + __ LoadDouble(g.ToDoubleRegister(destination), src); + } + } else { + DCHECK(destination->IsFPStackSlot()); + FPURegister temp = kScratchDoubleReg; + if (rep == MachineRepresentation::kFloat32) { + __ LoadFloat(temp, src); + __ StoreFloat(temp, g.ToMemOperand(destination)); + } else { + DCHECK_EQ(rep, MachineRepresentation::kFloat64); + __ LoadDouble(temp, src); + __ StoreDouble(temp, g.ToMemOperand(destination)); + } + } + } + } else { + UNREACHABLE(); + } +} + +void CodeGenerator::AssembleSwap(InstructionOperand* source, + InstructionOperand* destination) { + RiscvOperandConverter 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); + __ Move(temp, src); + __ Ld(src, dst); + __ Sd(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); + __ Ld(temp_0, src); + __ Ld(temp_1, dst); + __ Sd(temp_0, dst); + __ Sd(temp_1, src); + } else if (source->IsFPRegister()) { + MachineRepresentation rep = LocationOperand::cast(source)->representation(); + if (rep == MachineRepresentation::kSimd128) { + UNIMPLEMENTED(); + } 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); + __ LoadDouble(src, dst); + __ StoreDouble(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) { + UNIMPLEMENTED(); + } else { + FPURegister temp_1 = kScratchDoubleReg; + __ LoadDouble(temp_1, dst0); // Save destination in temp_1. + __ Lw(temp_0, src0); // Then use temp_0 to copy source to destination. + __ Sw(temp_0, dst0); + __ Lw(temp_0, src1); + __ Sw(temp_0, dst1); + __ StoreDouble(temp_1, src0); + } + } else { + // No other combinations are possible. + UNREACHABLE(); + } +} + +void CodeGenerator::AssembleJumpTable(Label** targets, size_t target_count) { + // On 64-bit RISC-V 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 TRACE_MSG +#undef TRACE_UNIMPL +#undef __ + +} // namespace compiler +} // namespace internal +} // namespace v8 Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/compiler/backend/riscv64/instruction-codes-riscv64.h =================================================================== --- /dev/null +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/compiler/backend/riscv64/instruction-codes-riscv64.h @@ -0,0 +1,432 @@ +// 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_RISCV_INSTRUCTION_CODES_RISCV_H_ +#define V8_COMPILER_BACKEND_RISCV_INSTRUCTION_CODES_RISCV_H_ + +namespace v8 { +namespace internal { +namespace compiler { + +// RISC-V-specific opcodes that specify which assembly sequence to emit. +// Most opcodes specify a single instruction. +#define TARGET_ARCH_OPCODE_LIST(V) \ + V(RiscvAdd32) \ + V(RiscvAdd64) \ + V(RiscvAddOvf64) \ + V(RiscvSub32) \ + V(RiscvSub64) \ + V(RiscvSubOvf64) \ + V(RiscvMul32) \ + V(RiscvMulOvf32) \ + V(RiscvMulHigh32) \ + V(RiscvMulHigh64) \ + V(RiscvMulHighU32) \ + V(RiscvMul64) \ + V(RiscvDiv32) \ + V(RiscvDiv64) \ + V(RiscvDivU32) \ + V(RiscvDivU64) \ + V(RiscvMod32) \ + V(RiscvMod64) \ + V(RiscvModU32) \ + V(RiscvModU64) \ + V(RiscvAnd) \ + V(RiscvAnd32) \ + V(RiscvOr) \ + V(RiscvOr32) \ + V(RiscvNor) \ + V(RiscvNor32) \ + V(RiscvXor) \ + V(RiscvXor32) \ + V(RiscvClz32) \ + V(RiscvShl32) \ + V(RiscvShr32) \ + V(RiscvSar32) \ + V(RiscvZeroExtendWord) \ + V(RiscvSignExtendWord) \ + V(RiscvClz64) \ + V(RiscvCtz32) \ + V(RiscvCtz64) \ + V(RiscvPopcnt32) \ + V(RiscvPopcnt64) \ + V(RiscvShl64) \ + V(RiscvShr64) \ + V(RiscvSar64) \ + V(RiscvRor32) \ + V(RiscvRor64) \ + V(RiscvMov) \ + V(RiscvTst) \ + V(RiscvCmp) \ + V(RiscvCmpS) \ + V(RiscvAddS) \ + V(RiscvSubS) \ + V(RiscvMulS) \ + V(RiscvDivS) \ + V(RiscvModS) \ + V(RiscvAbsS) \ + V(RiscvNegS) \ + V(RiscvSqrtS) \ + V(RiscvMaxS) \ + V(RiscvMinS) \ + V(RiscvCmpD) \ + V(RiscvAddD) \ + V(RiscvSubD) \ + V(RiscvMulD) \ + V(RiscvDivD) \ + V(RiscvModD) \ + V(RiscvAbsD) \ + V(RiscvNegD) \ + V(RiscvSqrtD) \ + V(RiscvMaxD) \ + V(RiscvMinD) \ + V(RiscvFloat64RoundDown) \ + V(RiscvFloat64RoundTruncate) \ + V(RiscvFloat64RoundUp) \ + V(RiscvFloat64RoundTiesEven) \ + V(RiscvFloat32RoundDown) \ + V(RiscvFloat32RoundTruncate) \ + V(RiscvFloat32RoundUp) \ + V(RiscvFloat32RoundTiesEven) \ + V(RiscvCvtSD) \ + V(RiscvCvtDS) \ + V(RiscvTruncWD) \ + V(RiscvRoundWD) \ + V(RiscvFloorWD) \ + V(RiscvCeilWD) \ + V(RiscvTruncWS) \ + V(RiscvRoundWS) \ + V(RiscvFloorWS) \ + V(RiscvCeilWS) \ + V(RiscvTruncLS) \ + V(RiscvTruncLD) \ + V(RiscvTruncUwD) \ + V(RiscvTruncUwS) \ + V(RiscvTruncUlS) \ + V(RiscvTruncUlD) \ + V(RiscvCvtDW) \ + V(RiscvCvtSL) \ + V(RiscvCvtSW) \ + V(RiscvCvtSUw) \ + V(RiscvCvtSUl) \ + V(RiscvCvtDL) \ + V(RiscvCvtDUw) \ + V(RiscvCvtDUl) \ + V(RiscvLb) \ + V(RiscvLbu) \ + V(RiscvSb) \ + V(RiscvLh) \ + V(RiscvUlh) \ + V(RiscvLhu) \ + V(RiscvUlhu) \ + V(RiscvSh) \ + V(RiscvUsh) \ + V(RiscvLd) \ + V(RiscvUld) \ + V(RiscvLw) \ + V(RiscvUlw) \ + V(RiscvLwu) \ + V(RiscvUlwu) \ + V(RiscvSw) \ + V(RiscvUsw) \ + V(RiscvSd) \ + V(RiscvUsd) \ + V(RiscvLoadFloat) \ + V(RiscvULoadFloat) \ + V(RiscvStoreFloat) \ + V(RiscvUStoreFloat) \ + V(RiscvLoadDouble) \ + V(RiscvULoadDouble) \ + V(RiscvStoreDouble) \ + V(RiscvUStoreDouble) \ + V(RiscvBitcastDL) \ + V(RiscvBitcastLD) \ + V(RiscvBitcastInt32ToFloat32) \ + V(RiscvBitcastFloat32ToInt32) \ + V(RiscvFloat64ExtractLowWord32) \ + V(RiscvFloat64ExtractHighWord32) \ + V(RiscvFloat64InsertLowWord32) \ + V(RiscvFloat64InsertHighWord32) \ + V(RiscvFloat32Max) \ + V(RiscvFloat64Max) \ + V(RiscvFloat32Min) \ + V(RiscvFloat64Min) \ + V(RiscvFloat64SilenceNaN) \ + V(RiscvPush) \ + V(RiscvPeek) \ + V(RiscvByteSwap64) \ + V(RiscvByteSwap32) \ + V(RiscvStoreToStackSlot) \ + V(RiscvStackClaim) \ + V(RiscvSignExtendByte) \ + V(RiscvSignExtendShort) \ + V(RiscvSync) \ + V(RiscvAssertEqual) \ + V(RiscvS128Const) \ + V(RiscvS128Zero) \ + V(RiscvS128AllOnes) \ + V(RiscvI32x4Splat) \ + V(RiscvI32x4ExtractLane) \ + V(RiscvI32x4ReplaceLane) \ + V(RiscvI32x4Add) \ + V(RiscvI32x4AddHoriz) \ + V(RiscvI32x4Sub) \ + V(RiscvF64x2Abs) \ + V(RiscvF64x2Neg) \ + V(RiscvF32x4Splat) \ + V(RiscvF32x4ExtractLane) \ + V(RiscvF32x4ReplaceLane) \ + V(RiscvF32x4SConvertI32x4) \ + V(RiscvF32x4UConvertI32x4) \ + V(RiscvI32x4Mul) \ + V(RiscvI32x4MaxS) \ + V(RiscvI32x4MinS) \ + V(RiscvI32x4Eq) \ + V(RiscvI32x4Ne) \ + V(RiscvI32x4Shl) \ + V(RiscvI32x4ShrS) \ + V(RiscvI32x4ShrU) \ + V(RiscvI32x4MaxU) \ + V(RiscvI32x4MinU) \ + V(RiscvF64x2Sqrt) \ + V(RiscvF64x2Add) \ + V(RiscvF64x2Sub) \ + V(RiscvF64x2Mul) \ + V(RiscvF64x2Div) \ + V(RiscvF64x2Min) \ + V(RiscvF64x2Max) \ + V(RiscvF64x2Eq) \ + V(RiscvF64x2Ne) \ + V(RiscvF64x2Lt) \ + V(RiscvF64x2Le) \ + V(RiscvF64x2Splat) \ + V(RiscvF64x2ExtractLane) \ + V(RiscvF64x2ReplaceLane) \ + V(RiscvF64x2Pmin) \ + V(RiscvF64x2Pmax) \ + V(RiscvF64x2Ceil) \ + V(RiscvF64x2Floor) \ + V(RiscvF64x2Trunc) \ + V(RiscvF64x2NearestInt) \ + V(RiscvI64x2Splat) \ + V(RiscvI64x2ExtractLane) \ + V(RiscvI64x2ReplaceLane) \ + V(RiscvI64x2Add) \ + V(RiscvI64x2Sub) \ + V(RiscvI64x2Mul) \ + V(RiscvI64x2Neg) \ + V(RiscvI64x2Shl) \ + V(RiscvI64x2ShrS) \ + V(RiscvI64x2ShrU) \ + V(RiscvF32x4Abs) \ + V(RiscvF32x4Neg) \ + V(RiscvF32x4Sqrt) \ + V(RiscvF32x4RecipApprox) \ + V(RiscvF32x4RecipSqrtApprox) \ + V(RiscvF32x4Add) \ + V(RiscvF32x4AddHoriz) \ + V(RiscvF32x4Sub) \ + V(RiscvF32x4Mul) \ + V(RiscvF32x4Div) \ + V(RiscvF32x4Max) \ + V(RiscvF32x4Min) \ + V(RiscvF32x4Eq) \ + V(RiscvF32x4Ne) \ + V(RiscvF32x4Lt) \ + V(RiscvF32x4Le) \ + V(RiscvF32x4Pmin) \ + V(RiscvF32x4Pmax) \ + V(RiscvF32x4Ceil) \ + V(RiscvF32x4Floor) \ + V(RiscvF32x4Trunc) \ + V(RiscvF32x4NearestInt) \ + V(RiscvI32x4SConvertF32x4) \ + V(RiscvI32x4UConvertF32x4) \ + V(RiscvI32x4Neg) \ + V(RiscvI32x4GtS) \ + V(RiscvI32x4GeS) \ + V(RiscvI32x4GtU) \ + V(RiscvI32x4GeU) \ + V(RiscvI32x4Abs) \ + V(RiscvI32x4BitMask) \ + V(RiscvI16x8Splat) \ + V(RiscvI16x8ExtractLaneU) \ + V(RiscvI16x8ExtractLaneS) \ + V(RiscvI16x8ReplaceLane) \ + V(RiscvI16x8Neg) \ + V(RiscvI16x8Shl) \ + V(RiscvI16x8ShrS) \ + V(RiscvI16x8ShrU) \ + V(RiscvI16x8Add) \ + V(RiscvI16x8AddSaturateS) \ + V(RiscvI16x8AddHoriz) \ + V(RiscvI16x8Sub) \ + V(RiscvI16x8SubSaturateS) \ + V(RiscvI16x8Mul) \ + V(RiscvI16x8MaxS) \ + V(RiscvI16x8MinS) \ + V(RiscvI16x8Eq) \ + V(RiscvI16x8Ne) \ + V(RiscvI16x8GtS) \ + V(RiscvI16x8GeS) \ + V(RiscvI16x8AddSaturateU) \ + V(RiscvI16x8SubSaturateU) \ + V(RiscvI16x8MaxU) \ + V(RiscvI16x8MinU) \ + V(RiscvI16x8GtU) \ + V(RiscvI16x8GeU) \ + V(RiscvI16x8RoundingAverageU) \ + V(RiscvI16x8Abs) \ + V(RiscvI16x8BitMask) \ + V(RiscvI8x16Splat) \ + V(RiscvI8x16ExtractLaneU) \ + V(RiscvI8x16ExtractLaneS) \ + V(RiscvI8x16ReplaceLane) \ + V(RiscvI8x16Neg) \ + V(RiscvI8x16Shl) \ + V(RiscvI8x16ShrS) \ + V(RiscvI8x16Add) \ + V(RiscvI8x16AddSaturateS) \ + V(RiscvI8x16Sub) \ + V(RiscvI8x16SubSaturateS) \ + V(RiscvI8x16Mul) \ + V(RiscvI8x16MaxS) \ + V(RiscvI8x16MinS) \ + V(RiscvI8x16Eq) \ + V(RiscvI8x16Ne) \ + V(RiscvI8x16GtS) \ + V(RiscvI8x16GeS) \ + V(RiscvI8x16ShrU) \ + V(RiscvI8x16AddSaturateU) \ + V(RiscvI8x16SubSaturateU) \ + V(RiscvI8x16MaxU) \ + V(RiscvI8x16MinU) \ + V(RiscvI8x16GtU) \ + V(RiscvI8x16GeU) \ + V(RiscvI8x16RoundingAverageU) \ + V(RiscvI8x16Abs) \ + V(RiscvI8x16BitMask) \ + V(RiscvS128And) \ + V(RiscvS128Or) \ + V(RiscvS128Xor) \ + V(RiscvS128Not) \ + V(RiscvS128Select) \ + V(RiscvS128AndNot) \ + V(RiscvV32x4AnyTrue) \ + V(RiscvV32x4AllTrue) \ + V(RiscvV16x8AnyTrue) \ + V(RiscvV16x8AllTrue) \ + V(RiscvV8x16AnyTrue) \ + V(RiscvV8x16AllTrue) \ + V(RiscvS32x4InterleaveRight) \ + V(RiscvS32x4InterleaveLeft) \ + V(RiscvS32x4PackEven) \ + V(RiscvS32x4PackOdd) \ + V(RiscvS32x4InterleaveEven) \ + V(RiscvS32x4InterleaveOdd) \ + V(RiscvS32x4Shuffle) \ + V(RiscvS16x8InterleaveRight) \ + V(RiscvS16x8InterleaveLeft) \ + V(RiscvS16x8PackEven) \ + V(RiscvS16x8PackOdd) \ + V(RiscvS16x8InterleaveEven) \ + V(RiscvS16x8InterleaveOdd) \ + V(RiscvS16x4Reverse) \ + V(RiscvS16x2Reverse) \ + V(RiscvS8x16InterleaveRight) \ + V(RiscvS8x16InterleaveLeft) \ + V(RiscvS8x16PackEven) \ + V(RiscvS8x16PackOdd) \ + V(RiscvS8x16InterleaveEven) \ + V(RiscvS8x16InterleaveOdd) \ + V(RiscvI8x16Shuffle) \ + V(RiscvI8x16Swizzle) \ + V(RiscvS8x16Concat) \ + V(RiscvS8x8Reverse) \ + V(RiscvS8x4Reverse) \ + V(RiscvS8x2Reverse) \ + V(RiscvS8x16LoadSplat) \ + V(RiscvS16x8LoadSplat) \ + V(RiscvS32x4LoadSplat) \ + V(RiscvS64x2LoadSplat) \ + V(RiscvI16x8Load8x8S) \ + V(RiscvI16x8Load8x8U) \ + V(RiscvI32x4Load16x4S) \ + V(RiscvI32x4Load16x4U) \ + V(RiscvI64x2Load32x2S) \ + V(RiscvI64x2Load32x2U) \ + V(RiscvMsaLd) \ + V(RiscvMsaSt) \ + V(RiscvI32x4SConvertI16x8Low) \ + V(RiscvI32x4SConvertI16x8High) \ + V(RiscvI32x4UConvertI16x8Low) \ + V(RiscvI32x4UConvertI16x8High) \ + V(RiscvI16x8SConvertI8x16Low) \ + V(RiscvI16x8SConvertI8x16High) \ + V(RiscvI16x8SConvertI32x4) \ + V(RiscvI16x8UConvertI32x4) \ + V(RiscvI16x8UConvertI8x16Low) \ + V(RiscvI16x8UConvertI8x16High) \ + V(RiscvI8x16SConvertI16x8) \ + V(RiscvI8x16UConvertI16x8) \ + V(RiscvWord64AtomicLoadUint8) \ + V(RiscvWord64AtomicLoadUint16) \ + V(RiscvWord64AtomicLoadUint32) \ + V(RiscvWord64AtomicLoadUint64) \ + V(RiscvWord64AtomicStoreWord8) \ + V(RiscvWord64AtomicStoreWord16) \ + V(RiscvWord64AtomicStoreWord32) \ + V(RiscvWord64AtomicStoreWord64) \ + V(RiscvWord64AtomicAddUint8) \ + V(RiscvWord64AtomicAddUint16) \ + V(RiscvWord64AtomicAddUint32) \ + V(RiscvWord64AtomicAddUint64) \ + V(RiscvWord64AtomicSubUint8) \ + V(RiscvWord64AtomicSubUint16) \ + V(RiscvWord64AtomicSubUint32) \ + V(RiscvWord64AtomicSubUint64) \ + V(RiscvWord64AtomicAndUint8) \ + V(RiscvWord64AtomicAndUint16) \ + V(RiscvWord64AtomicAndUint32) \ + V(RiscvWord64AtomicAndUint64) \ + V(RiscvWord64AtomicOrUint8) \ + V(RiscvWord64AtomicOrUint16) \ + V(RiscvWord64AtomicOrUint32) \ + V(RiscvWord64AtomicOrUint64) \ + V(RiscvWord64AtomicXorUint8) \ + V(RiscvWord64AtomicXorUint16) \ + V(RiscvWord64AtomicXorUint32) \ + V(RiscvWord64AtomicXorUint64) \ + V(RiscvWord64AtomicExchangeUint8) \ + V(RiscvWord64AtomicExchangeUint16) \ + V(RiscvWord64AtomicExchangeUint32) \ + V(RiscvWord64AtomicExchangeUint64) \ + V(RiscvWord64AtomicCompareExchangeUint8) \ + V(RiscvWord64AtomicCompareExchangeUint16) \ + V(RiscvWord64AtomicCompareExchangeUint32) \ + V(RiscvWord64AtomicCompareExchangeUint64) + +// 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] */ + +} // namespace compiler +} // namespace internal +} // namespace v8 + +#endif // V8_COMPILER_BACKEND_RISCV_INSTRUCTION_CODES_RISCV_H_ Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/compiler/backend/riscv64/instruction-scheduler-riscv64.cc =================================================================== --- /dev/null +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/compiler/backend/riscv64/instruction-scheduler-riscv64.cc @@ -0,0 +1,1571 @@ +// 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 kRiscvAbsD: + case kRiscvAbsS: + case kRiscvAdd32: + case kRiscvAddD: + case kRiscvAddS: + case kRiscvAnd: + case kRiscvAnd32: + case kRiscvAssertEqual: + case kRiscvBitcastDL: + case kRiscvBitcastLD: + case kRiscvBitcastInt32ToFloat32: + case kRiscvBitcastFloat32ToInt32: + case kRiscvByteSwap32: + case kRiscvByteSwap64: + case kRiscvCeilWD: + case kRiscvCeilWS: + case kRiscvClz32: + case kRiscvCmp: + case kRiscvCmpD: + case kRiscvCmpS: + case kRiscvCtz32: + case kRiscvCvtDL: + case kRiscvCvtDS: + case kRiscvCvtDUl: + case kRiscvCvtDUw: + case kRiscvCvtDW: + case kRiscvCvtSD: + case kRiscvCvtSL: + case kRiscvCvtSUl: + case kRiscvCvtSUw: + case kRiscvCvtSW: + case kRiscvMulHigh64: + case kRiscvMulHighU32: + case kRiscvAdd64: + case kRiscvAddOvf64: + case kRiscvClz64: + case kRiscvCtz64: + case kRiscvDiv64: + case kRiscvDivU64: + case kRiscvZeroExtendWord: + case kRiscvSignExtendWord: + case kRiscvDiv32: + case kRiscvDivD: + case kRiscvDivS: + case kRiscvDivU32: + case kRiscvMod64: + case kRiscvModU64: + case kRiscvMul64: + case kRiscvPopcnt64: + case kRiscvRor64: + case kRiscvSar64: + case kRiscvShl64: + case kRiscvShr64: + case kRiscvSub64: + case kRiscvSubOvf64: + case kRiscvF64x2Abs: + case kRiscvF64x2Neg: + case kRiscvF64x2Sqrt: + case kRiscvF64x2Add: + case kRiscvF64x2Sub: + case kRiscvF64x2Mul: + case kRiscvF64x2Div: + case kRiscvF64x2Min: + case kRiscvF64x2Max: + case kRiscvF64x2Eq: + case kRiscvF64x2Ne: + case kRiscvF64x2Lt: + case kRiscvF64x2Le: + case kRiscvF64x2Pmin: + case kRiscvF64x2Pmax: + case kRiscvF64x2Ceil: + case kRiscvF64x2Floor: + case kRiscvF64x2Trunc: + case kRiscvF64x2NearestInt: + case kRiscvI64x2Splat: + case kRiscvI64x2ExtractLane: + case kRiscvI64x2ReplaceLane: + case kRiscvI64x2Add: + case kRiscvI64x2Sub: + case kRiscvI64x2Mul: + case kRiscvI64x2Neg: + case kRiscvI64x2Shl: + case kRiscvI64x2ShrS: + case kRiscvI64x2ShrU: + case kRiscvF32x4Abs: + case kRiscvF32x4Add: + case kRiscvF32x4AddHoriz: + case kRiscvF32x4Eq: + case kRiscvF32x4ExtractLane: + case kRiscvF32x4Lt: + case kRiscvF32x4Le: + case kRiscvF32x4Max: + case kRiscvF32x4Min: + case kRiscvF32x4Mul: + case kRiscvF32x4Div: + case kRiscvF32x4Ne: + case kRiscvF32x4Neg: + case kRiscvF32x4Sqrt: + case kRiscvF32x4RecipApprox: + case kRiscvF32x4RecipSqrtApprox: + case kRiscvF32x4ReplaceLane: + case kRiscvF32x4SConvertI32x4: + case kRiscvF32x4Splat: + case kRiscvF32x4Sub: + case kRiscvF32x4UConvertI32x4: + case kRiscvF32x4Pmin: + case kRiscvF32x4Pmax: + case kRiscvF32x4Ceil: + case kRiscvF32x4Floor: + case kRiscvF32x4Trunc: + case kRiscvF32x4NearestInt: + case kRiscvF64x2Splat: + case kRiscvF64x2ExtractLane: + case kRiscvF64x2ReplaceLane: + case kRiscvFloat32Max: + case kRiscvFloat32Min: + case kRiscvFloat32RoundDown: + case kRiscvFloat32RoundTiesEven: + case kRiscvFloat32RoundTruncate: + case kRiscvFloat32RoundUp: + case kRiscvFloat64ExtractLowWord32: + case kRiscvFloat64ExtractHighWord32: + case kRiscvFloat64InsertLowWord32: + case kRiscvFloat64InsertHighWord32: + case kRiscvFloat64Max: + case kRiscvFloat64Min: + case kRiscvFloat64RoundDown: + case kRiscvFloat64RoundTiesEven: + case kRiscvFloat64RoundTruncate: + case kRiscvFloat64RoundUp: + case kRiscvFloat64SilenceNaN: + case kRiscvFloorWD: + case kRiscvFloorWS: + case kRiscvI16x8Add: + case kRiscvI16x8AddHoriz: + case kRiscvI16x8AddSaturateS: + case kRiscvI16x8AddSaturateU: + case kRiscvI16x8Eq: + case kRiscvI16x8ExtractLaneU: + case kRiscvI16x8ExtractLaneS: + case kRiscvI16x8GeS: + case kRiscvI16x8GeU: + case kRiscvI16x8GtS: + case kRiscvI16x8GtU: + case kRiscvI16x8MaxS: + case kRiscvI16x8MaxU: + case kRiscvI16x8MinS: + case kRiscvI16x8MinU: + case kRiscvI16x8Mul: + case kRiscvI16x8Ne: + case kRiscvI16x8Neg: + case kRiscvI16x8ReplaceLane: + case kRiscvI8x16SConvertI16x8: + case kRiscvI16x8SConvertI32x4: + case kRiscvI16x8SConvertI8x16High: + case kRiscvI16x8SConvertI8x16Low: + case kRiscvI16x8Shl: + case kRiscvI16x8ShrS: + case kRiscvI16x8ShrU: + case kRiscvI16x8Splat: + case kRiscvI16x8Sub: + case kRiscvI16x8SubSaturateS: + case kRiscvI16x8SubSaturateU: + case kRiscvI8x16UConvertI16x8: + case kRiscvI16x8UConvertI32x4: + case kRiscvI16x8UConvertI8x16High: + case kRiscvI16x8UConvertI8x16Low: + case kRiscvI16x8RoundingAverageU: + case kRiscvI16x8Abs: + case kRiscvI16x8BitMask: + case kRiscvI32x4Add: + case kRiscvI32x4AddHoriz: + case kRiscvI32x4Eq: + case kRiscvI32x4ExtractLane: + case kRiscvI32x4GeS: + case kRiscvI32x4GeU: + case kRiscvI32x4GtS: + case kRiscvI32x4GtU: + case kRiscvI32x4MaxS: + case kRiscvI32x4MaxU: + case kRiscvI32x4MinS: + case kRiscvI32x4MinU: + case kRiscvI32x4Mul: + case kRiscvI32x4Ne: + case kRiscvI32x4Neg: + case kRiscvI32x4ReplaceLane: + case kRiscvI32x4SConvertF32x4: + case kRiscvI32x4SConvertI16x8High: + case kRiscvI32x4SConvertI16x8Low: + case kRiscvI32x4Shl: + case kRiscvI32x4ShrS: + case kRiscvI32x4ShrU: + case kRiscvI32x4Splat: + case kRiscvI32x4Sub: + case kRiscvI32x4UConvertF32x4: + case kRiscvI32x4UConvertI16x8High: + case kRiscvI32x4UConvertI16x8Low: + case kRiscvI32x4Abs: + case kRiscvI32x4BitMask: + case kRiscvI8x16Add: + case kRiscvI8x16AddSaturateS: + case kRiscvI8x16AddSaturateU: + case kRiscvI8x16Eq: + case kRiscvI8x16ExtractLaneU: + case kRiscvI8x16ExtractLaneS: + case kRiscvI8x16GeS: + case kRiscvI8x16GeU: + case kRiscvI8x16GtS: + case kRiscvI8x16GtU: + case kRiscvI8x16MaxS: + case kRiscvI8x16MaxU: + case kRiscvI8x16MinS: + case kRiscvI8x16MinU: + case kRiscvI8x16Mul: + case kRiscvI8x16Ne: + case kRiscvI8x16Neg: + case kRiscvI8x16ReplaceLane: + case kRiscvI8x16Shl: + case kRiscvI8x16ShrS: + case kRiscvI8x16ShrU: + case kRiscvI8x16Splat: + case kRiscvI8x16Sub: + case kRiscvI8x16SubSaturateS: + case kRiscvI8x16SubSaturateU: + case kRiscvI8x16RoundingAverageU: + case kRiscvI8x16Abs: + case kRiscvI8x16BitMask: + case kRiscvMaxD: + case kRiscvMaxS: + case kRiscvMinD: + case kRiscvMinS: + case kRiscvMod32: + case kRiscvModU32: + case kRiscvMov: + case kRiscvMul32: + case kRiscvMulD: + case kRiscvMulHigh32: + case kRiscvMulOvf32: + case kRiscvMulS: + case kRiscvNegD: + case kRiscvNegS: + case kRiscvNor: + case kRiscvNor32: + case kRiscvOr: + case kRiscvOr32: + case kRiscvPopcnt32: + case kRiscvRor32: + case kRiscvRoundWD: + case kRiscvRoundWS: + case kRiscvS128And: + case kRiscvS128Or: + case kRiscvS128Not: + case kRiscvS128Select: + case kRiscvS128AndNot: + case kRiscvS128Xor: + case kRiscvS128Const: + case kRiscvS128Zero: + case kRiscvS128AllOnes: + case kRiscvS16x8InterleaveEven: + case kRiscvS16x8InterleaveOdd: + case kRiscvS16x8InterleaveLeft: + case kRiscvS16x8InterleaveRight: + case kRiscvS16x8PackEven: + case kRiscvS16x8PackOdd: + case kRiscvS16x2Reverse: + case kRiscvS16x4Reverse: + case kRiscvV8x16AllTrue: + case kRiscvV8x16AnyTrue: + case kRiscvV32x4AllTrue: + case kRiscvV32x4AnyTrue: + case kRiscvV16x8AllTrue: + case kRiscvV16x8AnyTrue: + case kRiscvS32x4InterleaveEven: + case kRiscvS32x4InterleaveOdd: + case kRiscvS32x4InterleaveLeft: + case kRiscvS32x4InterleaveRight: + case kRiscvS32x4PackEven: + case kRiscvS32x4PackOdd: + case kRiscvS32x4Shuffle: + case kRiscvS8x16Concat: + case kRiscvS8x16InterleaveEven: + case kRiscvS8x16InterleaveOdd: + case kRiscvS8x16InterleaveLeft: + case kRiscvS8x16InterleaveRight: + case kRiscvS8x16PackEven: + case kRiscvS8x16PackOdd: + case kRiscvS8x2Reverse: + case kRiscvS8x4Reverse: + case kRiscvS8x8Reverse: + case kRiscvI8x16Shuffle: + case kRiscvI8x16Swizzle: + case kRiscvSar32: + case kRiscvSignExtendByte: + case kRiscvSignExtendShort: + case kRiscvShl32: + case kRiscvShr32: + case kRiscvSqrtD: + case kRiscvSqrtS: + case kRiscvSub32: + case kRiscvSubD: + case kRiscvSubS: + case kRiscvTruncLD: + case kRiscvTruncLS: + case kRiscvTruncUlD: + case kRiscvTruncUlS: + case kRiscvTruncUwD: + case kRiscvTruncUwS: + case kRiscvTruncWD: + case kRiscvTruncWS: + case kRiscvTst: + case kRiscvXor: + case kRiscvXor32: + return kNoOpcodeFlags; + + case kRiscvLb: + case kRiscvLbu: + case kRiscvLd: + case kRiscvLoadDouble: + case kRiscvLh: + case kRiscvLhu: + case kRiscvLw: + case kRiscvLoadFloat: + case kRiscvLwu: + case kRiscvMsaLd: + case kRiscvPeek: + case kRiscvUld: + case kRiscvULoadDouble: + case kRiscvUlh: + case kRiscvUlhu: + case kRiscvUlw: + case kRiscvUlwu: + case kRiscvULoadFloat: + case kRiscvS8x16LoadSplat: + case kRiscvS16x8LoadSplat: + case kRiscvS32x4LoadSplat: + case kRiscvS64x2LoadSplat: + case kRiscvI16x8Load8x8S: + case kRiscvI16x8Load8x8U: + case kRiscvI32x4Load16x4S: + case kRiscvI32x4Load16x4U: + case kRiscvI64x2Load32x2S: + case kRiscvI64x2Load32x2U: + case kRiscvWord64AtomicLoadUint8: + case kRiscvWord64AtomicLoadUint16: + case kRiscvWord64AtomicLoadUint32: + case kRiscvWord64AtomicLoadUint64: + + return kIsLoadOperation; + + case kRiscvModD: + case kRiscvModS: + case kRiscvMsaSt: + case kRiscvPush: + case kRiscvSb: + case kRiscvSd: + case kRiscvStoreDouble: + case kRiscvSh: + case kRiscvStackClaim: + case kRiscvStoreToStackSlot: + case kRiscvSw: + case kRiscvStoreFloat: + case kRiscvUsd: + case kRiscvUStoreDouble: + case kRiscvUsh: + case kRiscvUsw: + case kRiscvUStoreFloat: + case kRiscvSync: + case kRiscvWord64AtomicStoreWord8: + case kRiscvWord64AtomicStoreWord16: + case kRiscvWord64AtomicStoreWord32: + case kRiscvWord64AtomicStoreWord64: + case kRiscvWord64AtomicAddUint8: + case kRiscvWord64AtomicAddUint16: + case kRiscvWord64AtomicAddUint32: + case kRiscvWord64AtomicAddUint64: + case kRiscvWord64AtomicSubUint8: + case kRiscvWord64AtomicSubUint16: + case kRiscvWord64AtomicSubUint32: + case kRiscvWord64AtomicSubUint64: + case kRiscvWord64AtomicAndUint8: + case kRiscvWord64AtomicAndUint16: + case kRiscvWord64AtomicAndUint32: + case kRiscvWord64AtomicAndUint64: + case kRiscvWord64AtomicOrUint8: + case kRiscvWord64AtomicOrUint16: + case kRiscvWord64AtomicOrUint32: + case kRiscvWord64AtomicOrUint64: + case kRiscvWord64AtomicXorUint8: + case kRiscvWord64AtomicXorUint16: + case kRiscvWord64AtomicXorUint32: + case kRiscvWord64AtomicXorUint64: + case kRiscvWord64AtomicExchangeUint8: + case kRiscvWord64AtomicExchangeUint16: + case kRiscvWord64AtomicExchangeUint32: + case kRiscvWord64AtomicExchangeUint64: + case kRiscvWord64AtomicCompareExchangeUint8: + case kRiscvWord64AtomicCompareExchangeUint16: + case kRiscvWord64AtomicCompareExchangeUint32: + case kRiscvWord64AtomicCompareExchangeUint64: + return kHasSideEffect; + +#define CASE(Name) case k##Name: + COMMON_ARCH_OPCODE_LIST(CASE) +#undef CASE + // Already covered in architecture independent code. + UNREACHABLE(); + } + + UNREACHABLE(); +} + +enum Latency { + BRANCH = 4, // Estimated max. + RINT_S = 4, // Estimated. + RINT_D = 4, // Estimated. + + // FIXME (RISCV): remove MULT instructions (MIPS legacy) + MULT = 4, + MULTU = 4, + DMULT = 4, + + MUL32 = 7, + + DIV32 = 50, // Min:11 Max:50 + DIV64 = 50, + DIVU32 = 50, + DIVU64 = 50, + + ABS_S = 4, + ABS_D = 4, + NEG_S = 4, + NEG_D = 4, + ADD_S = 4, + ADD_D = 4, + SUB_S = 4, + SUB_D = 4, + MAX_S = 4, // Estimated. + MIN_S = 4, + MAX_D = 4, // Estimated. + MIN_D = 4, + C_cond_S = 4, + C_cond_D = 4, + MUL_S = 4, + + MADD_S = 4, + MSUB_S = 4, + NMADD_S = 4, + NMSUB_S = 4, + + CABS_cond_S = 4, + CABS_cond_D = 4, + + CVT_D_S = 4, + CVT_PS_PW = 4, + + CVT_S_W = 4, + CVT_S_L = 4, + CVT_D_W = 4, + CVT_D_L = 4, + + CVT_S_D = 4, + + CVT_W_S = 4, + CVT_W_D = 4, + CVT_L_S = 4, + CVT_L_D = 4, + + CEIL_W_S = 4, + CEIL_W_D = 4, + CEIL_L_S = 4, + CEIL_L_D = 4, + + FLOOR_W_S = 4, + FLOOR_W_D = 4, + FLOOR_L_S = 4, + FLOOR_L_D = 4, + + ROUND_W_S = 4, + ROUND_W_D = 4, + ROUND_L_S = 4, + ROUND_L_D = 4, + + TRUNC_W_S = 4, + TRUNC_W_D = 4, + TRUNC_L_S = 4, + TRUNC_L_D = 4, + + MOV_S = 4, + MOV_D = 4, + + MOVF_S = 4, + MOVF_D = 4, + + MOVN_S = 4, + MOVN_D = 4, + + MOVT_S = 4, + MOVT_D = 4, + + MOVZ_S = 4, + MOVZ_D = 4, + + MUL_D = 5, + MADD_D = 5, + MSUB_D = 5, + NMADD_D = 5, + NMSUB_D = 5, + + RECIP_S = 13, + RECIP_D = 26, + + RSQRT_S = 17, + RSQRT_D = 36, + + DIV_S = 17, + SQRT_S = 17, + + DIV_D = 32, + SQRT_D = 32, + + MOVT_FREG = 4, + MOVT_HIGH_FREG = 4, + MOVT_DREG = 4, + LOAD_FLOAT = 4, + LOAD_DOUBLE = 4, + + MOVF_FREG = 1, + MOVF_HIGH_FREG = 1, + MOVF_HIGH_DREG = 1, + MOVF_HIGH = 1, + MOVF_LOW = 1, + STORE_FLOAT = 1, + STORE_DOUBLE = 1, +}; + +int Add64Latency(bool is_operand_register = true) { + if (is_operand_register) { + return 1; + } else { + return 2; // Estimated max. + } +} + +int Sub64Latency(bool is_operand_register = true) { + return Add64Latency(is_operand_register); +} + +int AndLatency(bool is_operand_register = true) { + return Add64Latency(is_operand_register); +} + +int OrLatency(bool is_operand_register = true) { + return Add64Latency(is_operand_register); +} + +int NorLatency(bool is_operand_register = true) { + if (is_operand_register) { + return 1; + } else { + return 2; // Estimated max. + } +} + +int XorLatency(bool is_operand_register = true) { + return Add64Latency(is_operand_register); +} + +int Mul32Latency(bool is_operand_register = true) { + if (is_operand_register) { + return Latency::MUL32; + } else { + return Latency::MUL32 + 1; + } +} + +int Mul64Latency(bool is_operand_register = true) { + int latency = Latency::DMULT + Latency::MOVF_LOW; + if (!is_operand_register) { + latency += 1; + } + return latency; +} + +int Mulh32Latency(bool is_operand_register = true) { + int latency = Latency::MULT + Latency::MOVF_HIGH; + if (!is_operand_register) { + latency += 1; + } + return latency; +} + +int Mulhu32Latency(bool is_operand_register = true) { + int latency = Latency::MULTU + Latency::MOVF_HIGH; + if (!is_operand_register) { + latency += 1; + } + return latency; +} + +int Mulh64Latency(bool is_operand_register = true) { + int latency = Latency::DMULT + Latency::MOVF_HIGH; + if (!is_operand_register) { + latency += 1; + } + return latency; +} + +int Div32Latency(bool is_operand_register = true) { + if (is_operand_register) { + return Latency::DIV32; + } else { + return Latency::DIV32 + 1; + } +} + +int Divu32Latency(bool is_operand_register = true) { + if (is_operand_register) { + return Latency::DIVU32; + } else { + return Latency::DIVU32 + 1; + } +} + +int Div64Latency(bool is_operand_register = true) { + int latency = Latency::DIV64 + Latency::MOVF_LOW; + if (!is_operand_register) { + latency += 1; + } + return latency; +} + +int Divu64Latency(bool is_operand_register = true) { + int latency = Latency::DIVU64 + Latency::MOVF_LOW; + if (!is_operand_register) { + latency += 1; + } + return latency; +} + +int Mod32Latency(bool is_operand_register = true) { + int latency = Latency::DIV32 + Latency::MOVF_HIGH; + if (!is_operand_register) { + latency += 1; + } + return latency; +} + +int Modu32Latency(bool is_operand_register = true) { + int latency = Latency::DIVU32 + Latency::MOVF_HIGH; + if (!is_operand_register) { + latency += 1; + } + return latency; +} + +int Mod64Latency(bool is_operand_register = true) { + int latency = Latency::DIV64 + Latency::MOVF_HIGH; + if (!is_operand_register) { + latency += 1; + } + return latency; +} + +int Modu64Latency(bool is_operand_register = true) { + int latency = Latency::DIV64 + Latency::MOVF_HIGH; + if (!is_operand_register) { + latency += 1; + } + return latency; +} + +int MovzLatency() { return 1; } + +int MovnLatency() { return 1; } + +int CallLatency() { + // Estimated. + return Add64Latency(false) + Latency::BRANCH + 5; +} + +int JumpLatency() { + // Estimated max. + return 1 + Add64Latency() + Latency::BRANCH + 2; +} + +int SmiUntagLatency() { return 1; } + +int PrepareForTailCallLatency() { + // Estimated max. + return 2 * (Add64Latency() + 1 + Add64Latency(false)) + 2 + Latency::BRANCH + + Latency::BRANCH + 2 * Sub64Latency(false) + 2 + Latency::BRANCH + 1; +} + +int AssemblePopArgumentsAdoptFrameLatency() { + return 1 + Latency::BRANCH + 1 + SmiUntagLatency() + + PrepareForTailCallLatency(); +} + +int AssertLatency() { return 1; } + +int PrepareCallCFunctionLatency() { + int frame_alignment = TurboAssembler::ActivationFrameAlignment(); + if (frame_alignment > kSystemPointerSize) { + return 1 + Sub64Latency(false) + AndLatency(false) + 1; + } else { + return Sub64Latency(false); + } +} + +int AdjustBaseAndOffsetLatency() { + return 3; // Estimated max. +} + +int AlignedMemoryLatency() { return AdjustBaseAndOffsetLatency() + 1; } + +int UlhuLatency() { + return AdjustBaseAndOffsetLatency() + 2 * AlignedMemoryLatency() + 2; +} + +int UlwLatency() { + // Estimated max. + return AdjustBaseAndOffsetLatency() + 3; +} + +int UlwuLatency() { return UlwLatency() + 1; } + +int UldLatency() { + // Estimated max. + return AdjustBaseAndOffsetLatency() + 3; +} + +int ULoadFloatLatency() { return UlwLatency() + Latency::MOVT_FREG; } + +int ULoadDoubleLatency() { return UldLatency() + Latency::MOVT_DREG; } + +int UshLatency() { + // Estimated max. + return AdjustBaseAndOffsetLatency() + 2 + 2 * AlignedMemoryLatency(); +} + +int UswLatency() { return AdjustBaseAndOffsetLatency() + 2; } + +int UsdLatency() { return AdjustBaseAndOffsetLatency() + 2; } + +int UStoreFloatLatency() { return Latency::MOVF_FREG + UswLatency(); } + +int UStoreDoubleLatency() { return Latency::MOVF_HIGH_DREG + UsdLatency(); } + +int LoadFloatLatency() { + return AdjustBaseAndOffsetLatency() + Latency::LOAD_FLOAT; +} + +int StoreFloatLatency() { + return AdjustBaseAndOffsetLatency() + Latency::STORE_FLOAT; +} + +int StoreDoubleLatency() { + return AdjustBaseAndOffsetLatency() + Latency::STORE_DOUBLE; +} + +int LoadDoubleLatency() { + return AdjustBaseAndOffsetLatency() + Latency::LOAD_DOUBLE; +} + +int MultiPushLatency() { + int latency = Sub64Latency(false); + for (int16_t i = kNumRegisters - 1; i >= 0; i--) { + latency++; + } + return latency; +} + +int MultiPushFPULatency() { + int latency = Sub64Latency(false); + for (int16_t i = kNumRegisters - 1; i >= 0; i--) { + latency += StoreDoubleLatency(); + } + return latency; +} + +int PushCallerSavedLatency(SaveFPRegsMode fp_mode) { + int latency = MultiPushLatency(); + if (fp_mode == kSaveFPRegs) { + latency += MultiPushFPULatency(); + } + return latency; +} + +int MultiPopLatency() { + int latency = Add64Latency(false); + for (int16_t i = 0; i < kNumRegisters; i++) { + latency++; + } + return latency; +} + +int MultiPopFPULatency() { + int latency = Add64Latency(false); + for (int16_t i = 0; i < kNumRegisters; i++) { + latency += LoadDoubleLatency(); + } + return latency; +} + +int PopCallerSavedLatency(SaveFPRegsMode fp_mode) { + int latency = MultiPopLatency(); + if (fp_mode == kSaveFPRegs) { + latency += MultiPopFPULatency(); + } + return latency; +} + +int CallCFunctionHelperLatency() { + // Estimated. + int latency = AndLatency(false) + Latency::BRANCH + 2 + CallLatency(); + if (base::OS::ActivationFrameAlignment() > kSystemPointerSize) { + latency++; + } else { + latency += Add64Latency(false); + } + return latency; +} + +int CallCFunctionLatency() { return 1 + CallCFunctionHelperLatency(); } + +int AssembleArchJumpLatency() { + // Estimated max. + return Latency::BRANCH; +} + +int GenerateSwitchTableLatency() { + int latency = 6; + latency += 2; + return latency; +} + +int AssembleArchTableSwitchLatency() { + return Latency::BRANCH + GenerateSwitchTableLatency(); +} + +int DropAndRetLatency() { + // Estimated max. + return Add64Latency(false) + JumpLatency(); +} + +int AssemblerReturnLatency() { + // Estimated max. + return Add64Latency(false) + MultiPopLatency() + MultiPopFPULatency() + + Latency::BRANCH + Add64Latency() + 1 + DropAndRetLatency(); +} + +int TryInlineTruncateDoubleToILatency() { + return 2 + Latency::TRUNC_W_D + Latency::MOVF_FREG + 2 + AndLatency(false) + + Latency::BRANCH; +} + +int CallStubDelayedLatency() { return 1 + CallLatency(); } + +int TruncateDoubleToIDelayedLatency() { + // TODO(riscv): This no longer reflects how TruncateDoubleToI is called. + return TryInlineTruncateDoubleToILatency() + 1 + Sub64Latency(false) + + StoreDoubleLatency() + CallStubDelayedLatency() + Add64Latency(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 BranchShortHelperLatency() { + return SltuLatency() + 2; // Estimated max. +} + +int BranchShortLatency() { return BranchShortHelperLatency(); } + +int MoveLatency() { return 1; } + +int MovToFloatParametersLatency() { return 2 * MoveLatency(); } + +int MovFromFloatResultLatency() { return MoveLatency(); } + +int AddOverflow64Latency() { + // Estimated max. + return 6; +} + +int SubOverflow64Latency() { + // Estimated max. + return 6; +} + +int MulOverflow32Latency() { + // Estimated max. + return Mul32Latency() + Mulh32Latency() + 2; +} + +// FIXME (RISCV): need update +int Clz64Latency() { return 1; } + +int Ctz32Latency() { + return Add64Latency(false) + XorLatency() + AndLatency() + Clz64Latency() + + 1 + Sub64Latency(); +} + +int Ctz64Latency() { + return Add64Latency(false) + XorLatency() + AndLatency() + 1 + Sub64Latency(); +} + +int Popcnt32Latency() { + return 2 + AndLatency() + Sub64Latency() + 1 + AndLatency() + 1 + + AndLatency() + Add64Latency() + 1 + Add64Latency() + 1 + AndLatency() + + 1 + Mul32Latency() + 1; +} + +int Popcnt64Latency() { + return 2 + AndLatency() + Sub64Latency() + 1 + AndLatency() + 1 + + AndLatency() + Add64Latency() + 1 + Add64Latency() + 1 + AndLatency() + + 1 + Mul64Latency() + 1; +} + +int CompareFLatency() { return Latency::C_cond_S; } + +int CompareF32Latency() { return CompareFLatency(); } + +int CompareF64Latency() { return CompareFLatency(); } + +int CompareIsNanFLatency() { return CompareFLatency(); } + +int CompareIsNanF32Latency() { return CompareIsNanFLatency(); } + +int CompareIsNanF64Latency() { return CompareIsNanFLatency(); } + +int NegsLatency() { + // Estimated. + return CompareIsNanF32Latency() + 2 * Latency::BRANCH + Latency::NEG_S + + Latency::MOVF_FREG + 1 + XorLatency() + Latency::MOVT_FREG; +} + +int NegdLatency() { + // Estimated. + return CompareIsNanF64Latency() + 2 * Latency::BRANCH + Latency::NEG_D + + Latency::MOVF_HIGH_DREG + 1 + XorLatency() + Latency::MOVT_DREG; +} + +int Float64RoundLatency() { + // For ceil_l_d, floor_l_d, round_l_d, trunc_l_d latency is 4. + return Latency::MOVF_HIGH_DREG + 1 + Latency::BRANCH + Latency::MOV_D + 4 + + Latency::MOVF_HIGH_DREG + Latency::BRANCH + Latency::CVT_D_L + 2 + + Latency::MOVT_HIGH_FREG; +} + +int Float32RoundLatency() { + // For ceil_w_s, floor_w_s, round_w_s, trunc_w_s latency is 4. + return Latency::MOVF_FREG + 1 + Latency::BRANCH + Latency::MOV_S + 4 + + Latency::MOVF_FREG + Latency::BRANCH + Latency::CVT_S_W + 2 + + Latency::MOVT_FREG; +} + +int Float32MaxLatency() { + // Estimated max. + int latency = CompareIsNanF32Latency() + Latency::BRANCH; + return latency + 5 * Latency::BRANCH + 2 * CompareF32Latency() + + Latency::MOVF_FREG + 1 + Latency::MOV_S; +} + +int Float64MaxLatency() { + // Estimated max. + int latency = CompareIsNanF64Latency() + Latency::BRANCH; + return latency + 5 * Latency::BRANCH + 2 * CompareF64Latency() + + Latency::MOVF_HIGH_DREG + Latency::MOV_D; +} + +int Float32MinLatency() { + // Estimated max. + int latency = CompareIsNanF32Latency() + Latency::BRANCH; + return latency + 5 * Latency::BRANCH + 2 * CompareF32Latency() + + Latency::MOVF_FREG + 1 + Latency::MOV_S; +} + +int Float64MinLatency() { + // Estimated max. + int latency = CompareIsNanF64Latency() + Latency::BRANCH; + return latency + 5 * Latency::BRANCH + 2 * CompareF32Latency() + + Latency::MOVF_HIGH_DREG + Latency::MOV_D; +} + +int TruncLSLatency(bool load_status) { + int latency = Latency::TRUNC_L_S + Latency::MOVF_HIGH_DREG; + if (load_status) { + latency += SltuLatency() + 7; + } + return latency; +} + +int TruncLDLatency(bool load_status) { + int latency = Latency::TRUNC_L_D + Latency::MOVF_HIGH_DREG; + if (load_status) { + latency += SltuLatency() + 7; + } + return latency; +} + +int TruncUlSLatency() { + // Estimated max. + return 2 * CompareF32Latency() + CompareIsNanF32Latency() + + 4 * Latency::BRANCH + Latency::SUB_S + 2 * Latency::TRUNC_L_S + + 3 * Latency::MOVF_HIGH_DREG + OrLatency() + Latency::MOVT_FREG + + Latency::MOV_S + SltuLatency() + 4; +} + +int TruncUlDLatency() { + // Estimated max. + return 2 * CompareF64Latency() + CompareIsNanF64Latency() + + 4 * Latency::BRANCH + Latency::SUB_D + 2 * Latency::TRUNC_L_D + + 3 * Latency::MOVF_HIGH_DREG + OrLatency() + Latency::MOVT_DREG + + Latency::MOV_D + SltuLatency() + 4; +} + +int PushLatency() { return Add64Latency() + AlignedMemoryLatency(); } + +int ByteSwapSignedLatency() { return 2; } + +int LlLatency(int offset) { + bool is_one_instruction = is_int12(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 + Sub64Latency(false) + 2; } + +int ScLatency(int offset) { return 3; } + +int Word32AtomicExchangeLatency(bool sign_extend, int size) { + return Add64Latency(false) + 1 + Sub64Latency() + 2 + LlLatency(0) + + ExtractBitsLatency(sign_extend, size) + InsertBitsLatency() + + ScLatency(0) + BranchShortLatency() + 1; +} + +int Word32AtomicCompareExchangeLatency(bool sign_extend, int size) { + return 2 + Sub64Latency() + 2 + LlLatency(0) + + ExtractBitsLatency(sign_extend, size) + InsertBitsLatency() + + ScLatency(0) + BranchShortLatency() + 1; +} + +int InstructionScheduler::GetInstructionLatency(const Instruction* instr) { + // FIXME(RISCV): Verify these latencies for RISC-V (currently using MIPS + // numbers) + switch (instr->arch_opcode()) { + case kArchCallCodeObject: + case kArchCallWasmFunction: + return CallLatency(); + case kArchTailCallCodeObjectFromJSFunction: + case kArchTailCallCodeObject: { + int latency = 0; + if (instr->arch_opcode() == kArchTailCallCodeObjectFromJSFunction) { + latency = AssemblePopArgumentsAdoptFrameLatency(); + } + return latency + JumpLatency(); + } + case kArchTailCallWasm: + case kArchTailCallAddress: + return JumpLatency(); + case kArchCallJSFunction: { + int latency = 0; + if (FLAG_debug_code) { + latency = 1 + AssertLatency(); + } + return latency + 1 + Add64Latency(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 kArchAbortCSAAssert: + 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: + // Estimated max. + return AlignedMemoryLatency(); + case kArchTruncateDoubleToI: + return TruncateDoubleToIDelayedLatency(); + case kArchStoreWithWriteBarrier: + return Add64Latency() + 1 + CheckPageFlagLatency(); + case kArchStackSlot: + // Estimated max. + return Add64Latency(false) + AndLatency(false) + AssertLatency() + + Add64Latency(false) + AndLatency(false) + BranchShortLatency() + + 1 + Sub64Latency() + Add64Latency(); + case kArchWordPoisonOnSpeculation: + return AndLatency(); + 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 kIeee754Float64Log10: + case kIeee754Float64Log2: + case kIeee754Float64Pow: + case kIeee754Float64Sin: + case kIeee754Float64Sinh: + case kIeee754Float64Tan: + case kIeee754Float64Tanh: + return PrepareCallCFunctionLatency() + MovToFloatParametersLatency() + + CallCFunctionLatency() + MovFromFloatResultLatency(); + case kRiscvAdd32: + case kRiscvAdd64: + return Add64Latency(instr->InputAt(1)->IsRegister()); + case kRiscvAddOvf64: + return AddOverflow64Latency(); + case kRiscvSub32: + case kRiscvSub64: + return Sub64Latency(instr->InputAt(1)->IsRegister()); + case kRiscvSubOvf64: + return SubOverflow64Latency(); + case kRiscvMul32: + return Mul32Latency(); + case kRiscvMulOvf32: + return MulOverflow32Latency(); + case kRiscvMulHigh32: + return Mulh32Latency(); + case kRiscvMulHighU32: + return Mulhu32Latency(); + case kRiscvMulHigh64: + return Mulh64Latency(); + case kRiscvDiv32: { + int latency = Div32Latency(instr->InputAt(1)->IsRegister()); + return latency + MovzLatency(); + } + case kRiscvDivU32: { + int latency = Divu32Latency(instr->InputAt(1)->IsRegister()); + return latency + MovzLatency(); + } + case kRiscvMod32: + return Mod32Latency(); + case kRiscvModU32: + return Modu32Latency(); + case kRiscvMul64: + return Mul64Latency(); + case kRiscvDiv64: { + int latency = Div64Latency(); + return latency + MovzLatency(); + } + case kRiscvDivU64: { + int latency = Divu64Latency(); + return latency + MovzLatency(); + } + case kRiscvMod64: + return Mod64Latency(); + case kRiscvModU64: + return Modu64Latency(); + case kRiscvAnd: + return AndLatency(instr->InputAt(1)->IsRegister()); + case kRiscvAnd32: { + 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 kRiscvOr: + return OrLatency(instr->InputAt(1)->IsRegister()); + case kRiscvOr32: { + 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 kRiscvNor: + return NorLatency(instr->InputAt(1)->IsRegister()); + case kRiscvNor32: { + 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 kRiscvXor: + return XorLatency(instr->InputAt(1)->IsRegister()); + case kRiscvXor32: { + 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 kRiscvClz32: + case kRiscvClz64: + return Clz64Latency(); + case kRiscvCtz32: + return Ctz32Latency(); + case kRiscvCtz64: + return Ctz64Latency(); + case kRiscvPopcnt32: + return Popcnt32Latency(); + case kRiscvPopcnt64: + return Popcnt64Latency(); + case kRiscvShl32: + return 1; + case kRiscvShr32: + case kRiscvSar32: + case kRiscvZeroExtendWord: + return 2; + case kRiscvSignExtendWord: + case kRiscvShl64: + case kRiscvShr64: + case kRiscvSar64: + case kRiscvRor32: + case kRiscvRor64: + return 1; + case kRiscvTst: + return AndLatency(instr->InputAt(1)->IsRegister()); + case kRiscvMov: + return 1; + case kRiscvCmpS: + return MoveLatency() + CompareF32Latency(); + case kRiscvAddS: + return Latency::ADD_S; + case kRiscvSubS: + return Latency::SUB_S; + case kRiscvMulS: + return Latency::MUL_S; + case kRiscvDivS: + return Latency::DIV_S; + case kRiscvModS: + return PrepareCallCFunctionLatency() + MovToFloatParametersLatency() + + CallCFunctionLatency() + MovFromFloatResultLatency(); + case kRiscvAbsS: + return Latency::ABS_S; + case kRiscvNegS: + return NegdLatency(); + case kRiscvSqrtS: + return Latency::SQRT_S; + case kRiscvMaxS: + return Latency::MAX_S; + case kRiscvMinS: + return Latency::MIN_S; + case kRiscvCmpD: + return MoveLatency() + CompareF64Latency(); + case kRiscvAddD: + return Latency::ADD_D; + case kRiscvSubD: + return Latency::SUB_D; + case kRiscvMulD: + return Latency::MUL_D; + case kRiscvDivD: + return Latency::DIV_D; + case kRiscvModD: + return PrepareCallCFunctionLatency() + MovToFloatParametersLatency() + + CallCFunctionLatency() + MovFromFloatResultLatency(); + case kRiscvAbsD: + return Latency::ABS_D; + case kRiscvNegD: + return NegdLatency(); + case kRiscvSqrtD: + return Latency::SQRT_D; + case kRiscvMaxD: + return Latency::MAX_D; + case kRiscvMinD: + return Latency::MIN_D; + case kRiscvFloat64RoundDown: + case kRiscvFloat64RoundTruncate: + case kRiscvFloat64RoundUp: + case kRiscvFloat64RoundTiesEven: + return Float64RoundLatency(); + case kRiscvFloat32RoundDown: + case kRiscvFloat32RoundTruncate: + case kRiscvFloat32RoundUp: + case kRiscvFloat32RoundTiesEven: + return Float32RoundLatency(); + case kRiscvFloat32Max: + return Float32MaxLatency(); + case kRiscvFloat64Max: + return Float64MaxLatency(); + case kRiscvFloat32Min: + return Float32MinLatency(); + case kRiscvFloat64Min: + return Float64MinLatency(); + case kRiscvFloat64SilenceNaN: + return Latency::SUB_D; + case kRiscvCvtSD: + return Latency::CVT_S_D; + case kRiscvCvtDS: + return Latency::CVT_D_S; + case kRiscvCvtDW: + return Latency::MOVT_FREG + Latency::CVT_D_W; + case kRiscvCvtSW: + return Latency::MOVT_FREG + Latency::CVT_S_W; + case kRiscvCvtSUw: + return 1 + Latency::MOVT_DREG + Latency::CVT_S_L; + case kRiscvCvtSL: + return Latency::MOVT_DREG + Latency::CVT_S_L; + case kRiscvCvtDL: + return Latency::MOVT_DREG + Latency::CVT_D_L; + case kRiscvCvtDUw: + return 1 + Latency::MOVT_DREG + Latency::CVT_D_L; + case kRiscvCvtDUl: + return 2 * Latency::BRANCH + 3 + 2 * Latency::MOVT_DREG + + 2 * Latency::CVT_D_L + Latency::ADD_D; + case kRiscvCvtSUl: + return 2 * Latency::BRANCH + 3 + 2 * Latency::MOVT_DREG + + 2 * Latency::CVT_S_L + Latency::ADD_S; + case kRiscvFloorWD: + return Latency::FLOOR_W_D + Latency::MOVF_FREG; + case kRiscvCeilWD: + return Latency::CEIL_W_D + Latency::MOVF_FREG; + case kRiscvRoundWD: + return Latency::ROUND_W_D + Latency::MOVF_FREG; + case kRiscvTruncWD: + return Latency::TRUNC_W_D + Latency::MOVF_FREG; + case kRiscvFloorWS: + return Latency::FLOOR_W_S + Latency::MOVF_FREG; + case kRiscvCeilWS: + return Latency::CEIL_W_S + Latency::MOVF_FREG; + case kRiscvRoundWS: + return Latency::ROUND_W_S + Latency::MOVF_FREG; + case kRiscvTruncWS: + return Latency::TRUNC_W_S + Latency::MOVF_FREG + 2 + MovnLatency(); + case kRiscvTruncLS: + return TruncLSLatency(instr->OutputCount() > 1); + case kRiscvTruncLD: + return TruncLDLatency(instr->OutputCount() > 1); + case kRiscvTruncUwD: + // Estimated max. + return CompareF64Latency() + 2 * Latency::BRANCH + + 2 * Latency::TRUNC_W_D + Latency::SUB_D + OrLatency() + + Latency::MOVT_FREG + Latency::MOVF_FREG + Latency::MOVT_HIGH_FREG + + 1; + case kRiscvTruncUwS: + // Estimated max. + return CompareF32Latency() + 2 * Latency::BRANCH + + 2 * Latency::TRUNC_W_S + Latency::SUB_S + OrLatency() + + Latency::MOVT_FREG + 2 * Latency::MOVF_FREG + 2 + MovzLatency(); + case kRiscvTruncUlS: + return TruncUlSLatency(); + case kRiscvTruncUlD: + return TruncUlDLatency(); + case kRiscvBitcastDL: + return Latency::MOVF_HIGH_DREG; + case kRiscvBitcastLD: + return Latency::MOVT_DREG; + case kRiscvFloat64ExtractLowWord32: + return Latency::MOVF_FREG; + case kRiscvFloat64InsertLowWord32: + return Latency::MOVF_HIGH_FREG + Latency::MOVT_FREG + + Latency::MOVT_HIGH_FREG; + case kRiscvFloat64ExtractHighWord32: + return Latency::MOVF_HIGH_FREG; + case kRiscvFloat64InsertHighWord32: + return Latency::MOVT_HIGH_FREG; + case kRiscvSignExtendByte: + case kRiscvSignExtendShort: + return 1; + case kRiscvLbu: + case kRiscvLb: + case kRiscvLhu: + case kRiscvLh: + case kRiscvLwu: + case kRiscvLw: + case kRiscvLd: + case kRiscvSb: + case kRiscvSh: + case kRiscvSw: + case kRiscvSd: + return AlignedMemoryLatency(); + case kRiscvLoadFloat: + return ULoadFloatLatency(); + case kRiscvLoadDouble: + return LoadDoubleLatency(); + case kRiscvStoreFloat: + return StoreFloatLatency(); + case kRiscvStoreDouble: + return StoreDoubleLatency(); + case kRiscvUlhu: + case kRiscvUlh: + return UlhuLatency(); + case kRiscvUlwu: + return UlwuLatency(); + case kRiscvUlw: + return UlwLatency(); + case kRiscvUld: + return UldLatency(); + case kRiscvULoadFloat: + return ULoadFloatLatency(); + case kRiscvULoadDouble: + return ULoadDoubleLatency(); + case kRiscvUsh: + return UshLatency(); + case kRiscvUsw: + return UswLatency(); + case kRiscvUsd: + return UsdLatency(); + case kRiscvUStoreFloat: + return UStoreFloatLatency(); + case kRiscvUStoreDouble: + return UStoreDoubleLatency(); + case kRiscvPush: { + int latency = 0; + if (instr->InputAt(0)->IsFPRegister()) { + latency = StoreDoubleLatency() + Sub64Latency(false); + } else { + latency = PushLatency(); + } + return latency; + } + case kRiscvPeek: { + int latency = 0; + if (instr->OutputAt(0)->IsFPRegister()) { + auto op = LocationOperand::cast(instr->OutputAt(0)); + switch (op->representation()) { + case MachineRepresentation::kFloat64: + latency = LoadDoubleLatency(); + break; + case MachineRepresentation::kFloat32: + latency = Latency::LOAD_FLOAT; + break; + default: + UNREACHABLE(); + } + } else { + latency = AlignedMemoryLatency(); + } + return latency; + } + case kRiscvStackClaim: + return Sub64Latency(false); + case kRiscvStoreToStackSlot: { + int latency = 0; + if (instr->InputAt(0)->IsFPRegister()) { + if (instr->InputAt(0)->IsSimd128Register()) { + latency = 1; // Estimated value. + } else { + latency = StoreDoubleLatency(); + } + } else { + latency = AlignedMemoryLatency(); + } + return latency; + } + case kRiscvByteSwap64: + return ByteSwapSignedLatency(); + case kRiscvByteSwap32: + return ByteSwapSignedLatency(); + case kWord32AtomicLoadInt8: + case kWord32AtomicLoadUint8: + case kWord32AtomicLoadInt16: + case kWord32AtomicLoadUint16: + case kWord32AtomicLoadWord32: + return 2; + case kWord32AtomicStoreWord8: + case kWord32AtomicStoreWord16: + case kWord32AtomicStoreWord32: + return 3; + case kWord32AtomicExchangeInt8: + return Word32AtomicExchangeLatency(true, 8); + case kWord32AtomicExchangeUint8: + return Word32AtomicExchangeLatency(false, 8); + case kWord32AtomicExchangeInt16: + return Word32AtomicExchangeLatency(true, 16); + case kWord32AtomicExchangeUint16: + return Word32AtomicExchangeLatency(false, 16); + case kWord32AtomicExchangeWord32: + return 2 + LlLatency(0) + 1 + ScLatency(0) + BranchShortLatency() + 1; + case kWord32AtomicCompareExchangeInt8: + return Word32AtomicCompareExchangeLatency(true, 8); + case kWord32AtomicCompareExchangeUint8: + return Word32AtomicCompareExchangeLatency(false, 8); + case kWord32AtomicCompareExchangeInt16: + return Word32AtomicCompareExchangeLatency(true, 16); + case kWord32AtomicCompareExchangeUint16: + return Word32AtomicCompareExchangeLatency(false, 16); + case kWord32AtomicCompareExchangeWord32: + return 3 + LlLatency(0) + BranchShortLatency() + 1 + ScLatency(0) + + BranchShortLatency() + 1; + case kRiscvAssertEqual: + return AssertLatency(); + default: + return 1; + } +} + +} // namespace compiler +} // namespace internal +} // namespace v8 Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/compiler/backend/riscv64/instruction-selector-riscv64.cc =================================================================== --- /dev/null +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/compiler/backend/riscv64/instruction-selector-riscv64.cc @@ -0,0 +1,2985 @@ +// 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/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 RISC-V-specific methods for generating InstructionOperands. +class RiscvOperandGenerator final : public OperandGenerator { + public: + explicit RiscvOperandGenerator(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 kRiscvShl32: + case kRiscvSar32: + case kRiscvShr32: + return is_uint5(value); + case kRiscvShl64: + case kRiscvSar64: + case kRiscvShr64: + return is_uint6(value); + case kRiscvAdd32: + case kRiscvAnd32: + case kRiscvAnd: + case kRiscvAdd64: + case kRiscvOr32: + case kRiscvOr: + case kRiscvTst: + case kRiscvXor: + return is_int12(value); + case kRiscvLb: + case kRiscvLbu: + case kRiscvSb: + case kRiscvLh: + case kRiscvLhu: + case kRiscvSh: + case kRiscvLw: + case kRiscvSw: + case kRiscvLd: + case kRiscvSd: + case kRiscvLoadFloat: + case kRiscvStoreFloat: + case kRiscvLoadDouble: + case kRiscvStoreDouble: + return is_int32(value); + default: + return is_int12(value); + } + } + + private: + bool ImmediateFitsAddrMode1Instruction(int32_t imm) const { + TRACE_UNIMPL(); + return false; + } +}; + +static void VisitRR(InstructionSelector* selector, ArchOpcode opcode, + Node* node) { + RiscvOperandGenerator g(selector); + selector->Emit(opcode, g.DefineAsRegister(node), + g.UseRegister(node->InputAt(0))); +} + +static void VisitRRI(InstructionSelector* selector, ArchOpcode opcode, + Node* node) { + RiscvOperandGenerator 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) { + RiscvOperandGenerator 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) { + RiscvOperandGenerator 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) { + RiscvOperandGenerator 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) { + RiscvOperandGenerator 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) { + RiscvOperandGenerator 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) { + RiscvOperandGenerator 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; + } + + RiscvOperandGenerator g(selector_); + Node* load = m.left().node(); + Node* offset = load->InputAt(1); + base_ = load->InputAt(0); + opcode_ = kRiscvLw; + if (g.CanBeImmediate(offset, opcode_)) { +#if defined(V8_TARGET_LITTLE_ENDIAN) + immediate_ = g.GetIntegerConstantValue(offset) + 4; +#elif defined(V8_TARGET_BIG_ENDIAN) + immediate_ = g.GetIntegerConstantValue(offset); +#endif + matches_ = g.CanBeImmediate(immediate_, kRiscvLw); + } + } + } +}; + +bool TryEmitExtendingLoad(InstructionSelector* selector, Node* node, + Node* output_node) { + ExtendingLoadMatcher m(node, selector); + RiscvOperandGenerator 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) { + RiscvOperandGenerator 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) { + RiscvOperandGenerator 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); + } + + if (cont->IsDeoptimize()) { + // If we can deoptimize as a result of the binop, we need to make sure that + // the deopt inputs are not overwritten by the binop result. One way + // to achieve that is to declare the output register as same-as-first. + outputs[output_count++] = g.DefineSameAsFirst(node); + } else { + 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)), + sequence()->AddImmediate(Constant(alignment)), 0, nullptr); +} + +void InstructionSelector::VisitAbortCSAAssert(Node* node) { + RiscvOperandGenerator g(this); + Emit(kArchAbortCSAAssert, g.NoOutput(), g.UseFixed(node->InputAt(0), a0)); +} + +void EmitLoad(InstructionSelector* selector, Node* node, InstructionCode opcode, + Node* output = nullptr) { + RiscvOperandGenerator 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(kRiscvAdd64 | AddressingModeField::encode(kMode_None), + addr_reg, g.UseRegister(index), g.UseRegister(base)); + // 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)); + } +} + +void InstructionSelector::VisitLoadTransform(Node* node) { + LoadTransformParameters params = LoadTransformParametersOf(node->op()); + + InstructionCode opcode = kArchNop; + switch (params.transformation) { + case LoadTransformation::kS8x16LoadSplat: + opcode = kRiscvS8x16LoadSplat; + break; + case LoadTransformation::kS16x8LoadSplat: + opcode = kRiscvS16x8LoadSplat; + break; + case LoadTransformation::kS32x4LoadSplat: + opcode = kRiscvS32x4LoadSplat; + break; + case LoadTransformation::kS64x2LoadSplat: + opcode = kRiscvS64x2LoadSplat; + break; + case LoadTransformation::kI16x8Load8x8S: + opcode = kRiscvI16x8Load8x8S; + break; + case LoadTransformation::kI16x8Load8x8U: + opcode = kRiscvI16x8Load8x8U; + break; + case LoadTransformation::kI32x4Load16x4S: + opcode = kRiscvI32x4Load16x4S; + break; + case LoadTransformation::kI32x4Load16x4U: + opcode = kRiscvI32x4Load16x4U; + break; + case LoadTransformation::kI64x2Load32x2S: + opcode = kRiscvI64x2Load32x2S; + break; + case LoadTransformation::kI64x2Load32x2U: + opcode = kRiscvI64x2Load32x2U; + break; + 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 = kRiscvLoadFloat; + break; + case MachineRepresentation::kFloat64: + opcode = kRiscvLoadDouble; + break; + case MachineRepresentation::kBit: // Fall through. + case MachineRepresentation::kWord8: + opcode = load_rep.IsUnsigned() ? kRiscvLbu : kRiscvLb; + break; + case MachineRepresentation::kWord16: + opcode = load_rep.IsUnsigned() ? kRiscvLhu : kRiscvLh; + break; + case MachineRepresentation::kWord32: + opcode = load_rep.IsUnsigned() ? kRiscvLwu : kRiscvLw; + break; + case MachineRepresentation::kTaggedSigned: // Fall through. + case MachineRepresentation::kTaggedPointer: // Fall through. + case MachineRepresentation::kTagged: // Fall through. + case MachineRepresentation::kWord64: + opcode = kRiscvLd; + break; + case MachineRepresentation::kSimd128: + opcode = kRiscvMsaLd; + break; + case MachineRepresentation::kCompressedPointer: // Fall through. + case MachineRepresentation::kCompressed: // Fall through. + case MachineRepresentation::kNone: + UNREACHABLE(); + } + if (node->opcode() == IrOpcode::kPoisonedLoad) { + CHECK_NE(poisoning_level_, PoisoningMitigationLevel::kDontPoison); + opcode |= MiscField::encode(kMemoryAccessPoisoned); + } + + EmitLoad(this, node, opcode); +} + +void InstructionSelector::VisitPoisonedLoad(Node* node) { VisitLoad(node); } + +void InstructionSelector::VisitProtectedLoad(Node* node) { + // TODO(eholk) + UNIMPLEMENTED(); +} + +void InstructionSelector::VisitStore(Node* node) { + RiscvOperandGenerator 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(); + + // TODO(riscv): I guess this could be done in a better way. + if (write_barrier_kind != kNoWriteBarrier && + V8_LIKELY(!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 = kArchNop; + switch (rep) { + case MachineRepresentation::kFloat32: + opcode = kRiscvStoreFloat; + break; + case MachineRepresentation::kFloat64: + opcode = kRiscvStoreDouble; + break; + case MachineRepresentation::kBit: // Fall through. + case MachineRepresentation::kWord8: + opcode = kRiscvSb; + break; + case MachineRepresentation::kWord16: + opcode = kRiscvSh; + break; + case MachineRepresentation::kWord32: + opcode = kRiscvSw; + break; + case MachineRepresentation::kTaggedSigned: // Fall through. + case MachineRepresentation::kTaggedPointer: // Fall through. + case MachineRepresentation::kTagged: // Fall through. + case MachineRepresentation::kWord64: + opcode = kRiscvSd; + break; + case MachineRepresentation::kSimd128: + opcode = kRiscvMsaSt; + break; + case MachineRepresentation::kCompressedPointer: // Fall through. + case MachineRepresentation::kCompressed: // Fall through. + case MachineRepresentation::kNone: + UNREACHABLE(); + return; + } + + 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(kRiscvAdd64 | 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, kRiscvAnd32, true, kRiscvAnd32); +} + +void InstructionSelector::VisitWord64And(Node* node) { + RiscvOperandGenerator g(this); + Int64BinopMatcher m(node); + if (m.left().IsWord64Shr() && CanCover(node, m.left().node()) && + m.right().HasValue()) { + uint64_t mask = m.right().Value(); + 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)) { + // The mask must be contiguous, and occupy the least-significant bits. + DCHECK_EQ(0u, base::bits::CountTrailingZeros64(mask)); + + // Select Dext for And(Shr(x, imm), mask) where the mask is in the least + // significant bits. + Int64BinopMatcher mleft(m.left().node()); + if (mleft.right().HasValue()) { + // Any shift value can match; int64 shifts use `value % 64`. + uint32_t lsb = static_cast(mleft.right().Value() & 0x3F); + + // Dext cannot extract bits past the register size, however since + // shifting the original value would have introduced some zeros we can + // still use Dext with a smaller mask and the remaining bits will be + // zeros. + if (lsb + mask_width > 64) mask_width = 64 - lsb; + + if (lsb == 0 && mask_width == 64) { + Emit(kArchNop, g.DefineSameAsFirst(node), g.Use(mleft.left().node())); + return; + } + } + // Other cases fall through to the normal And operation. + } + } + VisitBinop(this, node, kRiscvAnd, true, kRiscvAnd); +} + +void InstructionSelector::VisitWord32Or(Node* node) { + VisitBinop(this, node, kRiscvOr32, true, kRiscvOr32); +} + +void InstructionSelector::VisitWord64Or(Node* node) { + VisitBinop(this, node, kRiscvOr, true, kRiscvOr); +} + +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().HasValue()) { + RiscvOperandGenerator g(this); + Emit(kRiscvNor32, 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. + RiscvOperandGenerator g(this); + Emit(kRiscvNor32, g.DefineAsRegister(node), g.UseRegister(m.left().node()), + g.TempImmediate(0)); + return; + } + VisitBinop(this, node, kRiscvXor32, true, kRiscvXor32); +} + +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().HasValue()) { + RiscvOperandGenerator g(this); + Emit(kRiscvNor, 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. + RiscvOperandGenerator g(this); + Emit(kRiscvNor, g.DefineAsRegister(node), g.UseRegister(m.left().node()), + g.TempImmediate(0)); + return; + } + VisitBinop(this, node, kRiscvXor, true, kRiscvXor); +} + +void InstructionSelector::VisitWord32Shl(Node* node) { + Int32BinopMatcher m(node); + if (m.left().IsWord32And() && CanCover(node, m.left().node()) && + m.right().IsInRange(1, 31)) { + RiscvOperandGenerator 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().HasValue()) { + uint32_t mask = mleft.right().Value(); + 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().Value(); + 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(kRiscvShl32, g.DefineAsRegister(node), + g.UseRegister(mleft.left().node()), + g.UseImmediate(m.right().node())); + return; + } + } + } + } + VisitRRO(this, kRiscvShl32, node); +} + +void InstructionSelector::VisitWord32Shr(Node* node) { + VisitRRO(this, kRiscvShr32, 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().HasValue() && mleft.right().HasValue()) { + RiscvOperandGenerator g(this); + uint32_t sar = m.right().Value(); + uint32_t shl = mleft.right().Value(); + if ((sar == shl) && (sar == 16)) { + Emit(kRiscvSignExtendShort, g.DefineAsRegister(node), + g.UseRegister(mleft.left().node())); + return; + } else if ((sar == shl) && (sar == 24)) { + Emit(kRiscvSignExtendByte, g.DefineAsRegister(node), + g.UseRegister(mleft.left().node())); + return; + } else if ((sar == shl) && (sar == 32)) { + Emit(kRiscvShl32, g.DefineAsRegister(node), + g.UseRegister(mleft.left().node()), g.TempImmediate(0)); + return; + } + } + } + VisitRRO(this, kRiscvSar32, node); +} + +void InstructionSelector::VisitWord64Shl(Node* node) { + RiscvOperandGenerator 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(kRiscvShl64, g.DefineSameAsFirst(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 Dshl where the mask is + // contiguous, and the shift immediate non-zero. + Int64BinopMatcher mleft(m.left().node()); + if (mleft.right().HasValue()) { + uint64_t mask = mleft.right().Value(); + 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().Value(); + 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(kRiscvShl64, g.DefineAsRegister(node), + g.UseRegister(mleft.left().node()), + g.UseImmediate(m.right().node())); + return; + } + } + } + } + VisitRRO(this, kRiscvShl64, node); +} + +void InstructionSelector::VisitWord64Shr(Node* node) { + VisitRRO(this, kRiscvShr64, node); +} + +void InstructionSelector::VisitWord64Sar(Node* node) { + if (TryEmitExtendingLoad(this, node, node)) return; + VisitRRO(this, kRiscvSar64, node); +} + +void InstructionSelector::VisitWord32Rol(Node* node) { UNREACHABLE(); } + +void InstructionSelector::VisitWord64Rol(Node* node) { UNREACHABLE(); } + +void InstructionSelector::VisitWord32Ror(Node* node) { + VisitRRO(this, kRiscvRor32, node); +} + +void InstructionSelector::VisitWord32Clz(Node* node) { + VisitRR(this, kRiscvClz32, node); +} + +void InstructionSelector::VisitWord32ReverseBits(Node* node) { UNREACHABLE(); } + +void InstructionSelector::VisitWord64ReverseBits(Node* node) { UNREACHABLE(); } + +void InstructionSelector::VisitWord64ReverseBytes(Node* node) { + RiscvOperandGenerator g(this); + Emit(kRiscvByteSwap64, g.DefineAsRegister(node), + g.UseRegister(node->InputAt(0))); +} + +void InstructionSelector::VisitWord32ReverseBytes(Node* node) { + RiscvOperandGenerator g(this); + Emit(kRiscvByteSwap32, g.DefineAsRegister(node), + g.UseRegister(node->InputAt(0))); +} + +void InstructionSelector::VisitSimd128ReverseBytes(Node* node) { + UNREACHABLE(); +} + +void InstructionSelector::VisitWord32Ctz(Node* node) { + RiscvOperandGenerator g(this); + Emit(kRiscvCtz32, g.DefineAsRegister(node), g.UseRegister(node->InputAt(0))); +} + +void InstructionSelector::VisitWord64Ctz(Node* node) { + RiscvOperandGenerator g(this); + Emit(kRiscvCtz64, g.DefineAsRegister(node), g.UseRegister(node->InputAt(0))); +} + +void InstructionSelector::VisitWord32Popcnt(Node* node) { + RiscvOperandGenerator g(this); + Emit(kRiscvPopcnt32, g.DefineAsRegister(node), + g.UseRegister(node->InputAt(0))); +} + +void InstructionSelector::VisitWord64Popcnt(Node* node) { + RiscvOperandGenerator g(this); + Emit(kRiscvPopcnt64, g.DefineAsRegister(node), + g.UseRegister(node->InputAt(0))); +} + +void InstructionSelector::VisitWord64Ror(Node* node) { + VisitRRO(this, kRiscvRor64, node); +} + +void InstructionSelector::VisitWord64Clz(Node* node) { + VisitRR(this, kRiscvClz64, node); +} + +void InstructionSelector::VisitInt32Add(Node* node) { + VisitBinop(this, node, kRiscvAdd32, true, kRiscvAdd32); +} + +void InstructionSelector::VisitInt64Add(Node* node) { + VisitBinop(this, node, kRiscvAdd64, true, kRiscvAdd64); +} + +void InstructionSelector::VisitInt32Sub(Node* node) { + VisitBinop(this, node, kRiscvSub32); +} + +void InstructionSelector::VisitInt64Sub(Node* node) { + VisitBinop(this, node, kRiscvSub64); +} + +void InstructionSelector::VisitInt32Mul(Node* node) { + RiscvOperandGenerator g(this); + Int32BinopMatcher m(node); + if (m.right().HasValue() && m.right().Value() > 0) { + uint32_t value = static_cast(m.right().Value()); + if (base::bits::IsPowerOfTwo(value)) { + Emit(kRiscvShl32 | 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)) { + InstructionOperand temp = g.TempRegister(); + Emit(kRiscvShl32 | AddressingModeField::encode(kMode_None), temp, + g.UseRegister(m.left().node()), + g.TempImmediate(base::bits::WhichPowerOfTwo(value + 1))); + Emit(kRiscvSub32 | 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(kRiscvMulHigh64, g.DefineSameAsFirst(node), + g.UseRegister(leftInput.left().node()), + g.UseRegister(rightInput.left().node())); + return; + } + } + } + VisitRRR(this, kRiscvMul32, node); +} + +void InstructionSelector::VisitInt32MulHigh(Node* node) { + VisitRRR(this, kRiscvMulHigh32, node); +} + +void InstructionSelector::VisitUint32MulHigh(Node* node) { + VisitRRR(this, kRiscvMulHighU32, node); +} + +void InstructionSelector::VisitInt64Mul(Node* node) { + RiscvOperandGenerator g(this); + Int64BinopMatcher m(node); + // TODO(dusmil): Add optimization for shifts larger than 32. + if (m.right().HasValue() && m.right().Value() > 0) { + uint32_t value = static_cast(m.right().Value()); + if (base::bits::IsPowerOfTwo(value)) { + Emit(kRiscvShl64 | 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)) { + InstructionOperand temp = g.TempRegister(); + Emit(kRiscvShl64 | AddressingModeField::encode(kMode_None), temp, + g.UseRegister(m.left().node()), + g.TempImmediate(base::bits::WhichPowerOfTwo(value + 1))); + Emit(kRiscvSub64 | AddressingModeField::encode(kMode_None), + g.DefineAsRegister(node), temp, g.UseRegister(m.left().node())); + return; + } + } + Emit(kRiscvMul64, g.DefineAsRegister(node), g.UseRegister(m.left().node()), + g.UseRegister(m.right().node())); +} + +void InstructionSelector::VisitInt32Div(Node* node) { + RiscvOperandGenerator 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(kRiscvDiv64, g.DefineSameAsFirst(node), + g.UseRegister(leftInput.left().node()), + g.UseRegister(rightInput.left().node())); + return; + } + } + } + Emit(kRiscvDiv32, g.DefineSameAsFirst(node), g.UseRegister(m.left().node()), + g.UseRegister(m.right().node())); +} + +void InstructionSelector::VisitUint32Div(Node* node) { + RiscvOperandGenerator g(this); + Int32BinopMatcher m(node); + Emit(kRiscvDivU32, g.DefineSameAsFirst(node), g.UseRegister(m.left().node()), + g.UseRegister(m.right().node())); +} + +void InstructionSelector::VisitInt32Mod(Node* node) { + RiscvOperandGenerator 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(kRiscvMod64, g.DefineSameAsFirst(node), + g.UseRegister(leftInput.left().node()), + g.UseRegister(rightInput.left().node())); + return; + } + } + } + Emit(kRiscvMod32, g.DefineAsRegister(node), g.UseRegister(m.left().node()), + g.UseRegister(m.right().node())); +} + +void InstructionSelector::VisitUint32Mod(Node* node) { + RiscvOperandGenerator g(this); + Int32BinopMatcher m(node); + Emit(kRiscvModU32, g.DefineAsRegister(node), g.UseRegister(m.left().node()), + g.UseRegister(m.right().node())); +} + +void InstructionSelector::VisitInt64Div(Node* node) { + RiscvOperandGenerator g(this); + Int64BinopMatcher m(node); + Emit(kRiscvDiv64, g.DefineSameAsFirst(node), g.UseRegister(m.left().node()), + g.UseRegister(m.right().node())); +} + +void InstructionSelector::VisitUint64Div(Node* node) { + RiscvOperandGenerator g(this); + Int64BinopMatcher m(node); + Emit(kRiscvDivU64, g.DefineSameAsFirst(node), g.UseRegister(m.left().node()), + g.UseRegister(m.right().node())); +} + +void InstructionSelector::VisitInt64Mod(Node* node) { + RiscvOperandGenerator g(this); + Int64BinopMatcher m(node); + Emit(kRiscvMod64, g.DefineAsRegister(node), g.UseRegister(m.left().node()), + g.UseRegister(m.right().node())); +} + +void InstructionSelector::VisitUint64Mod(Node* node) { + RiscvOperandGenerator g(this); + Int64BinopMatcher m(node); + Emit(kRiscvModU64, g.DefineAsRegister(node), g.UseRegister(m.left().node()), + g.UseRegister(m.right().node())); +} + +void InstructionSelector::VisitChangeFloat32ToFloat64(Node* node) { + VisitRR(this, kRiscvCvtDS, node); +} + +void InstructionSelector::VisitRoundInt32ToFloat32(Node* node) { + VisitRR(this, kRiscvCvtSW, node); +} + +void InstructionSelector::VisitRoundUint32ToFloat32(Node* node) { + VisitRR(this, kRiscvCvtSUw, node); +} + +void InstructionSelector::VisitChangeInt32ToFloat64(Node* node) { + VisitRR(this, kRiscvCvtDW, node); +} + +void InstructionSelector::VisitChangeInt64ToFloat64(Node* node) { + VisitRR(this, kRiscvCvtDL, node); +} + +void InstructionSelector::VisitChangeUint32ToFloat64(Node* node) { + VisitRR(this, kRiscvCvtDUw, node); +} + +void InstructionSelector::VisitTruncateFloat32ToInt32(Node* node) { + VisitRR(this, kRiscvTruncWS, node); +} + +void InstructionSelector::VisitTruncateFloat32ToUint32(Node* node) { + VisitRR(this, kRiscvTruncUwS, node); +} + +void InstructionSelector::VisitChangeFloat64ToInt32(Node* node) { + RiscvOperandGenerator 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(kRiscvFloorWD, g.DefineAsRegister(node), + g.UseRegister(value->InputAt(0))); + return; + case IrOpcode::kFloat64RoundUp: + Emit(kRiscvCeilWD, g.DefineAsRegister(node), + g.UseRegister(value->InputAt(0))); + return; + case IrOpcode::kFloat64RoundTiesEven: + Emit(kRiscvRoundWD, g.DefineAsRegister(node), + g.UseRegister(value->InputAt(0))); + return; + case IrOpcode::kFloat64RoundTruncate: + Emit(kRiscvTruncWD, 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(kRiscvFloorWS, g.DefineAsRegister(node), + g.UseRegister(next->InputAt(0))); + return; + case IrOpcode::kFloat32RoundUp: + Emit(kRiscvCeilWS, g.DefineAsRegister(node), + g.UseRegister(next->InputAt(0))); + return; + case IrOpcode::kFloat32RoundTiesEven: + Emit(kRiscvRoundWS, g.DefineAsRegister(node), + g.UseRegister(next->InputAt(0))); + return; + case IrOpcode::kFloat32RoundTruncate: + Emit(kRiscvTruncWS, g.DefineAsRegister(node), + g.UseRegister(next->InputAt(0))); + return; + default: + Emit(kRiscvTruncWS, g.DefineAsRegister(node), + g.UseRegister(value->InputAt(0))); + return; + } + } else { + // Match float32 -> float64 -> int32 representation change path. + Emit(kRiscvTruncWS, g.DefineAsRegister(node), + g.UseRegister(value->InputAt(0))); + return; + } + } + } + VisitRR(this, kRiscvTruncWD, node); +} + +void InstructionSelector::VisitChangeFloat64ToInt64(Node* node) { + VisitRR(this, kRiscvTruncLD, node); +} + +void InstructionSelector::VisitChangeFloat64ToUint32(Node* node) { + VisitRR(this, kRiscvTruncUwD, node); +} + +void InstructionSelector::VisitChangeFloat64ToUint64(Node* node) { + VisitRR(this, kRiscvTruncUlD, node); +} + +void InstructionSelector::VisitTruncateFloat64ToUint32(Node* node) { + VisitRR(this, kRiscvTruncUwD, node); +} + +void InstructionSelector::VisitTruncateFloat64ToInt64(Node* node) { + VisitRR(this, kRiscvTruncLD, node); +} + +void InstructionSelector::VisitTryTruncateFloat32ToInt64(Node* node) { + RiscvOperandGenerator 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(kRiscvTruncLS, output_count, outputs, 1, inputs); +} + +void InstructionSelector::VisitTryTruncateFloat64ToInt64(Node* node) { + RiscvOperandGenerator 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(kRiscvTruncLD, output_count, outputs, 1, inputs); +} + +void InstructionSelector::VisitTryTruncateFloat32ToUint64(Node* node) { + RiscvOperandGenerator 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(kRiscvTruncUlS, output_count, outputs, 1, inputs); +} + +void InstructionSelector::VisitTryTruncateFloat64ToUint64(Node* node) { + RiscvOperandGenerator 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(kRiscvTruncUlD, output_count, outputs, 1, inputs); +} + +void InstructionSelector::VisitBitcastWord32ToWord64(Node* node) { + UNIMPLEMENTED(); +} + +void InstructionSelector::VisitChangeInt32ToInt64(Node* node) { + Node* value = node->InputAt(0); + if (value->opcode() == IrOpcode::kLoad && CanCover(node, value)) { + // Generate sign-extending load. + LoadRepresentation load_rep = LoadRepresentationOf(value->op()); + InstructionCode opcode = kArchNop; + switch (load_rep.representation()) { + case MachineRepresentation::kBit: // Fall through. + case MachineRepresentation::kWord8: + opcode = load_rep.IsUnsigned() ? kRiscvLbu : kRiscvLb; + break; + case MachineRepresentation::kWord16: + opcode = load_rep.IsUnsigned() ? kRiscvLhu : kRiscvLh; + break; + case MachineRepresentation::kWord32: + opcode = kRiscvLw; + break; + default: + UNREACHABLE(); + return; + } + EmitLoad(this, value, opcode, node); + } else { + RiscvOperandGenerator g(this); + Emit(kRiscvShl32, g.DefineAsRegister(node), g.UseRegister(node->InputAt(0)), + g.TempImmediate(0)); + } +} + +void InstructionSelector::VisitChangeUint32ToUint64(Node* node) { + RiscvOperandGenerator g(this); + Node* value = node->InputAt(0); + switch (value->opcode()) { + case IrOpcode::kLoad: { + LoadRepresentation load_rep = LoadRepresentationOf(value->op()); + if (load_rep.IsUnsigned()) { + switch (load_rep.representation()) { + case MachineRepresentation::kWord8: + case MachineRepresentation::kWord16: + case MachineRepresentation::kWord32: + Emit(kArchNop, g.DefineSameAsFirst(node), g.Use(value)); + return; + default: + break; + } + } + break; + } + default: + break; + } + Emit(kRiscvZeroExtendWord, g.DefineAsRegister(node), + g.UseRegister(node->InputAt(0))); +} + +void InstructionSelector::VisitTruncateInt64ToInt32(Node* node) { + RiscvOperandGenerator 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(kRiscvSar64, g.DefineSameAsFirst(node), + g.UseRegister(m.left().node()), + g.UseImmediate(m.right().node())); + return; + } + } + break; + } + default: + break; + } + } + + // Semantics of this machine IR is not clear. For example, x86 zero-extend the + // truncated value; arm treats it as nop thus the upper 32-bit as undefined; + // mips emits ext instruction which zero-extend the 32-bit value; for riscv, + // we do sign-extension of the truncated value + Emit(kRiscvSignExtendWord, g.DefineAsRegister(node), + g.UseRegister(node->InputAt(0))); +} + +void InstructionSelector::VisitTruncateFloat64ToFloat32(Node* node) { + RiscvOperandGenerator g(this); + Node* value = node->InputAt(0); + // Match TruncateFloat64ToFloat32(ChangeInt32ToFloat64) to corresponding + // instruction. + if (CanCover(node, value) && + value->opcode() == IrOpcode::kChangeInt32ToFloat64) { + Emit(kRiscvCvtSW, g.DefineAsRegister(node), + g.UseRegister(value->InputAt(0))); + return; + } + VisitRR(this, kRiscvCvtSD, node); +} + +void InstructionSelector::VisitTruncateFloat64ToWord32(Node* node) { + VisitRR(this, kArchTruncateDoubleToI, node); +} + +void InstructionSelector::VisitRoundFloat64ToInt32(Node* node) { + VisitRR(this, kRiscvTruncWD, node); +} + +void InstructionSelector::VisitRoundInt64ToFloat32(Node* node) { + VisitRR(this, kRiscvCvtSL, node); +} + +void InstructionSelector::VisitRoundInt64ToFloat64(Node* node) { + VisitRR(this, kRiscvCvtDL, node); +} + +void InstructionSelector::VisitRoundUint64ToFloat32(Node* node) { + VisitRR(this, kRiscvCvtSUl, node); +} + +void InstructionSelector::VisitRoundUint64ToFloat64(Node* node) { + VisitRR(this, kRiscvCvtDUl, node); +} + +void InstructionSelector::VisitBitcastFloat32ToInt32(Node* node) { + VisitRR(this, kRiscvBitcastFloat32ToInt32, node); +} + +void InstructionSelector::VisitBitcastFloat64ToInt64(Node* node) { + VisitRR(this, kRiscvBitcastDL, node); +} + +void InstructionSelector::VisitBitcastInt32ToFloat32(Node* node) { + VisitRR(this, kRiscvBitcastInt32ToFloat32, node); +} + +void InstructionSelector::VisitBitcastInt64ToFloat64(Node* node) { + VisitRR(this, kRiscvBitcastLD, node); +} + +void InstructionSelector::VisitFloat32Add(Node* node) { + VisitRRR(this, kRiscvAddS, node); +} + +void InstructionSelector::VisitFloat64Add(Node* node) { + VisitRRR(this, kRiscvAddD, node); +} + +void InstructionSelector::VisitFloat32Sub(Node* node) { + VisitRRR(this, kRiscvSubS, node); +} + +void InstructionSelector::VisitFloat64Sub(Node* node) { + VisitRRR(this, kRiscvSubD, node); +} + +void InstructionSelector::VisitFloat32Mul(Node* node) { + VisitRRR(this, kRiscvMulS, node); +} + +void InstructionSelector::VisitFloat64Mul(Node* node) { + VisitRRR(this, kRiscvMulD, node); +} + +void InstructionSelector::VisitFloat32Div(Node* node) { + VisitRRR(this, kRiscvDivS, node); +} + +void InstructionSelector::VisitFloat64Div(Node* node) { + VisitRRR(this, kRiscvDivD, node); +} + +void InstructionSelector::VisitFloat64Mod(Node* node) { + RiscvOperandGenerator g(this); + Emit(kRiscvModD, g.DefineAsFixed(node, fa0), + g.UseFixed(node->InputAt(0), fa0), g.UseFixed(node->InputAt(1), fa1)) + ->MarkAsCall(); +} + +void InstructionSelector::VisitFloat32Max(Node* node) { + RiscvOperandGenerator g(this); + Emit(kRiscvFloat32Max, g.DefineAsRegister(node), + g.UseRegister(node->InputAt(0)), g.UseRegister(node->InputAt(1))); +} + +void InstructionSelector::VisitFloat64Max(Node* node) { + RiscvOperandGenerator g(this); + Emit(kRiscvFloat64Max, g.DefineAsRegister(node), + g.UseRegister(node->InputAt(0)), g.UseRegister(node->InputAt(1))); +} + +void InstructionSelector::VisitFloat32Min(Node* node) { + RiscvOperandGenerator g(this); + Emit(kRiscvFloat32Min, g.DefineAsRegister(node), + g.UseRegister(node->InputAt(0)), g.UseRegister(node->InputAt(1))); +} + +void InstructionSelector::VisitFloat64Min(Node* node) { + RiscvOperandGenerator g(this); + Emit(kRiscvFloat64Min, g.DefineAsRegister(node), + g.UseRegister(node->InputAt(0)), g.UseRegister(node->InputAt(1))); +} + +void InstructionSelector::VisitFloat32Abs(Node* node) { + VisitRR(this, kRiscvAbsS, node); +} + +void InstructionSelector::VisitFloat64Abs(Node* node) { + VisitRR(this, kRiscvAbsD, node); +} + +void InstructionSelector::VisitFloat32Sqrt(Node* node) { + VisitRR(this, kRiscvSqrtS, node); +} + +void InstructionSelector::VisitFloat64Sqrt(Node* node) { + VisitRR(this, kRiscvSqrtD, node); +} + +void InstructionSelector::VisitFloat32RoundDown(Node* node) { + VisitRR(this, kRiscvFloat32RoundDown, node); +} + +void InstructionSelector::VisitFloat64RoundDown(Node* node) { + VisitRR(this, kRiscvFloat64RoundDown, node); +} + +void InstructionSelector::VisitFloat32RoundUp(Node* node) { + VisitRR(this, kRiscvFloat32RoundUp, node); +} + +void InstructionSelector::VisitFloat64RoundUp(Node* node) { + VisitRR(this, kRiscvFloat64RoundUp, node); +} + +void InstructionSelector::VisitFloat32RoundTruncate(Node* node) { + VisitRR(this, kRiscvFloat32RoundTruncate, node); +} + +void InstructionSelector::VisitFloat64RoundTruncate(Node* node) { + VisitRR(this, kRiscvFloat64RoundTruncate, node); +} + +void InstructionSelector::VisitFloat64RoundTiesAway(Node* node) { + UNREACHABLE(); +} + +void InstructionSelector::VisitFloat32RoundTiesEven(Node* node) { + VisitRR(this, kRiscvFloat32RoundTiesEven, node); +} + +void InstructionSelector::VisitFloat64RoundTiesEven(Node* node) { + VisitRR(this, kRiscvFloat64RoundTiesEven, node); +} + +void InstructionSelector::VisitFloat32Neg(Node* node) { + VisitRR(this, kRiscvNegS, node); +} + +void InstructionSelector::VisitFloat64Neg(Node* node) { + VisitRR(this, kRiscvNegD, node); +} + +void InstructionSelector::VisitFloat64Ieee754Binop(Node* node, + InstructionCode opcode) { + RiscvOperandGenerator g(this); + Emit(opcode, g.DefineAsFixed(node, fa0), g.UseFixed(node->InputAt(0), fa0), + g.UseFixed(node->InputAt(1), fa1)) + ->MarkAsCall(); +} + +void InstructionSelector::VisitFloat64Ieee754Unop(Node* node, + InstructionCode opcode) { + RiscvOperandGenerator g(this); + Emit(opcode, g.DefineAsFixed(node, fa0), g.UseFixed(node->InputAt(0), fa1)) + ->MarkAsCall(); +} + +void InstructionSelector::EmitPrepareArguments( + ZoneVector* arguments, const CallDescriptor* call_descriptor, + Node* node) { + RiscvOperandGenerator 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(kRiscvStoreToStackSlot, g.NoOutput(), g.UseRegister(input.node), + g.TempImmediate(slot << kSystemPointerSizeLog2)); + ++slot; + } + } else { + int push_count = static_cast(call_descriptor->StackParameterCount()); + if (push_count > 0) { + // Calculate needed space + int stack_size = 0; + for (PushParameter input : (*arguments)) { + if (input.node) { + stack_size += input.location.GetSizeInPointers(); + } + } + Emit(kRiscvStackClaim, g.NoOutput(), + g.TempImmediate(stack_size << kSystemPointerSizeLog2)); + } + for (size_t n = 0; n < arguments->size(); ++n) { + PushParameter input = (*arguments)[n]; + if (input.node) { + Emit(kRiscvStoreToStackSlot, g.NoOutput(), g.UseRegister(input.node), + g.TempImmediate(static_cast(n << kSystemPointerSizeLog2))); + } + } + } +} + +void InstructionSelector::EmitPrepareResults( + ZoneVector* results, const CallDescriptor* call_descriptor, + Node* node) { + RiscvOperandGenerator g(this); + + int reverse_slot = 0; + 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); + } + Emit(kRiscvPeek, g.DefineAsRegister(output.node), + g.UseImmediate(reverse_slot)); + } + reverse_slot += output.location.GetSizeInPointers(); + } +} + +bool InstructionSelector::IsTailCallAddressImmediate() { return false; } + +int InstructionSelector::GetTempsCountForTailCallFromJSFunction() { return 3; } + +void InstructionSelector::VisitUnalignedLoad(Node* node) { + LoadRepresentation load_rep = LoadRepresentationOf(node->op()); + RiscvOperandGenerator g(this); + Node* base = node->InputAt(0); + Node* index = node->InputAt(1); + + ArchOpcode opcode = kArchNop; + switch (load_rep.representation()) { + case MachineRepresentation::kFloat32: + opcode = kRiscvULoadFloat; + break; + case MachineRepresentation::kFloat64: + opcode = kRiscvULoadDouble; + break; + case MachineRepresentation::kBit: // Fall through. + case MachineRepresentation::kWord8: + UNREACHABLE(); + case MachineRepresentation::kWord16: + opcode = load_rep.IsUnsigned() ? kRiscvUlhu : kRiscvUlh; + break; + case MachineRepresentation::kWord32: + opcode = load_rep.IsUnsigned() ? kRiscvUlwu : kRiscvUlw; + break; + case MachineRepresentation::kTaggedSigned: // Fall through. + case MachineRepresentation::kTaggedPointer: // Fall through. + case MachineRepresentation::kTagged: // Fall through. + case MachineRepresentation::kWord64: + opcode = kRiscvUld; + break; + case MachineRepresentation::kSimd128: + opcode = kRiscvMsaLd; + break; + case MachineRepresentation::kCompressedPointer: // Fall through. + case MachineRepresentation::kCompressed: // 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(kRiscvAdd64 | 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) { + RiscvOperandGenerator g(this); + Node* base = node->InputAt(0); + Node* index = node->InputAt(1); + Node* value = node->InputAt(2); + + UnalignedStoreRepresentation rep = UnalignedStoreRepresentationOf(node->op()); + ArchOpcode opcode = kArchNop; + switch (rep) { + case MachineRepresentation::kFloat32: + opcode = kRiscvUStoreFloat; + break; + case MachineRepresentation::kFloat64: + opcode = kRiscvUStoreDouble; + break; + case MachineRepresentation::kBit: // Fall through. + case MachineRepresentation::kWord8: + UNREACHABLE(); + case MachineRepresentation::kWord16: + opcode = kRiscvUsh; + break; + case MachineRepresentation::kWord32: + opcode = kRiscvUsw; + break; + case MachineRepresentation::kTaggedSigned: // Fall through. + case MachineRepresentation::kTaggedPointer: // Fall through. + case MachineRepresentation::kTagged: // Fall through. + case MachineRepresentation::kWord64: + opcode = kRiscvUsd; + break; + case MachineRepresentation::kSimd128: + opcode = kRiscvMsaSt; + break; + case MachineRepresentation::kCompressedPointer: // Fall through. + case MachineRepresentation::kCompressed: // 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(kRiscvAdd64 | 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) { + RiscvOperandGenerator 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, kRiscvCmpS, lhs, rhs, cont); +} + +// Shared routine for multiple float64 compare operations. +void VisitFloat64Compare(InstructionSelector* selector, Node* node, + FlagsContinuation* cont) { + RiscvOperandGenerator 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, kRiscvCmpD, lhs, rhs, cont); +} + +// Shared routine for multiple word compare operations. +void VisitWordCompare(InstructionSelector* selector, Node* node, + InstructionCode opcode, FlagsContinuation* cont, + bool commutative) { + RiscvOperandGenerator 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 == kRiscvTst) { + 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 == kRiscvTst) { + 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.IsPoisonedLoad() || + m.IsProtectedLoad() || m.IsWord32AtomicLoad() || m.IsWord64AtomicLoad()) { + LoadRepresentation load_rep = LoadRepresentationOf(n->op()); + 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) { + RiscvOperandGenerator g(selector); + InstructionOperand leftOp = g.TempRegister(); + InstructionOperand rightOp = g.TempRegister(); + + selector->Emit(kRiscvShl64, leftOp, g.UseRegister(node->InputAt(0)), + g.TempImmediate(32)); + selector->Emit(kRiscvShl64, 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) { + RiscvOperandGenerator 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(kRiscvShl64, leftOp, g.UseRegister(node->InputAt(0)), + g.TempImmediate(32)); + selector->Emit(kRiscvShl64, rightOp, g.UseRegister(node->InputAt(1)), + g.TempImmediate(32)); + selector->Emit(testOpcode, fullResult, leftOp, rightOp); + + selector->Emit(kRiscvAssertEqual, g.NoOutput(), optimizedResult, fullResult, + g.TempImmediate(static_cast( + AbortReason::kUnsupportedNonPrimitiveCompare))); + } + + VisitWordCompare(selector, node, opcode, cont, false); +} + +void VisitWord32Compare(InstructionSelector* selector, Node* node, + FlagsContinuation* cont) { + // RISC-V 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, kRiscvCmp, cont); + } else { + VisitOptimizedWord32Compare(selector, node, kRiscvCmp, cont); + } +} + +void VisitWord64Compare(InstructionSelector* selector, Node* node, + FlagsContinuation* cont) { + VisitWordCompare(selector, node, kRiscvCmp, cont, false); +} + +void EmitWordCompareZero(InstructionSelector* selector, Node* value, + FlagsContinuation* cont) { + RiscvOperandGenerator g(selector); + selector->EmitWithContinuation(kRiscvCmp, g.UseRegister(value), + g.TempImmediate(0), cont); +} + +void VisitAtomicLoad(InstructionSelector* selector, Node* node, + ArchOpcode opcode) { + RiscvOperandGenerator 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(node), g.UseRegister(base), + g.UseImmediate(index)); + } else { + InstructionOperand addr_reg = g.TempRegister(); + selector->Emit(kRiscvAdd64 | AddressingModeField::encode(kMode_None), + addr_reg, g.UseRegister(index), g.UseRegister(base)); + // Emit desired load opcode, using temp addr_reg. + selector->Emit(opcode | AddressingModeField::encode(kMode_MRI), + g.DefineAsRegister(node), addr_reg, g.TempImmediate(0)); + } +} + +void VisitAtomicStore(InstructionSelector* selector, Node* node, + ArchOpcode opcode) { + RiscvOperandGenerator g(selector); + Node* base = node->InputAt(0); + Node* index = node->InputAt(1); + Node* value = node->InputAt(2); + + if (g.CanBeImmediate(index, opcode)) { + selector->Emit(opcode | AddressingModeField::encode(kMode_MRI), + g.NoOutput(), g.UseRegister(base), g.UseImmediate(index), + g.UseRegisterOrImmediateZero(value)); + } else { + InstructionOperand addr_reg = g.TempRegister(); + selector->Emit(kRiscvAdd64 | AddressingModeField::encode(kMode_None), + addr_reg, g.UseRegister(index), g.UseRegister(base)); + // Emit desired store opcode, using temp addr_reg. + selector->Emit(opcode | AddressingModeField::encode(kMode_MRI), + g.NoOutput(), addr_reg, g.TempImmediate(0), + g.UseRegisterOrImmediateZero(value)); + } +} + +void VisitAtomicExchange(InstructionSelector* selector, Node* node, + ArchOpcode opcode) { + RiscvOperandGenerator 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); + selector->Emit(code, 1, outputs, input_count, inputs, 3, temp); +} + +void VisitAtomicCompareExchange(InstructionSelector* selector, Node* node, + ArchOpcode opcode) { + RiscvOperandGenerator 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); + selector->Emit(code, 1, outputs, input_count, inputs, 3, temp); +} + +void VisitAtomicBinop(InstructionSelector* selector, Node* node, + ArchOpcode opcode) { + RiscvOperandGenerator 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); + 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)); + + RiscvOperandGenerator g(this); + + // No outputs. + InstructionOperand* const outputs = nullptr; + const int output_count = 0; + + // 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()}; + const int temp_count = (kind == StackCheckKind::kJSFunctionEntry ? 1 : 0); + 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, kRiscvAdd64, cont); + case IrOpcode::kInt32SubWithOverflow: + cont->OverwriteAndNegateIfEqual(kOverflow); + return VisitBinop(this, node, kRiscvSub64, cont); + case IrOpcode::kInt32MulWithOverflow: + cont->OverwriteAndNegateIfEqual(kOverflow); + return VisitBinop(this, node, kRiscvMulOvf32, cont); + case IrOpcode::kInt64AddWithOverflow: + cont->OverwriteAndNegateIfEqual(kOverflow); + return VisitBinop(this, node, kRiscvAddOvf64, cont); + case IrOpcode::kInt64SubWithOverflow: + cont->OverwriteAndNegateIfEqual(kOverflow); + return VisitBinop(this, node, kRiscvSubOvf64, cont); + default: + break; + } + } + } + break; + case IrOpcode::kWord32And: + case IrOpcode::kWord64And: + return VisitWordCompare(this, value, kRiscvTst, 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) { + RiscvOperandGenerator 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(kRiscvSub32, 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, kRiscvAdd64, &cont); + } + FlagsContinuation cont; + VisitBinop(this, node, kRiscvAdd64, &cont); +} + +void InstructionSelector::VisitInt32SubWithOverflow(Node* node) { + if (Node* ovf = NodeProperties::FindProjection(node, 1)) { + FlagsContinuation cont = FlagsContinuation::ForSet(kOverflow, ovf); + return VisitBinop(this, node, kRiscvSub64, &cont); + } + FlagsContinuation cont; + VisitBinop(this, node, kRiscvSub64, &cont); +} + +void InstructionSelector::VisitInt32MulWithOverflow(Node* node) { + if (Node* ovf = NodeProperties::FindProjection(node, 1)) { + FlagsContinuation cont = FlagsContinuation::ForSet(kOverflow, ovf); + return VisitBinop(this, node, kRiscvMulOvf32, &cont); + } + FlagsContinuation cont; + VisitBinop(this, node, kRiscvMulOvf32, &cont); +} + +void InstructionSelector::VisitInt64AddWithOverflow(Node* node) { + if (Node* ovf = NodeProperties::FindProjection(node, 1)) { + FlagsContinuation cont = FlagsContinuation::ForSet(kOverflow, ovf); + return VisitBinop(this, node, kRiscvAddOvf64, &cont); + } + FlagsContinuation cont; + VisitBinop(this, node, kRiscvAddOvf64, &cont); +} + +void InstructionSelector::VisitInt64SubWithOverflow(Node* node) { + if (Node* ovf = NodeProperties::FindProjection(node, 1)) { + FlagsContinuation cont = FlagsContinuation::ForSet(kOverflow, ovf); + return VisitBinop(this, node, kRiscvSubOvf64, &cont); + } + FlagsContinuation cont; + VisitBinop(this, node, kRiscvSubOvf64, &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, kRiscvFloat64ExtractLowWord32, node); +} + +void InstructionSelector::VisitFloat64ExtractHighWord32(Node* node) { + VisitRR(this, kRiscvFloat64ExtractHighWord32, node); +} + +void InstructionSelector::VisitFloat64SilenceNaN(Node* node) { + VisitRR(this, kRiscvFloat64SilenceNaN, node); +} + +void InstructionSelector::VisitFloat64InsertLowWord32(Node* node) { + RiscvOperandGenerator g(this); + Node* left = node->InputAt(0); + Node* right = node->InputAt(1); + Emit(kRiscvFloat64InsertLowWord32, g.DefineSameAsFirst(node), + g.UseRegister(left), g.UseRegister(right)); +} + +void InstructionSelector::VisitFloat64InsertHighWord32(Node* node) { + RiscvOperandGenerator g(this); + Node* left = node->InputAt(0); + Node* right = node->InputAt(1); + Emit(kRiscvFloat64InsertHighWord32, g.DefineSameAsFirst(node), + g.UseRegister(left), g.UseRegister(right)); +} + +void InstructionSelector::VisitMemoryBarrier(Node* node) { + RiscvOperandGenerator g(this); + Emit(kRiscvSync, g.NoOutput()); +} + +void InstructionSelector::VisitWord32AtomicLoad(Node* node) { + LoadRepresentation load_rep = LoadRepresentationOf(node->op()); + ArchOpcode opcode = kArchNop; + switch (load_rep.representation()) { + case MachineRepresentation::kWord8: + opcode = + load_rep.IsSigned() ? kWord32AtomicLoadInt8 : kWord32AtomicLoadUint8; + break; + case MachineRepresentation::kWord16: + opcode = load_rep.IsSigned() ? kWord32AtomicLoadInt16 + : kWord32AtomicLoadUint16; + break; + case MachineRepresentation::kWord32: + opcode = kWord32AtomicLoadWord32; + break; + default: + UNREACHABLE(); + } + VisitAtomicLoad(this, node, opcode); +} + +void InstructionSelector::VisitWord32AtomicStore(Node* node) { + MachineRepresentation rep = AtomicStoreRepresentationOf(node->op()); + ArchOpcode opcode = kArchNop; + switch (rep) { + case MachineRepresentation::kWord8: + opcode = kWord32AtomicStoreWord8; + break; + case MachineRepresentation::kWord16: + opcode = kWord32AtomicStoreWord16; + break; + case MachineRepresentation::kWord32: + opcode = kWord32AtomicStoreWord32; + break; + default: + UNREACHABLE(); + } + + VisitAtomicStore(this, node, opcode); +} + +void InstructionSelector::VisitWord64AtomicLoad(Node* node) { + LoadRepresentation load_rep = LoadRepresentationOf(node->op()); + ArchOpcode opcode = kArchNop; + switch (load_rep.representation()) { + case MachineRepresentation::kWord8: + opcode = kRiscvWord64AtomicLoadUint8; + break; + case MachineRepresentation::kWord16: + opcode = kRiscvWord64AtomicLoadUint16; + break; + case MachineRepresentation::kWord32: + opcode = kRiscvWord64AtomicLoadUint32; + break; + case MachineRepresentation::kWord64: + opcode = kRiscvWord64AtomicLoadUint64; + break; + default: + UNREACHABLE(); + } + VisitAtomicLoad(this, node, opcode); +} + +void InstructionSelector::VisitWord64AtomicStore(Node* node) { + MachineRepresentation rep = AtomicStoreRepresentationOf(node->op()); + ArchOpcode opcode = kArchNop; + switch (rep) { + case MachineRepresentation::kWord8: + opcode = kRiscvWord64AtomicStoreWord8; + break; + case MachineRepresentation::kWord16: + opcode = kRiscvWord64AtomicStoreWord16; + break; + case MachineRepresentation::kWord32: + opcode = kRiscvWord64AtomicStoreWord32; + break; + case MachineRepresentation::kWord64: + opcode = kRiscvWord64AtomicStoreWord64; + break; + default: + UNREACHABLE(); + } + + VisitAtomicStore(this, node, opcode); +} + +void InstructionSelector::VisitWord32AtomicExchange(Node* node) { + ArchOpcode opcode = kArchNop; + MachineType type = AtomicOpType(node->op()); + if (type == MachineType::Int8()) { + opcode = kWord32AtomicExchangeInt8; + } else if (type == MachineType::Uint8()) { + opcode = kWord32AtomicExchangeUint8; + } else if (type == MachineType::Int16()) { + opcode = kWord32AtomicExchangeInt16; + } else if (type == MachineType::Uint16()) { + opcode = kWord32AtomicExchangeUint16; + } else if (type == MachineType::Int32() || type == MachineType::Uint32()) { + opcode = kWord32AtomicExchangeWord32; + } else { + UNREACHABLE(); + return; + } + + VisitAtomicExchange(this, node, opcode); +} + +void InstructionSelector::VisitWord64AtomicExchange(Node* node) { + ArchOpcode opcode = kArchNop; + MachineType type = AtomicOpType(node->op()); + if (type == MachineType::Uint8()) { + opcode = kRiscvWord64AtomicExchangeUint8; + } else if (type == MachineType::Uint16()) { + opcode = kRiscvWord64AtomicExchangeUint16; + } else if (type == MachineType::Uint32()) { + opcode = kRiscvWord64AtomicExchangeUint32; + } else if (type == MachineType::Uint64()) { + opcode = kRiscvWord64AtomicExchangeUint64; + } else { + UNREACHABLE(); + return; + } + VisitAtomicExchange(this, node, opcode); +} + +void InstructionSelector::VisitWord32AtomicCompareExchange(Node* node) { + ArchOpcode opcode = kArchNop; + MachineType type = AtomicOpType(node->op()); + if (type == MachineType::Int8()) { + opcode = kWord32AtomicCompareExchangeInt8; + } else if (type == MachineType::Uint8()) { + opcode = kWord32AtomicCompareExchangeUint8; + } else if (type == MachineType::Int16()) { + opcode = kWord32AtomicCompareExchangeInt16; + } else if (type == MachineType::Uint16()) { + opcode = kWord32AtomicCompareExchangeUint16; + } else if (type == MachineType::Int32() || type == MachineType::Uint32()) { + opcode = kWord32AtomicCompareExchangeWord32; + } else { + UNREACHABLE(); + return; + } + + VisitAtomicCompareExchange(this, node, opcode); +} + +void InstructionSelector::VisitWord64AtomicCompareExchange(Node* node) { + ArchOpcode opcode = kArchNop; + MachineType type = AtomicOpType(node->op()); + if (type == MachineType::Uint8()) { + opcode = kRiscvWord64AtomicCompareExchangeUint8; + } else if (type == MachineType::Uint16()) { + opcode = kRiscvWord64AtomicCompareExchangeUint16; + } else if (type == MachineType::Uint32()) { + opcode = kRiscvWord64AtomicCompareExchangeUint32; + } else if (type == MachineType::Uint64()) { + opcode = kRiscvWord64AtomicCompareExchangeUint64; + } else { + UNREACHABLE(); + return; + } + VisitAtomicCompareExchange(this, node, opcode); +} +void InstructionSelector::VisitWord32AtomicBinaryOperation( + Node* node, ArchOpcode int8_op, ArchOpcode uint8_op, ArchOpcode int16_op, + ArchOpcode uint16_op, ArchOpcode word32_op) { + ArchOpcode opcode = kArchNop; + 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(); + return; + } + + VisitAtomicBinop(this, node, opcode); +} + +#define VISIT_ATOMIC_BINOP(op) \ + void InstructionSelector::VisitWord32Atomic##op(Node* node) { \ + VisitWord32AtomicBinaryOperation( \ + node, kWord32Atomic##op##Int8, kWord32Atomic##op##Uint8, \ + kWord32Atomic##op##Int16, kWord32Atomic##op##Uint16, \ + kWord32Atomic##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 = kArchNop; + 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(); + return; + } + VisitAtomicBinop(this, node, opcode); +} + +#define VISIT_ATOMIC_BINOP(op) \ + void InstructionSelector::VisitWord64Atomic##op(Node* node) { \ + VisitWord64AtomicBinaryOperation( \ + node, kRiscvWord64Atomic##op##Uint8, kRiscvWord64Atomic##op##Uint16, \ + kRiscvWord64Atomic##op##Uint32, kRiscvWord64Atomic##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(F32x4) \ + V(I32x4) \ + V(I16x8) \ + V(I8x16) + +#define SIMD_UNOP_LIST(V) \ + V(F64x2Abs, kRiscvF64x2Abs) \ + V(F64x2Neg, kRiscvF64x2Neg) \ + V(F64x2Sqrt, kRiscvF64x2Sqrt) \ + V(F64x2Ceil, kRiscvF64x2Ceil) \ + V(F64x2Floor, kRiscvF64x2Floor) \ + V(F64x2Trunc, kRiscvF64x2Trunc) \ + V(F64x2NearestInt, kRiscvF64x2NearestInt) \ + V(I64x2Neg, kRiscvI64x2Neg) \ + V(F32x4SConvertI32x4, kRiscvF32x4SConvertI32x4) \ + V(F32x4UConvertI32x4, kRiscvF32x4UConvertI32x4) \ + V(F32x4Abs, kRiscvF32x4Abs) \ + V(F32x4Neg, kRiscvF32x4Neg) \ + V(F32x4Sqrt, kRiscvF32x4Sqrt) \ + V(F32x4RecipApprox, kRiscvF32x4RecipApprox) \ + V(F32x4RecipSqrtApprox, kRiscvF32x4RecipSqrtApprox) \ + V(F32x4Ceil, kRiscvF32x4Ceil) \ + V(F32x4Floor, kRiscvF32x4Floor) \ + V(F32x4Trunc, kRiscvF32x4Trunc) \ + V(F32x4NearestInt, kRiscvF32x4NearestInt) \ + V(I32x4SConvertF32x4, kRiscvI32x4SConvertF32x4) \ + V(I32x4UConvertF32x4, kRiscvI32x4UConvertF32x4) \ + V(I32x4Neg, kRiscvI32x4Neg) \ + V(I32x4SConvertI16x8Low, kRiscvI32x4SConvertI16x8Low) \ + V(I32x4SConvertI16x8High, kRiscvI32x4SConvertI16x8High) \ + V(I32x4UConvertI16x8Low, kRiscvI32x4UConvertI16x8Low) \ + V(I32x4UConvertI16x8High, kRiscvI32x4UConvertI16x8High) \ + V(I32x4Abs, kRiscvI32x4Abs) \ + V(I32x4BitMask, kRiscvI32x4BitMask) \ + V(I16x8Neg, kRiscvI16x8Neg) \ + V(I16x8SConvertI8x16Low, kRiscvI16x8SConvertI8x16Low) \ + V(I16x8SConvertI8x16High, kRiscvI16x8SConvertI8x16High) \ + V(I16x8UConvertI8x16Low, kRiscvI16x8UConvertI8x16Low) \ + V(I16x8UConvertI8x16High, kRiscvI16x8UConvertI8x16High) \ + V(I16x8Abs, kRiscvI16x8Abs) \ + V(I16x8BitMask, kRiscvI16x8BitMask) \ + V(I8x16Neg, kRiscvI8x16Neg) \ + V(I8x16Abs, kRiscvI8x16Abs) \ + V(I8x16BitMask, kRiscvI8x16BitMask) \ + V(S128Not, kRiscvS128Not) \ + V(V32x4AnyTrue, kRiscvV32x4AnyTrue) \ + V(V32x4AllTrue, kRiscvV32x4AllTrue) \ + V(V16x8AnyTrue, kRiscvV16x8AnyTrue) \ + V(V16x8AllTrue, kRiscvV16x8AllTrue) \ + V(V8x16AnyTrue, kRiscvV8x16AnyTrue) \ + V(V8x16AllTrue, kRiscvV8x16AllTrue) + +#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, kRiscvF64x2Add) \ + V(F64x2Sub, kRiscvF64x2Sub) \ + V(F64x2Mul, kRiscvF64x2Mul) \ + V(F64x2Div, kRiscvF64x2Div) \ + V(F64x2Min, kRiscvF64x2Min) \ + V(F64x2Max, kRiscvF64x2Max) \ + V(F64x2Eq, kRiscvF64x2Eq) \ + V(F64x2Ne, kRiscvF64x2Ne) \ + V(F64x2Lt, kRiscvF64x2Lt) \ + V(F64x2Le, kRiscvF64x2Le) \ + V(I64x2Add, kRiscvI64x2Add) \ + V(I64x2Sub, kRiscvI64x2Sub) \ + V(I64x2Mul, kRiscvI64x2Mul) \ + V(F32x4Add, kRiscvF32x4Add) \ + V(F32x4AddHoriz, kRiscvF32x4AddHoriz) \ + V(F32x4Sub, kRiscvF32x4Sub) \ + V(F32x4Mul, kRiscvF32x4Mul) \ + V(F32x4Div, kRiscvF32x4Div) \ + V(F32x4Max, kRiscvF32x4Max) \ + V(F32x4Min, kRiscvF32x4Min) \ + V(F32x4Eq, kRiscvF32x4Eq) \ + V(F32x4Ne, kRiscvF32x4Ne) \ + V(F32x4Lt, kRiscvF32x4Lt) \ + V(F32x4Le, kRiscvF32x4Le) \ + V(I32x4Add, kRiscvI32x4Add) \ + V(I32x4AddHoriz, kRiscvI32x4AddHoriz) \ + V(I32x4Sub, kRiscvI32x4Sub) \ + V(I32x4Mul, kRiscvI32x4Mul) \ + V(I32x4MaxS, kRiscvI32x4MaxS) \ + V(I32x4MinS, kRiscvI32x4MinS) \ + V(I32x4MaxU, kRiscvI32x4MaxU) \ + V(I32x4MinU, kRiscvI32x4MinU) \ + V(I32x4Eq, kRiscvI32x4Eq) \ + V(I32x4Ne, kRiscvI32x4Ne) \ + V(I32x4GtS, kRiscvI32x4GtS) \ + V(I32x4GeS, kRiscvI32x4GeS) \ + V(I32x4GtU, kRiscvI32x4GtU) \ + V(I32x4GeU, kRiscvI32x4GeU) \ + V(I16x8Add, kRiscvI16x8Add) \ + V(I16x8AddSaturateS, kRiscvI16x8AddSaturateS) \ + V(I16x8AddSaturateU, kRiscvI16x8AddSaturateU) \ + V(I16x8AddHoriz, kRiscvI16x8AddHoriz) \ + V(I16x8Sub, kRiscvI16x8Sub) \ + V(I16x8SubSaturateS, kRiscvI16x8SubSaturateS) \ + V(I16x8SubSaturateU, kRiscvI16x8SubSaturateU) \ + V(I16x8Mul, kRiscvI16x8Mul) \ + V(I16x8MaxS, kRiscvI16x8MaxS) \ + V(I16x8MinS, kRiscvI16x8MinS) \ + V(I16x8MaxU, kRiscvI16x8MaxU) \ + V(I16x8MinU, kRiscvI16x8MinU) \ + V(I16x8Eq, kRiscvI16x8Eq) \ + V(I16x8Ne, kRiscvI16x8Ne) \ + V(I16x8GtS, kRiscvI16x8GtS) \ + V(I16x8GeS, kRiscvI16x8GeS) \ + V(I16x8GtU, kRiscvI16x8GtU) \ + V(I16x8GeU, kRiscvI16x8GeU) \ + V(I16x8RoundingAverageU, kRiscvI16x8RoundingAverageU) \ + V(I16x8SConvertI32x4, kRiscvI16x8SConvertI32x4) \ + V(I16x8UConvertI32x4, kRiscvI16x8UConvertI32x4) \ + V(I8x16Add, kRiscvI8x16Add) \ + V(I8x16AddSaturateS, kRiscvI8x16AddSaturateS) \ + V(I8x16AddSaturateU, kRiscvI8x16AddSaturateU) \ + V(I8x16Sub, kRiscvI8x16Sub) \ + V(I8x16SubSaturateS, kRiscvI8x16SubSaturateS) \ + V(I8x16SubSaturateU, kRiscvI8x16SubSaturateU) \ + V(I8x16Mul, kRiscvI8x16Mul) \ + V(I8x16MaxS, kRiscvI8x16MaxS) \ + V(I8x16MinS, kRiscvI8x16MinS) \ + V(I8x16MaxU, kRiscvI8x16MaxU) \ + V(I8x16MinU, kRiscvI8x16MinU) \ + V(I8x16Eq, kRiscvI8x16Eq) \ + V(I8x16Ne, kRiscvI8x16Ne) \ + V(I8x16GtS, kRiscvI8x16GtS) \ + V(I8x16GeS, kRiscvI8x16GeS) \ + V(I8x16GtU, kRiscvI8x16GtU) \ + V(I8x16GeU, kRiscvI8x16GeU) \ + V(I8x16RoundingAverageU, kRiscvI8x16RoundingAverageU) \ + V(I8x16SConvertI16x8, kRiscvI8x16SConvertI16x8) \ + V(I8x16UConvertI16x8, kRiscvI8x16UConvertI16x8) \ + V(S128And, kRiscvS128And) \ + V(S128Or, kRiscvS128Or) \ + V(S128Xor, kRiscvS128Xor) \ + V(S128AndNot, kRiscvS128AndNot) + +void InstructionSelector::VisitS128Const(Node* node) { + RiscvOperandGenerator 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(kRiscvS128Zero, dst); + } else if (all_ones) { + Emit(kRiscvS128AllOnes, dst); + } else { + Emit(kRiscvS128Const, dst, g.UseImmediate(val[0]), g.UseImmediate(val[1]), + g.UseImmediate(val[2]), g.UseImmediate(val[3])); + } +} + +void InstructionSelector::VisitS128Zero(Node* node) { + RiscvOperandGenerator g(this); + Emit(kRiscvS128Zero, g.DefineAsRegister(node)); +} + +#define SIMD_VISIT_SPLAT(Type) \ + void InstructionSelector::Visit##Type##Splat(Node* node) { \ + VisitRR(this, kRiscv##Type##Splat, node); \ + } +SIMD_TYPE_LIST(SIMD_VISIT_SPLAT) +SIMD_VISIT_SPLAT(F64x2) +#undef SIMD_VISIT_SPLAT + +#define SIMD_VISIT_EXTRACT_LANE(Type, Sign) \ + void InstructionSelector::Visit##Type##ExtractLane##Sign(Node* node) { \ + VisitRRI(this, kRiscv##Type##ExtractLane##Sign, node); \ + } +SIMD_VISIT_EXTRACT_LANE(F64x2, ) +SIMD_VISIT_EXTRACT_LANE(F32x4, ) +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, kRiscv##Type##ReplaceLane, node); \ + } +SIMD_TYPE_LIST(SIMD_VISIT_REPLACE_LANE) +SIMD_VISIT_REPLACE_LANE(F64x2) +#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, kRiscv##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, kRiscvS128Select, node); +} + +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}, + kRiscvS32x4InterleaveRight}, + {{8, 9, 10, 11, 24, 25, 26, 27, 12, 13, 14, 15, 28, 29, 30, 31}, + kRiscvS32x4InterleaveLeft}, + {{0, 1, 2, 3, 8, 9, 10, 11, 16, 17, 18, 19, 24, 25, 26, 27}, + kRiscvS32x4PackEven}, + {{4, 5, 6, 7, 12, 13, 14, 15, 20, 21, 22, 23, 28, 29, 30, 31}, + kRiscvS32x4PackOdd}, + {{0, 1, 2, 3, 16, 17, 18, 19, 8, 9, 10, 11, 24, 25, 26, 27}, + kRiscvS32x4InterleaveEven}, + {{4, 5, 6, 7, 20, 21, 22, 23, 12, 13, 14, 15, 28, 29, 30, 31}, + kRiscvS32x4InterleaveOdd}, + + {{0, 1, 16, 17, 2, 3, 18, 19, 4, 5, 20, 21, 6, 7, 22, 23}, + kRiscvS16x8InterleaveRight}, + {{8, 9, 24, 25, 10, 11, 26, 27, 12, 13, 28, 29, 14, 15, 30, 31}, + kRiscvS16x8InterleaveLeft}, + {{0, 1, 4, 5, 8, 9, 12, 13, 16, 17, 20, 21, 24, 25, 28, 29}, + kRiscvS16x8PackEven}, + {{2, 3, 6, 7, 10, 11, 14, 15, 18, 19, 22, 23, 26, 27, 30, 31}, + kRiscvS16x8PackOdd}, + {{0, 1, 16, 17, 4, 5, 20, 21, 8, 9, 24, 25, 12, 13, 28, 29}, + kRiscvS16x8InterleaveEven}, + {{2, 3, 18, 19, 6, 7, 22, 23, 10, 11, 26, 27, 14, 15, 30, 31}, + kRiscvS16x8InterleaveOdd}, + {{6, 7, 4, 5, 2, 3, 0, 1, 14, 15, 12, 13, 10, 11, 8, 9}, + kRiscvS16x4Reverse}, + {{2, 3, 0, 1, 6, 7, 4, 5, 10, 11, 8, 9, 14, 15, 12, 13}, + kRiscvS16x2Reverse}, + + {{0, 16, 1, 17, 2, 18, 3, 19, 4, 20, 5, 21, 6, 22, 7, 23}, + kRiscvS8x16InterleaveRight}, + {{8, 24, 9, 25, 10, 26, 11, 27, 12, 28, 13, 29, 14, 30, 15, 31}, + kRiscvS8x16InterleaveLeft}, + {{0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30}, + kRiscvS8x16PackEven}, + {{1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31}, + kRiscvS8x16PackOdd}, + {{0, 16, 2, 18, 4, 20, 6, 22, 8, 24, 10, 26, 12, 28, 14, 30}, + kRiscvS8x16InterleaveEven}, + {{1, 17, 3, 19, 5, 21, 7, 23, 9, 25, 11, 27, 13, 29, 15, 31}, + kRiscvS8x16InterleaveOdd}, + {{7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8}, kRiscvS8x8Reverse}, + {{3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8, 15, 14, 13, 12}, kRiscvS8x4Reverse}, + {{1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14}, + kRiscvS8x2Reverse}}; + +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; + RiscvOperandGenerator g(this); + if (wasm::SimdShuffle::TryMatchConcat(shuffle, &offset)) { + Emit(kRiscvS8x16Concat, g.DefineSameAsFirst(node), g.UseRegister(input1), + g.UseRegister(input0), g.UseImmediate(offset)); + return; + } + if (wasm::SimdShuffle::TryMatch32x4Shuffle(shuffle, shuffle32x4)) { + Emit(kRiscvS32x4Shuffle, g.DefineAsRegister(node), g.UseRegister(input0), + g.UseRegister(input1), + g.UseImmediate(wasm::SimdShuffle::Pack4Lanes(shuffle32x4))); + return; + } + Emit(kRiscvI8x16Shuffle, 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))); +} + +void InstructionSelector::VisitI8x16Swizzle(Node* node) { + RiscvOperandGenerator 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(kRiscvI8x16Swizzle, g.DefineAsRegister(node), + g.UseUniqueRegister(node->InputAt(0)), + g.UseUniqueRegister(node->InputAt(1)), arraysize(temps), temps); +} + +void InstructionSelector::VisitSignExtendWord8ToInt32(Node* node) { + RiscvOperandGenerator g(this); + Emit(kRiscvSignExtendByte, g.DefineAsRegister(node), + g.UseRegister(node->InputAt(0))); +} + +void InstructionSelector::VisitSignExtendWord16ToInt32(Node* node) { + RiscvOperandGenerator g(this); + Emit(kRiscvSignExtendShort, g.DefineAsRegister(node), + g.UseRegister(node->InputAt(0))); +} + +void InstructionSelector::VisitSignExtendWord8ToInt64(Node* node) { + RiscvOperandGenerator g(this); + Emit(kRiscvSignExtendByte, g.DefineAsRegister(node), + g.UseRegister(node->InputAt(0))); +} + +void InstructionSelector::VisitSignExtendWord16ToInt64(Node* node) { + RiscvOperandGenerator g(this); + Emit(kRiscvSignExtendShort, g.DefineAsRegister(node), + g.UseRegister(node->InputAt(0))); +} + +void InstructionSelector::VisitSignExtendWord32ToInt64(Node* node) { + RiscvOperandGenerator g(this); + Emit(kRiscvShl32, g.DefineAsRegister(node), g.UseRegister(node->InputAt(0)), + g.TempImmediate(0)); +} + +void InstructionSelector::VisitF32x4Pmin(Node* node) { + VisitUniqueRRR(this, kRiscvF32x4Pmin, node); +} + +void InstructionSelector::VisitF32x4Pmax(Node* node) { + VisitUniqueRRR(this, kRiscvF32x4Pmax, node); +} + +void InstructionSelector::VisitF64x2Pmin(Node* node) { + VisitUniqueRRR(this, kRiscvF64x2Pmin, node); +} + +void InstructionSelector::VisitF64x2Pmax(Node* node) { + VisitUniqueRRR(this, kRiscvF64x2Pmax, node); +} + +// static +MachineOperatorBuilder::Flags +InstructionSelector::SupportedMachineOperatorFlags() { + MachineOperatorBuilder::Flags flags = MachineOperatorBuilder::kNoFlags; + return flags | 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 Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/compiler/c-linkage.cc =================================================================== --- qtwebengine-everywhere-src-5.15.3.orig/src/3rdparty/chromium/v8/src/compiler/c-linkage.cc +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/compiler/c-linkage.cc @@ -131,6 +131,19 @@ namespace { d8.bit() | d9.bit() | d10.bit() | d11.bit() | d12.bit() | d13.bit() | \ d14.bit() | d15.bit() +#elif V8_TARGET_ARCH_RISCV64 +// =========================================================================== +// == riscv64 ================================================================= +// =========================================================================== +#define PARAM_REGISTERS a0, a1, a2, a3, a4, a5, a6, a7 +// fp is not part of CALLEE_SAVE_REGISTERS (similar to how MIPS64 or PPC defines +// it) +#define CALLEE_SAVE_REGISTERS \ + s1.bit() | s2.bit() | s3.bit() | s4.bit() | s5.bit() | s6.bit() | s7.bit() | \ + s8.bit() | s9.bit() | s10.bit() | s11.bit() +#define CALLEE_SAVE_FP_REGISTERS \ + fs0.bit() | fs1.bit() | fs2.bit() | fs3.bit() | fs4.bit() | fs5.bit() | \ + fs6.bit() | fs7.bit() | fs8.bit() | fs9.bit() | fs10.bit() | fs11.bit() #else // =========================================================================== // == unknown ================================================================ Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/debug/debug-evaluate.cc =================================================================== --- qtwebengine-everywhere-src-5.15.3.orig/src/3rdparty/chromium/v8/src/debug/debug-evaluate.cc +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/debug/debug-evaluate.cc @@ -1060,8 +1060,10 @@ void DebugEvaluate::VerifyTransitiveBuil } } CHECK(!failed); -#if defined(V8_TARGET_ARCH_PPC) || defined(V8_TARGET_ARCH_PPC64) || \ - defined(V8_TARGET_ARCH_MIPS64) + // FIXME (RISCV): does RISCV need this? +#if defined(V8_TARGET_ARCH_PPC) || defined(V8_TARGET_ARCH_PPC64) || \ + defined(V8_TARGET_ARCH_MIPS64) || defined(V8_TARGET_ARCH_RISCV64) || \ + defined(V8_TARGET_ARCH_RISCV) // 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. Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/debug/riscv64/debug-riscv64.cc =================================================================== --- /dev/null +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/debug/riscv64/debug-riscv64.cc @@ -0,0 +1,55 @@ +// 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_RISCV64 + +#include "src/codegen/macro-assembler.h" +#include "src/debug/debug.h" +#include "src/debug/liveedit.h" +#include "src/execution/frames-inl.h" + +namespace v8 { +namespace internal { + +#define __ ACCESS_MASM(masm) + +void DebugCodegen::GenerateHandleDebuggerStatement(MacroAssembler* masm) { + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ CallRuntime(Runtime::kHandleDebuggerStatement, 0); + } + __ MaybeDropFrames(); + + // Return to caller. + __ Ret(); +} + +void DebugCodegen::GenerateFrameDropperTrampoline(MacroAssembler* masm) { + // Frame is being dropped: + // - Drop to the target frame specified by a1. + // - Look up current function on the frame. + // - Leave the frame. + // - Restart the frame by calling the function. + __ mv(fp, a1); + __ Ld(a1, MemOperand(fp, StandardFrameConstants::kFunctionOffset)); + + // Pop return address and frame. + __ LeaveFrame(StackFrame::INTERNAL); + + __ Ld(a0, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset)); + __ Lhu(a0, + FieldMemOperand(a0, SharedFunctionInfo::kFormalParameterCountOffset)); + __ mv(a2, a0); + + __ InvokeFunction(a1, a2, a0, JUMP_FUNCTION); +} + +const bool LiveEdit::kFrameDropperSupported = true; + +#undef __ + +} // namespace internal +} // namespace v8 + +#endif // V8_TARGET_ARCH_RISCV64 Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/deoptimizer/riscv64/deoptimizer-riscv64.cc =================================================================== --- /dev/null +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/deoptimizer/riscv64/deoptimizer-riscv64.cc @@ -0,0 +1,240 @@ +// 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/codegen/macro-assembler.h" +#include "src/codegen/register-configuration.h" +#include "src/codegen/safepoint-table.h" +#include "src/deoptimizer/deoptimizer.h" + +namespace v8 { +namespace internal { + +const bool Deoptimizer::kSupportsFixedDeoptExitSizes = false; +const int Deoptimizer::kNonLazyDeoptExitSize = 0; +const int Deoptimizer::kLazyDeoptExitSize = 0; + +#define __ masm-> + +// This code tries to be close to ia32 code so that any changes can be +// easily ported. +void Deoptimizer::GenerateDeoptimizationEntries(MacroAssembler* masm, + Isolate* isolate, + DeoptimizeKind deopt_kind) { + NoRootArrayScope no_root_array(masm); + + // 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.bit() | ra.bit(); + + const int kDoubleRegsSize = kDoubleSize * DoubleRegister::kNumRegisters; + + // Save all double FPU registers before messing with them. + __ Sub64(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; + __ StoreDouble(fpu_reg, MemOperand(sp, offset)); + } + + // Push saved_regs (needed to populate FrameDescription::registers_). + // Leave gaps for other registers. + __ Sub64(sp, sp, kNumberOfRegisters * kPointerSize); + for (int16_t i = kNumberOfRegisters - 1; i >= 0; i--) { + if ((saved_regs & (1 << i)) != 0) { + __ Sd(ToRegister(i), MemOperand(sp, kPointerSize * i)); + } + } + + __ li(a2, Operand(ExternalReference::Create( + IsolateAddressId::kCEntryFPAddress, isolate))); + __ Sd(fp, MemOperand(a2)); + + const int kSavedRegistersAreaSize = + (kNumberOfRegisters * kPointerSize) + kDoubleRegsSize; + + // Get the bailout is passed as kRootRegister by the caller. + __ mv(a2, kRootRegister); + + // Get the address of the location in the code object (a3) (return + // address for lazy deoptimization) and compute the fp-to-sp delta in + // register a4. + __ mv(a3, ra); + __ Add64(a4, sp, Operand(kSavedRegistersAreaSize)); + + __ Sub64(a4, fp, a4); + + // Allocate a new deoptimizer object. + __ PrepareCallCFunction(6, a5); + // Pass six arguments, according to n64 ABI. + __ mv(a0, zero_reg); + Label context_check; + __ Ld(a1, MemOperand(fp, CommonFrameConstants::kContextOrFrameTypeOffset)); + __ JumpIfSmi(a1, &context_check); + __ Ld(a0, MemOperand(fp, StandardFrameConstants::kFunctionOffset)); + __ bind(&context_check); + __ li(a1, Operand(static_cast(deopt_kind))); + // a2: bailout id already loaded. + // a3: code address or 0 already loaded. + // a4: already has fp-to-sp delta. + __ li(a5, Operand(ExternalReference::isolate_address(isolate))); + + // Call Deoptimizer::New(). + { + AllowExternalCallThatCantCauseGC scope(masm); + __ CallCFunction(ExternalReference::new_deoptimizer_function(), 6); + } + + // Preserve "deoptimizer" object in register a0 and get the input + // frame descriptor pointer to a1 (deoptimizer->input_); + // Move deopt-obj to a0 for call to Deoptimizer::ComputeOutputFrames() below. + __ Ld(a1, MemOperand(a0, 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 & (1 << i)) != 0) { + __ Ld(a2, MemOperand(sp, i * kPointerSize)); + __ Sd(a2, MemOperand(a1, offset)); + } else if (FLAG_debug_code) { + __ li(a2, kDebugZapValue); + __ Sd(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; + __ LoadDouble(ft0, MemOperand(sp, src_offset)); + __ StoreDouble(ft0, MemOperand(a1, dst_offset)); + } + + // Remove the saved registers from the stack. + __ Add64(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. + __ Ld(a2, MemOperand(a1, FrameDescription::frame_size_offset())); + __ Add64(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. + __ Add64(a3, a1, Operand(FrameDescription::frame_content_offset())); + Label pop_loop; + Label pop_loop_header; + __ BranchShort(&pop_loop_header); + __ bind(&pop_loop); + __ pop(a4); + __ Sd(a4, MemOperand(a3, 0)); + __ addi(a3, a3, sizeof(uint64_t)); + __ 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). + + __ Ld(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**. + __ Lw(a1, MemOperand(a0, Deoptimizer::output_count_offset())); + __ Ld(a4, MemOperand(a0, Deoptimizer::output_offset())); // a4 is output_. + __ CalcScaledAddress(a1, a4, a1, kPointerSizeLog2); + __ BranchShort(&outer_loop_header); + __ bind(&outer_push_loop); + // Inner loop state: a2 = current FrameDescription*, a3 = loop index. + __ Ld(a2, MemOperand(a4, 0)); // output_[ix] + __ Ld(a3, MemOperand(a2, FrameDescription::frame_size_offset())); + __ BranchShort(&inner_loop_header); + __ bind(&inner_push_loop); + __ Sub64(a3, a3, Operand(sizeof(uint64_t))); + __ Add64(a6, a2, Operand(a3)); + __ Ld(a7, MemOperand(a6, FrameDescription::frame_content_offset())); + __ push(a7); + __ bind(&inner_loop_header); + __ BranchShort(&inner_push_loop, ne, a3, Operand(zero_reg)); + + __ Add64(a4, a4, Operand(kPointerSize)); + __ bind(&outer_loop_header); + __ BranchShort(&outer_push_loop, lt, a4, Operand(a1)); + + __ Ld(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; + __ LoadDouble(fpu_reg, MemOperand(a1, src_offset)); + } + + // Push pc and continuation from the last output frame. + __ Ld(a6, MemOperand(a2, FrameDescription::pc_offset())); + __ push(a6); + __ Ld(a6, MemOperand(a2, FrameDescription::continuation_offset())); + __ push(a6); + + // Technically restoring 't3' should work unless zero_reg is also restored + // but it's safer to check for this. + DCHECK(!(t3.bit() & restored_regs)); + // Restore the registers from the last output frame. + __ mv(t3, a2); + for (int i = kNumberOfRegisters - 1; i >= 0; i--) { + int offset = (i * kPointerSize) + FrameDescription::registers_offset(); + if ((restored_regs & (1 << i)) != 0) { + __ Ld(ToRegister(i), MemOperand(t3, offset)); + } + } + + __ pop(t3); // Get continuation, leave pc on stack. + __ pop(ra); + __ Jump(t3); + __ stop(); +} + +// Maximum size of a table entry generated below. +// FIXME(RISCV): Is this value correct? +const int Deoptimizer::table_entry_size_ = 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; } + +#undef __ + +} // namespace internal +} // namespace v8 Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/diagnostics/perf-jit.h =================================================================== --- qtwebengine-everywhere-src-5.15.3.orig/src/3rdparty/chromium/v8/src/diagnostics/perf-jit.h +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/diagnostics/perf-jit.h @@ -86,6 +86,7 @@ class PerfJitLogger : public CodeEventLo static const uint32_t kElfMachARM64 = 183; static const uint32_t kElfMachS390x = 22; static const uint32_t kElfMachPPC64 = 21; + static const uint32_t kElfMachRISCV = 243; uint32_t GetElfMach() { #if V8_TARGET_ARCH_IA32 @@ -104,6 +105,8 @@ class PerfJitLogger : public CodeEventLo return kElfMachS390x; #elif V8_TARGET_ARCH_PPC64 return kElfMachPPC64; +#elif V8_TARGET_ARCH_RISCV64 || V8_TARGET_ARCH_RISCV + return kElfMachRISCV; #else UNIMPLEMENTED(); return 0; Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/diagnostics/riscv64/disasm-riscv64.cc =================================================================== --- /dev/null +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/diagnostics/riscv64/disasm-riscv64.cc @@ -0,0 +1,1448 @@ +// 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::internal::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 + +#if V8_TARGET_ARCH_RISCV64 + +#include "src/base/platform/platform.h" +#include "src/codegen/macro-assembler.h" +#include "src/codegen/riscv64/constants-riscv64.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::internal::Vector out_buffer) + : converter_(converter), out_buffer_(out_buffer), out_buffer_pos_(0) { + out_buffer_[out_buffer_pos_] = '\0'; + } + + ~Decoder() {} + + // Writes one disassembled instruction into 'buffer' (0-terminated). + // Returns the length of the disassembled machine instruction in bytes. + int InstructionDecode(byte* instruction); + + private: + // Bottleneck functions to print into the out_buffer. + void PrintChar(const char ch); + void Print(const char* str); + + // Printing of common values. + void PrintRegister(int reg); + void PrintFPURegister(int freg); + void PrintFPUStatusRegister(int freg); + void PrintRs1(Instruction* instr); + void PrintRs2(Instruction* instr); + void PrintRd(Instruction* instr); + void PrintVs1(Instruction* instr); + void PrintFRs1(Instruction* instr); + void PrintFRs2(Instruction* instr); + void PrintFRs3(Instruction* instr); + void PrintFRd(Instruction* instr); + void PrintImm12(Instruction* instr); + void PrintImm12X(Instruction* instr); + void PrintImm20U(Instruction* instr); + void PrintImm20J(Instruction* instr); + void PrintShamt(Instruction* instr); + void PrintShamt32(Instruction* instr); + void PrintAcquireRelease(Instruction* instr); + void PrintBranchOffset(Instruction* instr); + void PrintStoreOffset(Instruction* instr); + void PrintCSRReg(Instruction* instr); + void PrintRoundingMode(Instruction* instr); + void PrintMemoryOrder(Instruction* instr, bool is_pred); + + // Each of these functions decodes one particular instruction type. + void DecodeRType(Instruction* instr); + void DecodeR4Type(Instruction* instr); + void DecodeRAType(Instruction* instr); + void DecodeRFPType(Instruction* instr); + void DecodeIType(Instruction* instr); + void DecodeSType(Instruction* instr); + void DecodeBType(Instruction* instr); + void DecodeUType(Instruction* instr); + void DecodeJType(Instruction* instr); + + // Printing of instruction name. + void PrintInstructionName(Instruction* instr); + + // Handle formatting of instructions and their options. + int FormatRegister(Instruction* instr, const char* option); + int FormatFPURegisterOrRoundMode(Instruction* instr, const char* option); + int FormatOption(Instruction* instr, const char* option); + void Format(Instruction* instr, const char* format); + void Unknown(Instruction* instr); + + const disasm::NameConverter& converter_; + v8::internal::Vector out_buffer_; + int out_buffer_pos_; + + DISALLOW_COPY_AND_ASSIGN(Decoder); +}; + +// 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)); +} + +void Decoder::PrintRs1(Instruction* instr) { + int reg = instr->Rs1Value(); + PrintRegister(reg); +} + +void Decoder::PrintRs2(Instruction* instr) { + int reg = instr->Rs2Value(); + PrintRegister(reg); +} + +void Decoder::PrintRd(Instruction* instr) { + int reg = instr->RdValue(); + PrintRegister(reg); +} + +void Decoder::PrintVs1(Instruction* instr) { + int val = instr->Rs1Value(); + out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "0x%x", val); +} + +// Print the FPUregister name according to the active name converter. +void Decoder::PrintFPURegister(int freg) { + Print(converter_.NameOfXMMRegister(freg)); +} + +void Decoder::PrintFRs1(Instruction* instr) { + int reg = instr->Rs1Value(); + PrintFPURegister(reg); +} + +void Decoder::PrintFRs2(Instruction* instr) { + int reg = instr->Rs2Value(); + PrintFPURegister(reg); +} + +void Decoder::PrintFRs3(Instruction* instr) { + int reg = instr->Rs3Value(); + PrintFPURegister(reg); +} + +void Decoder::PrintFRd(Instruction* instr) { + int reg = instr->RdValue(); + PrintFPURegister(reg); +} + +void Decoder::PrintImm12X(Instruction* instr) { + int32_t imm = instr->Imm12Value(); + out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "0x%x", imm); +} + +void Decoder::PrintImm12(Instruction* instr) { + int32_t imm = instr->Imm12Value(); + out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", imm); +} + +void Decoder::PrintBranchOffset(Instruction* instr) { + int32_t imm = instr->BranchOffset(); + const char* target = converter_.NameOfAddress(reinterpret_cast(instr) + imm); + out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d -> %s", imm, target); +} + +void Decoder::PrintStoreOffset(Instruction* instr) { + int32_t imm = instr->StoreOffset(); + out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", imm); +} + +void Decoder::PrintImm20U(Instruction* instr) { + int32_t imm = instr->Imm20UValue(); + out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "0x%x", imm); +} + +void Decoder::PrintImm20J(Instruction* instr) { + int32_t imm = instr->Imm20JValue(); + const char* target = converter_.NameOfAddress(reinterpret_cast(instr) + imm); + out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d -> %s", imm, target); +} + +void Decoder::PrintShamt(Instruction* instr) { + int32_t imm = instr->Shamt(); + out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", imm); +} + +void Decoder::PrintShamt32(Instruction* instr) { + int32_t imm = instr->Shamt32(); + out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", imm); +} + +void Decoder::PrintAcquireRelease(Instruction* instr) { + bool aq = instr->AqValue(); + bool rl = instr->RlValue(); + if (aq || rl) { + out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "."); + } + if (aq) { + out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "aq"); + } + if (rl) { + out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "rl"); + } +} + +void Decoder::PrintCSRReg(Instruction* instr) { + int32_t csr_reg = instr->CsrValue(); + std::string s; + switch (csr_reg) { + case csr_fflags: // Floating-Point Accrued Exceptions (RW) + s = "csr_fflags"; + break; + case csr_frm: // Floating-Point Dynamic Rounding Mode (RW) + s = "csr_frm"; + break; + case csr_fcsr: // Floating-Point Control and Status Register (RW) + s = "csr_fcsr"; + break; + case csr_cycle: + s = "csr_cycle"; + break; + case csr_time: + s = "csr_time"; + break; + case csr_instret: + s = "csr_instret"; + break; + case csr_cycleh: + s = "csr_cycleh"; + break; + case csr_timeh: + s = "csr_timeh"; + break; + case csr_instreth: + s = "csr_instreth"; + break; + default: + UNREACHABLE(); + } + out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%s", s.c_str()); +} + +void Decoder::PrintRoundingMode(Instruction* instr) { + int frm = instr->RoundMode(); + std::string s; + switch (frm) { + case RNE: + s = "RNE"; + break; + case RTZ: + s = "RTZ"; + break; + case RDN: + s = "RDN"; + break; + case RUP: + s = "RUP"; + break; + case RMM: + s = "RMM"; + break; + case DYN: + s = "DYN"; + break; + default: + UNREACHABLE(); + } + out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%s", s.c_str()); +} + +void Decoder::PrintMemoryOrder(Instruction* instr, bool is_pred) { + int memOrder = instr->MemoryOrder(is_pred); + std::string s; + if ((memOrder & PSI) == PSI) { + s += "i"; + } + if ((memOrder & PSO) == PSO) { + s += "o"; + } + if ((memOrder & PSR) == PSR) { + s += "r"; + } + if ((memOrder & PSW) == PSW) { + s += "w"; + } + out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%s", s.c_str()); +} + +// Printing of instruction name. +void Decoder::PrintInstructionName(Instruction* instr) {} + +// 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[12]: Rs register. + if (format[2] == '1') { + int reg = instr->Rs1Value(); + PrintRegister(reg); + return 3; + } else if (format[2] == '2') { + int reg = instr->Rs2Value(); + PrintRegister(reg); + return 3; + } + UNREACHABLE(); + } else if (format[1] == 'd') { // 'rd: rd register. + int reg = instr->RdValue(); + PrintRegister(reg); + return 2; + } + UNREACHABLE(); +} + +// Handle all FPUregister based formatting in this function to reduce the +// complexity of FormatOption. +int Decoder::FormatFPURegisterOrRoundMode(Instruction* instr, + const char* format) { + DCHECK_EQ(format[0], 'f'); + if (format[1] == 's') { // 'fs[1-3]: Rs register. + if (format[2] == '1') { + int reg = instr->Rs1Value(); + PrintFPURegister(reg); + return 3; + } else if (format[2] == '2') { + int reg = instr->Rs2Value(); + PrintFPURegister(reg); + return 3; + } else if (format[2] == '3') { + int reg = instr->Rs3Value(); + PrintFPURegister(reg); + return 3; + } + UNREACHABLE(); + } else if (format[1] == 'd') { // 'fd: fd register. + int reg = instr->RdValue(); + PrintFPURegister(reg); + return 2; + } else if (format[1] == 'r') { // 'frm + DCHECK(STRING_STARTS_WITH(format, "frm")); + PrintRoundingMode(instr); + return 3; + } + 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 'c': { // `csr: CSR registers + if (format[1] == 's') { + if (format[2] == 'r') { + PrintCSRReg(instr); + return 3; + } + } + UNREACHABLE(); + } + case 'i': { // 'imm12, 'imm12x, 'imm20U, or 'imm20J: Immediates. + if (format[3] == '1') { + if (format[4] == '2') { + DCHECK(STRING_STARTS_WITH(format, "imm12")); + if (format[5] == 'x') { + PrintImm12X(instr); + return 6; + } + PrintImm12(instr); + return 5; + } + } else if (format[3] == '2' && format[4] == '0') { + DCHECK(STRING_STARTS_WITH(format, "imm20")); + switch (format[5]) { + case 'U': + DCHECK(STRING_STARTS_WITH(format, "imm20U")); + PrintImm20U(instr); + break; + case 'J': + DCHECK(STRING_STARTS_WITH(format, "imm20J")); + PrintImm20J(instr); + break; + } + return 6; + } + UNREACHABLE(); + } + case 'o': { // 'offB or 'offS: Offsets. + if (format[3] == 'B') { + DCHECK(STRING_STARTS_WITH(format, "offB")); + PrintBranchOffset(instr); + return 4; + } else if (format[3] == 'S') { + DCHECK(STRING_STARTS_WITH(format, "offS")); + PrintStoreOffset(instr); + return 4; + } + UNREACHABLE(); + } + case 'r': { // 'r: registers. + return FormatRegister(instr, format); + } + case 'f': { // 'f: FPUregisters or `frm + return FormatFPURegisterOrRoundMode(instr, format); + } + case 'a': { // 'a: Atomic acquire and release. + PrintAcquireRelease(instr); + return 1; + } + case 'p': { // `pre + DCHECK(STRING_STARTS_WITH(format, "pre")); + PrintMemoryOrder(instr, true); + return 3; + } + case 's': { // 's32 or 's64: Shift amount. + if (format[1] == '3') { + DCHECK(STRING_STARTS_WITH(format, "s32")); + PrintShamt32(instr); + return 3; + } else if (format[1] == '6') { + DCHECK(STRING_STARTS_WITH(format, "s64")); + PrintShamt(instr); + return 3; + } else if (format[1] == 'u') { + DCHECK(STRING_STARTS_WITH(format, "suc")); + PrintMemoryOrder(instr, false); + return 3; + } + UNREACHABLE(); + } + case 'v': { // 'vs1: Raw values from register fields + DCHECK(STRING_STARTS_WITH(format, "vs1")); + PrintVs1(instr); + return 3; + } + } + 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"); } + +// RISCV Instruction Decode Routine +void Decoder::DecodeRType(Instruction* instr) { + switch (instr->InstructionBits() & kRTypeMask) { + case RO_ADD: + Format(instr, "add 'rd, 'rs1, 'rs2"); + break; + case RO_SUB: + if (instr->Rs1Value() == zero_reg.code()) + Format(instr, "neg 'rd, rs2"); + else + Format(instr, "sub 'rd, 'rs1, 'rs2"); + break; + case RO_SLL: + Format(instr, "sll 'rd, 'rs1, 'rs2"); + break; + case RO_SLT: + if (instr->Rs2Value() == zero_reg.code()) + Format(instr, "sltz 'rd, 'rs1"); + else if (instr->Rs1Value() == zero_reg.code()) + Format(instr, "sgtz 'rd, 'rs2"); + else + Format(instr, "slt 'rd, 'rs1, 'rs2"); + break; + case RO_SLTU: + if (instr->Rs1Value() == zero_reg.code()) + Format(instr, "snez 'rd, 'rs2"); + else + Format(instr, "sltu 'rd, 'rs1, 'rs2"); + break; + case RO_XOR: + Format(instr, "xor 'rd, 'rs1, 'rs2"); + break; + case RO_SRL: + Format(instr, "srl 'rd, 'rs1, 'rs2"); + break; + case RO_SRA: + Format(instr, "sra 'rd, 'rs1, 'rs2"); + break; + case RO_OR: + Format(instr, "or 'rd, 'rs1, 'rs2"); + break; + case RO_AND: + Format(instr, "and 'rd, 'rs1, 'rs2"); + break; +#ifdef V8_TARGET_ARCH_64_BIT + case RO_ADDW: + Format(instr, "addw 'rd, 'rs1, 'rs2"); + break; + case RO_SUBW: + if (instr->Rs1Value() == zero_reg.code()) + Format(instr, "negw 'rd, 'rs2"); + else + Format(instr, "subw 'rd, 'rs1, 'rs2"); + break; + case RO_SLLW: + Format(instr, "sllw 'rd, 'rs1, 'rs2"); + break; + case RO_SRLW: + Format(instr, "srlw 'rd, 'rs1, 'rs2"); + break; + case RO_SRAW: + Format(instr, "sraw 'rd, 'rs1, 'rs2"); + break; +#endif /* V8_TARGET_ARCH_64_BIT */ + // TODO: Add RISCV M extension macro + case RO_MUL: + Format(instr, "mul 'rd, 'rs1, 'rs2"); + break; + case RO_MULH: + Format(instr, "mulh 'rd, 'rs1, 'rs2"); + break; + case RO_MULHSU: + Format(instr, "mulhsu 'rd, 'rs1, 'rs2"); + break; + case RO_MULHU: + Format(instr, "mulhu 'rd, 'rs1, 'rs2"); + break; + case RO_DIV: + Format(instr, "div 'rd, 'rs1, 'rs2"); + break; + case RO_DIVU: + Format(instr, "divu 'rd, 'rs1, 'rs2"); + break; + case RO_REM: + Format(instr, "rem 'rd, 'rs1, 'rs2"); + break; + case RO_REMU: + Format(instr, "remu 'rd, 'rs1, 'rs2"); + break; +#ifdef V8_TARGET_ARCH_64_BIT + case RO_MULW: + Format(instr, "mulw 'rd, 'rs1, 'rs2"); + break; + case RO_DIVW: + Format(instr, "divw 'rd, 'rs1, 'rs2"); + break; + case RO_DIVUW: + Format(instr, "divuw 'rd, 'rs1, 'rs2"); + break; + case RO_REMW: + Format(instr, "remw 'rd, 'rs1, 'rs2"); + break; + case RO_REMUW: + Format(instr, "remuw 'rd, 'rs1, 'rs2"); + break; +#endif /*V8_TARGET_ARCH_64_BIT*/ + // TODO: End Add RISCV M extension macro + default: { + switch (instr->BaseOpcode()) { + case AMO: + DecodeRAType(instr); + break; + case OP_FP: + DecodeRFPType(instr); + break; + default: + UNSUPPORTED_RISCV(); + } + } + } +} + +void Decoder::DecodeRAType(Instruction* instr) { + // TODO: Add macro for RISCV A extension + // Special handling for A extension instructions because it uses func5 + // For all A extension instruction, V8 simulator is pure sequential. No + // Memory address lock or other synchronizaiton behaviors. + switch (instr->InstructionBits() & kRATypeMask) { + case RO_LR_W: + Format(instr, "lr.w'a 'rd, ('rs1)"); + break; + case RO_SC_W: + Format(instr, "sc.w'a 'rd, 'rs2, ('rs1)"); + break; + case RO_AMOSWAP_W: + Format(instr, "amoswap.w'a 'rd, 'rs2, ('rs1)"); + break; + case RO_AMOADD_W: + Format(instr, "amoadd.w'a 'rd, 'rs2, ('rs1)"); + break; + case RO_AMOXOR_W: + Format(instr, "amoxor.w'a 'rd, 'rs2, ('rs1)"); + break; + case RO_AMOAND_W: + Format(instr, "amoand.w'a 'rd, 'rs2, ('rs1)"); + break; + case RO_AMOOR_W: + Format(instr, "amoor.w'a 'rd, 'rs2, ('rs1)"); + break; + case RO_AMOMIN_W: + Format(instr, "amomin.w'a 'rd, 'rs2, ('rs1)"); + break; + case RO_AMOMAX_W: + Format(instr, "amomax.w'a 'rd, 'rs2, ('rs1)"); + break; + case RO_AMOMINU_W: + Format(instr, "amominu.w'a 'rd, 'rs2, ('rs1)"); + break; + case RO_AMOMAXU_W: + Format(instr, "amomaxu.w'a 'rd, 'rs2, ('rs1)"); + break; +#ifdef V8_TARGET_ARCH_64_BIT + case RO_LR_D: + Format(instr, "lr.d'a 'rd, ('rs1)"); + break; + case RO_SC_D: + Format(instr, "sc.d'a 'rd, 'rs2, ('rs1)"); + break; + case RO_AMOSWAP_D: + Format(instr, "amoswap.d'a 'rd, 'rs2, ('rs1)"); + break; + case RO_AMOADD_D: + Format(instr, "amoadd.d'a 'rd, 'rs2, ('rs1)"); + break; + case RO_AMOXOR_D: + Format(instr, "amoxor.d'a 'rd, 'rs2, ('rs1)"); + break; + case RO_AMOAND_D: + Format(instr, "amoand.d'a 'rd, 'rs2, ('rs1)"); + break; + case RO_AMOOR_D: + Format(instr, "amoor.d'a 'rd, 'rs2, ('rs1)"); + break; + case RO_AMOMIN_D: + Format(instr, "amomin.d'a 'rd, 'rs2, ('rs1)"); + break; + case RO_AMOMAX_D: + Format(instr, "amoswap.d'a 'rd, 'rs2, ('rs1)"); + break; + case RO_AMOMINU_D: + Format(instr, "amominu.d'a 'rd, 'rs2, ('rs1)"); + break; + case RO_AMOMAXU_D: + Format(instr, "amomaxu.d'a 'rd, 'rs2, ('rs1)"); + break; +#endif /*V8_TARGET_ARCH_64_BIT*/ + // TODO: End Add macro for RISCV A extension + default: { + UNSUPPORTED_RISCV(); + } + } +} + +void Decoder::DecodeRFPType(Instruction* instr) { + // OP_FP instructions (F/D) uses func7 first. Some further uses fun3 and rs2() + + // kRATypeMask is only for func7 + switch (instr->InstructionBits() & kRFPTypeMask) { + // TODO: Add macro for RISCV F extension + case RO_FADD_S: + Format(instr, "fadd.s 'fd, 'fs1, 'fs2"); + break; + case RO_FSUB_S: + Format(instr, "fsub.s 'fd, 'fs1, 'fs2"); + break; + case RO_FMUL_S: + Format(instr, "fmul.s 'fd, 'fs1, 'fs2"); + break; + case RO_FDIV_S: + Format(instr, "fdiv.s 'fd, 'fs1, 'fs2"); + break; + case RO_FSQRT_S: + Format(instr, "fsqrt.s 'fd, 'fs1"); + break; + case RO_FSGNJ_S: { // RO_FSGNJN_S RO_FSGNJX_S + switch (instr->Funct3Value()) { + case 0b000: // RO_FSGNJ_S + if (instr->Rs1Value() == instr->Rs2Value()) + Format(instr, "fmv.s 'fd, 'fs1"); + else + Format(instr, "fsgnj.s 'fd, 'fs1, 'fs2"); + break; + case 0b001: // RO_FSGNJN_S + if (instr->Rs1Value() == instr->Rs2Value()) + Format(instr, "fneg.s 'fd, 'fs1"); + else + Format(instr, "fsgnjn.s 'fd, 'fs1, 'fs2"); + break; + case 0b010: // RO_FSGNJX_S + if (instr->Rs1Value() == instr->Rs2Value()) + Format(instr, "fabs.s 'fd, 'fs1"); + else + Format(instr, "fsgnjx.s 'fd, 'fs1, 'fs2"); + break; + default: + UNSUPPORTED_RISCV(); + } + break; + } + case RO_FMIN_S: { // RO_FMAX_S + switch (instr->Funct3Value()) { + case 0b000: // RO_FMIN_S + Format(instr, "fmin.s 'fd, 'fs1, 'fs2"); + break; + case 0b001: // RO_FMAX_S + Format(instr, "fmax.s 'fd, 'fs1, 'fs2"); + break; + default: + UNSUPPORTED_RISCV(); + } + break; + } + case RO_FCVT_W_S: { // RO_FCVT_WU_S , 64F RO_FCVT_L_S RO_FCVT_LU_S + switch (instr->Rs2Value()) { + case 0b00000: // RO_FCVT_W_S + Format(instr, "fcvt.w.s ['frm] 'rd, 'fs1"); + break; + case 0b00001: // RO_FCVT_WU_S + Format(instr, "fcvt.wu.s ['frm] 'rd, 'fs1"); + break; +#ifdef V8_TARGET_ARCH_64_BIT + case 0b00010: // RO_FCVT_L_S + Format(instr, "fcvt.l.s ['frm] 'rd, 'fs1"); + break; + case 0b00011: // RO_FCVT_LU_S + Format(instr, "fcvt.lu.s ['frm] 'rd, 'fs1"); + break; +#endif /* V8_TARGET_ARCH_64_BIT */ + default: + UNSUPPORTED_RISCV(); + } + break; + } + case RO_FMV: { // RO_FCLASS_S + if (instr->Rs2Value() != 0b00000) { + UNSUPPORTED_RISCV(); + } + switch (instr->Funct3Value()) { + case 0b000: // RO_FMV_X_W + Format(instr, "fmv.x.w 'rd, 'fs1"); + break; + case 0b001: // RO_FCLASS_S + Format(instr, "fclass.s 'rd, 'fs1"); + break; + default: + UNSUPPORTED_RISCV(); + } + break; + } + case RO_FLE_S: { // RO_FEQ_S RO_FLT_S RO_FLE_S + switch (instr->Funct3Value()) { + case 0b010: // RO_FEQ_S + Format(instr, "feq.s 'rd, 'fs1, 'fs2"); + break; + case 0b001: // RO_FLT_S + Format(instr, "flt.s 'rd, 'fs1, 'fs2"); + break; + case 0b000: // RO_FLE_S + Format(instr, "fle.s 'rd, 'fs1, 'fs2"); + break; + default: + UNSUPPORTED_RISCV(); + } + break; + } + case RO_FCVT_S_W: { // RO_FCVT_S_WU , 64F RO_FCVT_S_L RO_FCVT_S_LU + switch (instr->Rs2Value()) { + case 0b00000: // RO_FCVT_S_W + Format(instr, "fcvt.s.w 'fd, 'rs1"); + break; + case 0b00001: // RO_FCVT_S_WU + Format(instr, "fcvt.s.wu 'fd, 'rs1"); + break; +#ifdef V8_TARGET_ARCH_64_BIT + case 0b00010: // RO_FCVT_S_L + Format(instr, "fcvt.s.l 'fd, 'rs1"); + break; + case 0b00011: // RO_FCVT_S_LU + Format(instr, "fcvt.s.lu 'fd, 'rs1"); + break; +#endif /* V8_TARGET_ARCH_64_BIT */ + default: { + UNSUPPORTED_RISCV(); + } + } + break; + } + case RO_FMV_W_X: { + if (instr->Funct3Value() == 0b000) { + Format(instr, "fmv.w.x 'fd, 'rs1"); + } else { + UNSUPPORTED_RISCV(); + } + break; + } + // TODO: Add macro for RISCV D extension + case RO_FADD_D: + Format(instr, "fadd.d 'fd, 'fs1, 'fs2"); + break; + case RO_FSUB_D: + Format(instr, "fsub.d 'fd, 'fs1, 'fs2"); + break; + case RO_FMUL_D: + Format(instr, "fmul.d 'fd, 'fs1, 'fs2"); + break; + case RO_FDIV_D: + Format(instr, "fdiv.d 'fd, 'fs1, 'fs2"); + break; + case RO_FSQRT_D: { + if (instr->Rs2Value() == 0b00000) { + Format(instr, "fsqrt.d 'fd, 'fs1"); + } else { + UNSUPPORTED_RISCV(); + } + break; + } + case RO_FSGNJ_D: { // RO_FSGNJN_D RO_FSGNJX_D + switch (instr->Funct3Value()) { + case 0b000: // RO_FSGNJ_D + if (instr->Rs1Value() == instr->Rs2Value()) + Format(instr, "fmv.d 'fd, 'fs1"); + else + Format(instr, "fsgnj.d 'fd, 'fs1, 'fs2"); + break; + case 0b001: // RO_FSGNJN_D + if (instr->Rs1Value() == instr->Rs2Value()) + Format(instr, "fneg.d 'fd, 'fs1"); + else + Format(instr, "fsgnjn.d 'fd, 'fs1, 'fs2"); + break; + case 0b010: // RO_FSGNJX_D + if (instr->Rs1Value() == instr->Rs2Value()) + Format(instr, "fabs.d 'fd, 'fs1"); + else + Format(instr, "fsgnjx.d 'fd, 'fs1, 'fs2"); + break; + default: + UNSUPPORTED_RISCV(); + } + break; + } + case RO_FMIN_D: { // RO_FMAX_D + switch (instr->Funct3Value()) { + case 0b000: // RO_FMIN_D + Format(instr, "fmin.d 'fd, 'fs1, 'fs2"); + break; + case 0b001: // RO_FMAX_D + Format(instr, "fmax.d 'fd, 'fs1, 'fs2"); + break; + default: + UNSUPPORTED_RISCV(); + } + break; + } + case (RO_FCVT_S_D & kRFPTypeMask): { + if (instr->Rs2Value() == 0b00001) { + Format(instr, "fcvt.s.d ['frm] 'fd, 'rs1"); + } else { + UNSUPPORTED_RISCV(); + } + break; + } + case RO_FCVT_D_S: { + if (instr->Rs2Value() == 0b00000) { + Format(instr, "fcvt.d.s 'fd, 'fs1"); + } else { + UNSUPPORTED_RISCV(); + } + break; + } + case RO_FLE_D: { // RO_FEQ_D RO_FLT_D RO_FLE_D + switch (instr->Funct3Value()) { + case 0b010: // RO_FEQ_S + Format(instr, "feq.d 'rd, 'fs1, 'fs2"); + break; + case 0b001: // RO_FLT_D + Format(instr, "flt.d 'rd, 'fs1, 'fs2"); + break; + case 0b000: // RO_FLE_D + Format(instr, "fle.d 'rd, 'fs1, 'fs2"); + break; + default: + UNSUPPORTED_RISCV(); + } + break; + } + case (RO_FCLASS_D & kRFPTypeMask): { // RO_FCLASS_D , 64D RO_FMV_X_D + if (instr->Rs2Value() != 0b00000) { + UNSUPPORTED_RISCV(); + break; + } + switch (instr->Funct3Value()) { + case 0b001: // RO_FCLASS_D + Format(instr, "fclass.d 'rd, 'fs1"); + break; +#ifdef V8_TARGET_ARCH_64_BIT + case 0b000: // RO_FMV_X_D + Format(instr, "fmv.x.d 'rd, 'fs1"); + break; +#endif /* V8_TARGET_ARCH_64_BIT */ + default: + UNSUPPORTED_RISCV(); + } + break; + } + case RO_FCVT_W_D: { // RO_FCVT_WU_D , 64F RO_FCVT_L_D RO_FCVT_LU_D + switch (instr->Rs2Value()) { + case 0b00000: // RO_FCVT_W_D + Format(instr, "fcvt.w.d ['frm] 'rd, 'fs1"); + break; + case 0b00001: // RO_FCVT_WU_D + Format(instr, "fcvt.wu.d ['frm] 'rd, 'fs1"); + break; +#ifdef V8_TARGET_ARCH_64_BIT + case 0b00010: // RO_FCVT_L_D + Format(instr, "fcvt.l.d ['frm] 'rd, 'fs1"); + break; + case 0b00011: // RO_FCVT_LU_D + Format(instr, "fcvt.lu.d ['frm] 'rd, 'fs1"); + break; +#endif /* V8_TARGET_ARCH_64_BIT */ + default: + UNSUPPORTED_RISCV(); + } + break; + } + case RO_FCVT_D_W: { // RO_FCVT_D_WU , 64F RO_FCVT_D_L RO_FCVT_D_LU + switch (instr->Rs2Value()) { + case 0b00000: // RO_FCVT_D_W + Format(instr, "fcvt.d.w 'fd, 'rs1"); + break; + case 0b00001: // RO_FCVT_D_WU + Format(instr, "fcvt.d.wu 'fd, 'rs1"); + break; +#ifdef V8_TARGET_ARCH_64_BIT + case 0b00010: // RO_FCVT_D_L + Format(instr, "fcvt.d.l 'fd, 'rs1"); + break; + case 0b00011: // RO_FCVT_D_LU + Format(instr, "fcvt.d.lu 'fd, 'rs1"); + break; +#endif /* V8_TARGET_ARCH_64_BIT */ + default: + UNSUPPORTED_RISCV(); + } + break; + } +#ifdef V8_TARGET_ARCH_64_BIT + case RO_FMV_D_X: { + if (instr->Funct3Value() == 0b000 && instr->Rs2Value() == 0b00000) { + Format(instr, "fmv.d.x 'fd, 'rs1"); + } else { + UNSUPPORTED_RISCV(); + } + break; + } +#endif /* V8_TARGET_ARCH_64_BIT */ + default: { + UNSUPPORTED_RISCV(); + } + } +} + +void Decoder::DecodeR4Type(Instruction* instr) { + switch (instr->InstructionBits() & kR4TypeMask) { + // TODO: use F Extension macro block + case RO_FMADD_S: + Format(instr, "fmadd.s 'fd, 'fs1, 'fs2, 'fs3"); + break; + case RO_FMSUB_S: + Format(instr, "fmsub.s 'fd, 'fs1, 'fs2, 'fs3"); + break; + case RO_FNMSUB_S: + Format(instr, "fnmsub.s 'fd, 'fs1, 'fs2, 'fs3"); + break; + case RO_FNMADD_S: + Format(instr, "fnmadd.s 'fd, 'fs1, 'fs2, 'fs3"); + break; + // TODO: use F Extension macro block + case RO_FMADD_D: + Format(instr, "fmadd.d 'fd, 'fs1, 'fs2, 'fs3"); + break; + case RO_FMSUB_D: + Format(instr, "fmsub.d 'fd, 'fs1, 'fs2, 'fs3"); + break; + case RO_FNMSUB_D: + Format(instr, "fnmsub.d 'fd, 'fs1, 'fs2, 'fs3"); + break; + case RO_FNMADD_D: + Format(instr, "fnmadd.d 'fd, 'fs1, 'fs2, 'fs3"); + break; + default: + UNSUPPORTED_RISCV(); + } +} + +void Decoder::DecodeIType(Instruction* instr) { + switch (instr->InstructionBits() & kITypeMask) { + case RO_JALR: + if (instr->RdValue() == zero_reg.code() && + instr->Rs1Value() == ra.code() && instr->Imm12Value() == 0) + Format(instr, "ret"); + else if (instr->RdValue() == zero_reg.code() && instr->Imm12Value() == 0) + Format(instr, "jr 'rs1"); + else if (instr->RdValue() == ra.code() && instr->Imm12Value() == 0) + Format(instr, "jalr 'rs1"); + else + Format(instr, "jalr 'rd, 'imm12('rs1)"); + break; + case RO_LB: + Format(instr, "lb 'rd, 'imm12('rs1)"); + break; + case RO_LH: + Format(instr, "lh 'rd, 'imm12('rs1)"); + break; + case RO_LW: + Format(instr, "lw 'rd, 'imm12('rs1)"); + break; + case RO_LBU: + Format(instr, "lbu 'rd, 'imm12('rs1)"); + break; + case RO_LHU: + Format(instr, "lhu 'rd, 'imm12('rs1)"); + break; +#ifdef V8_TARGET_ARCH_64_BIT + case RO_LWU: + Format(instr, "lwu 'rd, 'imm12('rs1)"); + break; + case RO_LD: + Format(instr, "ld 'rd, 'imm12('rs1)"); + break; +#endif /*V8_TARGET_ARCH_64_BIT*/ + case RO_ADDI: + if (instr->Imm12Value() == 0) { + if (instr->RdValue() == zero_reg.code() && + instr->Rs1Value() == zero_reg.code()) + Format(instr, "nop"); + else + Format(instr, "mv 'rd, 'rs1"); + } else if (instr->Rs1Value() == zero_reg.code()) { + Format(instr, "li 'rd, 'imm12"); + } else { + Format(instr, "addi 'rd, 'rs1, 'imm12"); + } + break; + case RO_SLTI: + Format(instr, "slti 'rd, 'rs1, 'imm12"); + break; + case RO_SLTIU: + if (instr->Imm12Value() == 1) + Format(instr, "seqz 'rd, 'rs1"); + else + Format(instr, "sltiu 'rd, 'rs1, 'imm12"); + break; + case RO_XORI: + if (instr->Imm12Value() == -1) + Format(instr, "not 'rd, 'rs1"); + else + Format(instr, "xori 'rd, 'rs1, 'imm12x"); + break; + case RO_ORI: + Format(instr, "ori 'rd, 'rs1, 'imm12x"); + break; + case RO_ANDI: + Format(instr, "andi 'rd, 'rs1, 'imm12x"); + break; + case RO_SLLI: + Format(instr, "slli 'rd, 'rs1, 's64"); + break; + case RO_SRLI: { // RO_SRAI + if (!instr->IsArithShift()) { + Format(instr, "srli 'rd, 'rs1, 's64"); + } else { + Format(instr, "srai 'rd, 'rs1, 's64"); + } + break; + } +#ifdef V8_TARGET_ARCH_64_BIT + case RO_ADDIW: + if (instr->Imm12Value() == 0) + Format(instr, "sext.w 'rd, 'rs1"); + else + Format(instr, "addiw 'rd, 'rs1, 'imm12"); + break; + case RO_SLLIW: + Format(instr, "slliw 'rd, 'rs1, 's32"); + break; + case RO_SRLIW: { // RO_SRAIW + if (!instr->IsArithShift()) { + Format(instr, "srliw 'rd, 'rs1, 's32"); + } else { + Format(instr, "sraiw 'rd, 'rs1, 's32"); + } + break; + } +#endif /*V8_TARGET_ARCH_64_BIT*/ + case RO_FENCE: + if (instr->MemoryOrder(true) == PSIORW && + instr->MemoryOrder(false) == PSIORW) + Format(instr, "fence"); + else + Format(instr, "fence 'pre, 'suc"); + break; + case RO_ECALL: { // RO_EBREAK + if (instr->Imm12Value() == 0) { // ECALL + Format(instr, "ecall"); + } else if (instr->Imm12Value() == 1) { // EBREAK + Format(instr, "ebreak"); + } else { + UNSUPPORTED_RISCV(); + } + break; + } + // TODO: use Zifencei Standard Extension macro block + case RO_FENCE_I: + Format(instr, "fence.i"); + break; + // TODO: use Zicsr Standard Extension macro block + // FIXME(RISC-V): Add special formatting for CSR registers + case RO_CSRRW: + if (instr->CsrValue() == csr_fcsr) { + if (instr->RdValue() == zero_reg.code()) + Format(instr, "fscsr 'rs1"); + else + Format(instr, "fscsr 'rd, 'rs1"); + } else if (instr->CsrValue() == csr_frm) { + if (instr->RdValue() == zero_reg.code()) + Format(instr, "fsrm 'rs1"); + else + Format(instr, "fsrm 'rd, 'rs1"); + } else if (instr->CsrValue() == csr_fflags) { + if (instr->RdValue() == zero_reg.code()) + Format(instr, "fsflags 'rs1"); + else + Format(instr, "fsflags 'rd, 'rs1"); + } else if (instr->RdValue() == zero_reg.code()) { + Format(instr, "csrw 'csr, 'rs1"); + } else { + Format(instr, "csrrw 'rd, 'csr, 'rs1"); + } + break; + case RO_CSRRS: + if (instr->Rs1Value() == zero_reg.code()) { + switch (instr->CsrValue()) { + case csr_instret: + Format(instr, "rdinstret 'rd"); + break; + case csr_instreth: + Format(instr, "rdinstreth 'rd"); + break; + case csr_time: + Format(instr, "rdtime 'rd"); + break; + case csr_timeh: + Format(instr, "rdtimeh 'rd"); + break; + case csr_cycle: + Format(instr, "rdcycle 'rd"); + break; + case csr_cycleh: + Format(instr, "rdcycleh 'rd"); + break; + case csr_fflags: + Format(instr, "frflags 'rd"); + break; + case csr_frm: + Format(instr, "frrm 'rd"); + break; + case csr_fcsr: + Format(instr, "frcsr 'rd"); + break; + default: + UNREACHABLE(); + } + } else if (instr->Rs1Value() == zero_reg.code()) { + Format(instr, "csrr 'rd, 'csr"); + } else if (instr->RdValue() == zero_reg.code()) { + Format(instr, "csrs 'csr, 'rs1"); + } else + Format(instr, "csrrs 'rd, 'csr, 'rs1"); + break; + case RO_CSRRC: + if (instr->RdValue() == zero_reg.code()) + Format(instr, "csrc 'csr, 'rs1"); + else + Format(instr, "csrrc 'rd, 'csr, 'rs1"); + break; + case RO_CSRRWI: + if (instr->RdValue() == zero_reg.code()) + Format(instr, "csrwi 'csr, 'vs1"); + else + Format(instr, "csrrwi 'rd, 'csr, 'vs1"); + break; + case RO_CSRRSI: + if (instr->RdValue() == zero_reg.code()) + Format(instr, "csrsi 'csr, 'vs1"); + else + Format(instr, "csrrsi 'rd, 'csr, 'vs1"); + break; + case RO_CSRRCI: + if (instr->RdValue() == zero_reg.code()) + Format(instr, "csrci 'csr, 'vs1"); + else + Format(instr, "csrrci 'rd, 'csr, 'vs1"); + break; + // TODO: use F Extension macro block + case RO_FLW: + Format(instr, "flw 'fd, 'imm12('rs1)"); + break; + // TODO: use D Extension macro block + case RO_FLD: + Format(instr, "fld 'fd, 'imm12('rs1)"); + break; + default: + UNSUPPORTED_RISCV(); + } +} + +void Decoder::DecodeSType(Instruction* instr) { + switch (instr->InstructionBits() & kSTypeMask) { + case RO_SB: + Format(instr, "sb 'rs2, 'offS('rs1)"); + break; + case RO_SH: + Format(instr, "sh 'rs2, 'offS('rs1)"); + break; + case RO_SW: + Format(instr, "sw 'rs2, 'offS('rs1)"); + break; +#ifdef V8_TARGET_ARCH_64_BIT + case RO_SD: + Format(instr, "sd 'rs2, 'offS('rs1)"); + break; +#endif /*V8_TARGET_ARCH_64_BIT*/ + // TODO: use F Extension macro block + case RO_FSW: + Format(instr, "fsw 'fs2, 'offS('rs1)"); + break; + // TODO: use D Extension macro block + case RO_FSD: + Format(instr, "fsd 'fs2, 'offS('rs1)"); + break; + default: + UNSUPPORTED_RISCV(); + } +} + +void Decoder::DecodeBType(Instruction* instr) { + switch (instr->InstructionBits() & kBTypeMask) { + case RO_BEQ: + Format(instr, "beq 'rs1, 'rs2, 'offB"); + break; + case RO_BNE: + Format(instr, "bne 'rs1, 'rs2, 'offB"); + break; + case RO_BLT: + Format(instr, "blt 'rs1, 'rs2, 'offB"); + break; + case RO_BGE: + Format(instr, "bge 'rs1, 'rs2, 'offB"); + break; + case RO_BLTU: + Format(instr, "bltu 'rs1, 'rs2, 'offB"); + break; + case RO_BGEU: + Format(instr, "bgeu 'rs1, 'rs2, 'offB"); + break; + default: + UNSUPPORTED_RISCV(); + } +} +void Decoder::DecodeUType(Instruction* instr) { + // U Type doesn't have additional mask + switch (instr->BaseOpcodeFieldRaw()) { + case RO_LUI: + Format(instr, "lui 'rd, 'imm20U"); + break; + case RO_AUIPC: + Format(instr, "auipc 'rd, 'imm20U"); + break; + default: + UNSUPPORTED_RISCV(); + } +} +void Decoder::DecodeJType(Instruction* instr) { + // J Type doesn't have additional mask + switch (instr->BaseOpcodeValue()) { + case RO_JAL: + if (instr->RdValue() == zero_reg.code()) + Format(instr, "j 'imm20J"); + else if (instr->RdValue() == ra.code()) + Format(instr, "jal 'imm20J"); + else + Format(instr, "jal 'rd, 'imm20J"); + break; + default: + UNSUPPORTED_RISCV(); + } +} + +// 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); + // Print raw instruction bytes. + out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%08x ", + instr->InstructionBits()); + switch (instr->InstructionType()) { + case Instruction::kRType: + DecodeRType(instr); + break; + case Instruction::kR4Type: + DecodeR4Type(instr); + break; + case Instruction::kIType: + DecodeIType(instr); + break; + case Instruction::kSType: + DecodeSType(instr); + break; + case Instruction::kBType: + DecodeBType(instr); + break; + case Instruction::kUType: + DecodeUType(instr); + break; + case Instruction::kJType: + DecodeJType(instr); + break; + default: + Format(instr, "UNSUPPORTED"); + UNSUPPORTED_RISCV(); + } + return kInstrSize; +} + +} // namespace internal +} // namespace v8 + +//------------------------------------------------------------------------------ + +namespace disasm { + +const char* NameConverter::NameOfAddress(byte* addr) const { + v8::internal::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(); // RISC-V does not have the concept of a byte register. + return "nobytereg"; +} + +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::internal::Vector buffer, + byte* instruction) { + v8::internal::Decoder d(converter_, buffer); + return d.InstructionDecode(instruction); +} + +// The RISC-V 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::internal::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_RISCV64 Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/execution/frame-constants.h =================================================================== --- qtwebengine-everywhere-src-5.15.3.orig/src/3rdparty/chromium/v8/src/execution/frame-constants.h +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/execution/frame-constants.h @@ -356,6 +356,10 @@ inline static int FrameSlotToFPOffset(in #include "src/execution/mips64/frame-constants-mips64.h" // NOLINT #elif V8_TARGET_ARCH_S390 #include "src/execution/s390/frame-constants-s390.h" // NOLINT +#elif V8_TARGET_ARCH_RISCV64 +#include "src/execution/riscv64/frame-constants-riscv64.h" // NOLINT +#elif V8_TARGET_ARCH_RISCV +#include "src/execution/riscv/frame-constants-riscv.h" // NOLINT #else #error Unsupported target architecture. #endif Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/execution/riscv64/frame-constants-riscv64.cc =================================================================== --- /dev/null +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/execution/riscv64/frame-constants-riscv64.cc @@ -0,0 +1,32 @@ +// 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_RISCV64 + +#include "src/codegen/riscv64/assembler-riscv64-inl.h" +#include "src/execution/frame-constants.h" +#include "src/execution/frames.h" + +#include "src/execution/riscv64/frame-constants-riscv64.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 InterpreterFrameConstants::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_RISCV64 Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/execution/riscv64/frame-constants-riscv64.h =================================================================== --- /dev/null +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/execution/riscv64/frame-constants-riscv64.h @@ -0,0 +1,87 @@ +// 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_RISCV_FRAME_CONSTANTS_RISCV_H_ +#define V8_EXECUTION_RISCV_FRAME_CONSTANTS_RISCV_H_ + +#include "src/base/bits.h" +#include "src/base/macros.h" +#include "src/execution/frame-constants.h" +#include "src/wasm/baseline/liftoff-assembler-defs.h" +#include "src/wasm/wasm-linkage.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 = + -(StandardFrameConstants::kFixedFrameSizeFromFp + kPointerSize); +}; + +class WasmCompileLazyFrameConstants : public TypedFrameConstants { + public: + static constexpr int kNumberOfSavedGpParamRegs = + arraysize(wasm::kGpParamRegisters); + static constexpr int kNumberOfSavedFpParamRegs = + arraysize(wasm::kFpParamRegisters); + + // FP-relative. + // Builtins::Generate_WasmCompileLazy pushes WasmInstance to the stack after + // pushing SavedGPParamRegs and SavedFpParamRegs onto the stack, therefore + // kWasmInstanceOffset is setup as such + static constexpr int kWasmInstanceOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET( + kNumberOfSavedGpParamRegs + kNumberOfSavedGpParamRegs); + static constexpr int kFixedFrameSizeFromFp = + TypedFrameConstants::kFixedFrameSizeFromFp + + kNumberOfSavedGpParamRegs * kPointerSize + + kNumberOfSavedFpParamRegs * kDoubleSize; +}; + +// 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: + // constexpr RegList kLiftoffAssemblerGpCacheRegs = + // Register::ListOf(a0, a1, a2, a3, a4, a5, a6, a7, t0, t1, t2, s7); + static constexpr uint32_t kPushedGpRegs = wasm::kLiftoffAssemblerGpCacheRegs; + + // constexpr RegList kLiftoffAssemblerFpCacheRegs = DoubleRegister::ListOf( + // ft0, ft1, ft2, ft3, ft4, ft5, ft6, ft7, fa0, fa1, fa2, fa3, fa4, fa5, + // fa6, fa7, ft8, ft9, ft10, ft11); + static constexpr uint32_t kPushedFpRegs = wasm::kLiftoffAssemblerFpCacheRegs; + + static constexpr int kNumPushedGpRegisters = + base::bits::CountPopulation(kPushedGpRegs); + static constexpr int kNumPushedFpRegisters = + base::bits::CountPopulation(kPushedFpRegs); + + 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 & (1 << reg_code)); + uint32_t lower_regs = kPushedGpRegs & ((uint32_t{1} << reg_code) - 1); + return kLastPushedGpRegisterOffset + + base::bits::CountPopulation(lower_regs) * kSystemPointerSize; + } + + static int GetPushedFpRegisterOffset(int reg_code) { + DCHECK_NE(0, kPushedFpRegs & (1 << reg_code)); + uint32_t lower_regs = kPushedFpRegs & ((uint32_t{1} << reg_code) - 1); + return kLastPushedFpRegisterOffset + + base::bits::CountPopulation(lower_regs) * kDoubleSize; + } +}; + +} // namespace internal +} // namespace v8 + +#endif // V8_EXECUTION_RISCV_FRAME_CONSTANTS_RISCV_H_ Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/execution/riscv64/simulator-riscv64.cc =================================================================== --- /dev/null +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/execution/riscv64/simulator-riscv64.cc @@ -0,0 +1,3509 @@ +// 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. + +// Copyright(c) 2010 - 2017, +// The Regents of the University of California(Regents).All Rights Reserved. +// +// Redistribution and use in source and binary forms, +// with or without modification, +// are permitted provided that the following +// conditions are met : 1. Redistributions of source code must retain the +// above copyright notice, this list of conditions and the following +// disclaimer.2. Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer in +// the +// documentation and / +// or +// other materials provided with the distribution.3. Neither the name of +// the Regents nor the names of its contributors may be used to endorse +// or +// promote products derived from +// this software without specific prior written permission. +// +// IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, +// INDIRECT, SPECIAL, +// INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, +// ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, +// EVEN IF REGENTS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +// PARTICULAR PURPOSE.THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, +// IF ANY, +// PROVIDED HEREUNDER IS PROVIDED +// "AS IS".REGENTS HAS NO OBLIGATION TO PROVIDE MAINTENANCE, +// SUPPORT, UPDATES, ENHANCEMENTS, +// OR MODIFICATIONS. + +// The original source code covered by the above license above has been +// modified significantly by the v8 project authors. + +#include "src/execution/riscv64/simulator-riscv64.h" + +// Only build the simulator if not compiling for real RISCV hardware. +#if defined(USE_SIMULATOR) + +#include +#include +#include +#include + +#include + +#include "src/base/bits.h" +#include "src/codegen/assembler-inl.h" +#include "src/codegen/macro-assembler.h" +#include "src/codegen/riscv64/constants-riscv64.h" +#include "src/diagnostics/disasm.h" +#include "src/heap/combined-heap.h" +#include "src/runtime/runtime-utils.h" +#include "src/utils/ostreams.h" +#include "src/utils/vector.h" + +namespace v8 { +namespace internal { + +DEFINE_LAZY_LEAKY_OBJECT_GETTER(Simulator::GlobalMonitor, + Simulator::GlobalMonitor::Get) + +// Util functions. +inline bool HaveSameSign(int64_t a, int64_t b) { return ((a ^ b) >= 0); } + +uint32_t get_fcsr_condition_bit(uint32_t cc) { + if (cc == 0) { + return 23; + } else { + return 24 + cc; + } +} + +// Generated by Assembler::break_()/stop(), ebreak code is passed as immediate +// field of a subsequent LUI instruction; otherwise returns -1 +static inline int32_t get_ebreak_code(Instruction* instr) { + DCHECK(instr->InstructionBits() == kBreakInstr); + byte* cur = reinterpret_cast(instr); + Instruction* next_instr = reinterpret_cast(cur + kInstrSize); + if (next_instr->BaseOpcodeFieldRaw() == RO_LUI) + return (next_instr->Imm20UValue()); + else + return -1; +} + +// This macro provides a platform independent use of sscanf. The reason for +// SScanF not being implemented in a platform independent was through +// ::v8::internal::OS in the same way as SNPrintF is that the Windows C Run-Time +// Library does not provide vsscanf. +#define SScanF sscanf // NOLINT + +// The RiscvDebugger class is used by the simulator while debugging simulated +// code. +class RiscvDebugger { + public: + explicit RiscvDebugger(Simulator* sim) : sim_(sim) {} + + void Debug(); + // Print all registers with a nice formatting. + void PrintRegs(char name_prefix, int start_index, int end_index); + void PrintAllRegs(); + void PrintAllRegsIncludingFPU(); + + static const Instr kNopInstr = 0x0; + + private: + Simulator* sim_; + + int64_t GetRegisterValue(int regnum); + int64_t GetFPURegisterValue(int regnum); + float GetFPURegisterValueFloat(int regnum); + double GetFPURegisterValueDouble(int regnum); + bool GetValue(const char* desc, int64_t* value); +}; + +inline void UNSUPPORTED() { + printf("Sim: Unsupported instruction.\n"); + base::OS::Abort(); +} + +int64_t RiscvDebugger::GetRegisterValue(int regnum) { + if (regnum == kNumSimuRegisters) { + return sim_->get_pc(); + } else { + return sim_->get_register(regnum); + } +} + +int64_t RiscvDebugger::GetFPURegisterValue(int regnum) { + if (regnum == kNumFPURegisters) { + return sim_->get_pc(); + } else { + return sim_->get_fpu_register(regnum); + } +} + +float RiscvDebugger::GetFPURegisterValueFloat(int regnum) { + if (regnum == kNumFPURegisters) { + return sim_->get_pc(); + } else { + return sim_->get_fpu_register_float(regnum); + } +} + +double RiscvDebugger::GetFPURegisterValueDouble(int regnum) { + if (regnum == kNumFPURegisters) { + return sim_->get_pc(); + } else { + return sim_->get_fpu_register_double(regnum); + } +} + +bool RiscvDebugger::GetValue(const char* desc, int64_t* value) { + int regnum = Registers::Number(desc); + int fpuregnum = FPURegisters::Number(desc); + + if (regnum != kInvalidRegister) { + *value = GetRegisterValue(regnum); + return true; + } else if (fpuregnum != kInvalidFPURegister) { + *value = GetFPURegisterValue(fpuregnum); + return true; + } else if (strncmp(desc, "0x", 2) == 0) { + return SScanF(desc + 2, "%" SCNx64, reinterpret_cast(value)) == + 1; + } else { + return SScanF(desc, "%" SCNu64, reinterpret_cast(value)) == 1; + } + return false; +} + +#define REG_INFO(name) \ + name, GetRegisterValue(Registers::Number(name)), \ + GetRegisterValue(Registers::Number(name)) + +void RiscvDebugger::PrintRegs(char name_prefix, int start_index, + int end_index) { + EmbeddedVector name1, name2; + DCHECK(name_prefix == 'a' || name_prefix == 't' || name_prefix == 's'); + DCHECK(start_index >= 0 && end_index <= 99); + int num_registers = (end_index - start_index) + 1; + for (int i = 0; i < num_registers / 2; i++) { + SNPrintF(name1, "%c%d", name_prefix, start_index + 2 * i); + SNPrintF(name2, "%c%d", name_prefix, start_index + 2 * i + 1); + PrintF("%3s: 0x%016" PRIx64 " %14" PRId64 " \t%3s: 0x%016" PRIx64 + " %14" PRId64 " \n", + REG_INFO(name1.begin()), REG_INFO(name2.begin())); + } + if (num_registers % 2 == 1) { + SNPrintF(name1, "%c%d", name_prefix, end_index); + PrintF("%3s: 0x%016" PRIx64 " %14" PRId64 " \n", REG_INFO(name1.begin())); + } +} + +void RiscvDebugger::PrintAllRegs() { + PrintF("\n"); + // ra, sp, gp + PrintF("%3s: 0x%016" PRIx64 " %14" PRId64 "\t%3s: 0x%016" PRIx64 " %14" PRId64 + "\t%3s: 0x%016" PRIx64 " %14" PRId64 "\n", + REG_INFO("ra"), REG_INFO("sp"), REG_INFO("gp")); + + // tp, fp, pc + PrintF("%3s: 0x%016" PRIx64 " %14" PRId64 "\t%3s: 0x%016" PRIx64 " %14" PRId64 + "\t%3s: 0x%016" PRIx64 " %14" PRId64 "\n", + REG_INFO("tp"), REG_INFO("fp"), REG_INFO("pc")); + + // print register a0, .., a7 + PrintRegs('a', 0, 7); + // print registers s1, ..., s11 + PrintRegs('s', 1, 11); + // print registers t0, ..., t6 + PrintRegs('t', 0, 6); +} + +#undef REG_INFO + +void RiscvDebugger::PrintAllRegsIncludingFPU() { +#define FPU_REG_INFO(n) \ + FPURegisters::Name(n), GetFPURegisterValue(n), GetFPURegisterValueDouble(n) + + PrintAllRegs(); + + PrintF("\n\n"); + // f0, f1, f2, ... f31. + DCHECK(kNumFPURegisters % 2 == 0); + for (int i = 0; i < kNumFPURegisters; i += 2) + PrintF("%3s: 0x%016" PRIx64 " %16.4e \t%3s: 0x%016" PRIx64 " %16.4e\n", + FPU_REG_INFO(i), FPU_REG_INFO(i + 1)); +#undef FPU_REG_INFO +} + +void RiscvDebugger::Debug() { + intptr_t last_pc = -1; + bool done = false; + +#define COMMAND_SIZE 63 +#define ARG_SIZE 255 + +#define STR(a) #a +#define XSTR(a) STR(a) + + char cmd[COMMAND_SIZE + 1]; + char arg1[ARG_SIZE + 1]; + char arg2[ARG_SIZE + 1]; + char* argv[3] = {cmd, arg1, arg2}; + + // Make sure to have a proper terminating character if reaching the limit. + cmd[COMMAND_SIZE] = 0; + arg1[ARG_SIZE] = 0; + arg2[ARG_SIZE] = 0; + + while (!done && (sim_->get_pc() != Simulator::end_sim_pc)) { + if (last_pc != sim_->get_pc()) { + disasm::NameConverter converter; + disasm::Disassembler dasm(converter); + // Use a reasonably large buffer. + v8::internal::EmbeddedVector buffer; + dasm.InstructionDecode(buffer, reinterpret_cast(sim_->get_pc())); + PrintF(" 0x%016" PRIx64 " %s\n", sim_->get_pc(), buffer.begin()); + last_pc = sim_->get_pc(); + } + char* line = ReadLine("sim> "); + if (line == nullptr) { + break; + } else { + char* last_input = sim_->last_debugger_input(); + if (strcmp(line, "\n") == 0 && last_input != nullptr) { + line = last_input; + } else { + // Ownership is transferred to sim_; + sim_->set_last_debugger_input(line); + } + // Use sscanf to parse the individual parts of the command line. At the + // moment no command expects more than two parameters. + int argc = SScanF( + line, + "%" XSTR(COMMAND_SIZE) "s " + "%" XSTR(ARG_SIZE) "s " + "%" XSTR(ARG_SIZE) "s", + cmd, arg1, arg2); + if ((strcmp(cmd, "si") == 0) || (strcmp(cmd, "stepi") == 0)) { + Instruction* instr = reinterpret_cast(sim_->get_pc()); + if (!(instr->IsTrap()) || + instr->InstructionBits() == rtCallRedirInstr) { + sim_->InstructionDecode( + reinterpret_cast(sim_->get_pc())); + } else { + // Allow si to jump over generated breakpoints. + PrintF("/!\\ Jumping over generated breakpoint.\n"); + sim_->set_pc(sim_->get_pc() + kInstrSize); + } + } else if ((strcmp(cmd, "c") == 0) || (strcmp(cmd, "cont") == 0)) { + // Execute the one instruction we broke at with breakpoints disabled. + sim_->InstructionDecode(reinterpret_cast(sim_->get_pc())); + // Leave the debugger shell. + done = true; + } else if ((strcmp(cmd, "p") == 0) || (strcmp(cmd, "print") == 0)) { + if (argc == 2) { + int64_t value; + double dvalue; + if (strcmp(arg1, "all") == 0) { + PrintAllRegs(); + } else if (strcmp(arg1, "allf") == 0) { + PrintAllRegsIncludingFPU(); + } else { + int regnum = Registers::Number(arg1); + int fpuregnum = FPURegisters::Number(arg1); + + if (regnum != kInvalidRegister) { + value = GetRegisterValue(regnum); + PrintF("%s: 0x%08" PRIx64 " %" PRId64 " \n", arg1, value, + value); + } else if (fpuregnum != kInvalidFPURegister) { + value = GetFPURegisterValue(fpuregnum); + dvalue = GetFPURegisterValueDouble(fpuregnum); + PrintF("%3s: 0x%016" PRIx64 " %16.4e\n", + FPURegisters::Name(fpuregnum), value, dvalue); + } else { + PrintF("%s unrecognized\n", arg1); + } + } + } else { + if (argc == 3) { + if (strcmp(arg2, "single") == 0) { + int64_t value; + float fvalue; + int fpuregnum = FPURegisters::Number(arg1); + + if (fpuregnum != kInvalidFPURegister) { + value = GetFPURegisterValue(fpuregnum); + value &= 0xFFFFFFFFUL; + fvalue = GetFPURegisterValueFloat(fpuregnum); + PrintF("%s: 0x%08" PRIx64 " %11.4e\n", arg1, value, fvalue); + } else { + PrintF("%s unrecognized\n", arg1); + } + } else { + PrintF("print single\n"); + } + } else { + PrintF("print or print single\n"); + } + } + } else if ((strcmp(cmd, "po") == 0) || + (strcmp(cmd, "printobject") == 0)) { + if (argc == 2) { + int64_t value; + StdoutStream os; + if (GetValue(arg1, &value)) { + Object obj(value); + os << arg1 << ": \n"; +#ifdef DEBUG + obj.Print(os); + os << "\n"; +#else + os << Brief(obj) << "\n"; +#endif + } else { + os << arg1 << " unrecognized\n"; + } + } else { + PrintF("printobject \n"); + } + } else if (strcmp(cmd, "stack") == 0 || strcmp(cmd, "mem") == 0) { + int64_t* cur = nullptr; + int64_t* end = nullptr; + int next_arg = 1; + + if (strcmp(cmd, "stack") == 0) { + cur = reinterpret_cast(sim_->get_register(Simulator::sp)); + } else { // Command "mem". + if (argc < 2) { + PrintF("Need to specify
to mem command\n"); + continue; + } + int64_t value; + if (!GetValue(arg1, &value)) { + PrintF("%s unrecognized\n", arg1); + continue; + } + cur = reinterpret_cast(value); + next_arg++; + } + + int64_t words; + if (argc == next_arg) { + words = 10; + } else { + if (!GetValue(argv[next_arg], &words)) { + words = 10; + } + } + end = cur + words; + + while (cur < end) { + PrintF(" 0x%012" PRIxPTR " : 0x%016" PRIx64 " %14" PRId64 " ", + reinterpret_cast(cur), *cur, *cur); + Object obj(*cur); + Heap* current_heap = sim_->isolate_->heap(); + if (obj.IsSmi() || + IsValidHeapObject(current_heap, HeapObject::cast(obj))) { + PrintF(" ("); + if (obj.IsSmi()) { + PrintF("smi %d", Smi::ToInt(obj)); + } else { + obj.ShortPrint(); + } + PrintF(")"); + } + PrintF("\n"); + cur++; + } + + } else if ((strcmp(cmd, "disasm") == 0) || (strcmp(cmd, "dpc") == 0) || + (strcmp(cmd, "di") == 0)) { + disasm::NameConverter converter; + disasm::Disassembler dasm(converter); + // Use a reasonably large buffer. + v8::internal::EmbeddedVector buffer; + + byte* cur = nullptr; + byte* end = nullptr; + + if (argc == 1) { + cur = reinterpret_cast(sim_->get_pc()); + end = cur + (10 * kInstrSize); + } else if (argc == 2) { + int regnum = Registers::Number(arg1); + if (regnum != kInvalidRegister || strncmp(arg1, "0x", 2) == 0) { + // The argument is an address or a register name. + int64_t value; + if (GetValue(arg1, &value)) { + cur = reinterpret_cast(value); + // Disassemble 10 instructions at . + end = cur + (10 * kInstrSize); + } + } else { + // The argument is the number of instructions. + int64_t value; + if (GetValue(arg1, &value)) { + cur = reinterpret_cast(sim_->get_pc()); + // Disassemble instructions. + end = cur + (value * kInstrSize); + } + } + } else { + int64_t value1; + int64_t value2; + if (GetValue(arg1, &value1) && GetValue(arg2, &value2)) { + cur = reinterpret_cast(value1); + end = cur + (value2 * kInstrSize); + } + } + + while (cur < end) { + dasm.InstructionDecode(buffer, cur); + PrintF(" 0x%08" PRIxPTR " %s\n", reinterpret_cast(cur), + buffer.begin()); + cur += kInstrSize; + } + } else if (strcmp(cmd, "gdb") == 0) { + PrintF("relinquishing control to gdb\n"); + v8::base::OS::DebugBreak(); + PrintF("regaining control from gdb\n"); + } else if (strcmp(cmd, "break") == 0 + || strcmp(cmd, "b") == 0 + || strcmp(cmd, "tbreak") == 0) { + bool is_tbreak = strcmp(cmd, "tbreak") == 0; + if (argc == 2) { + int64_t value; + if (GetValue(arg1, &value)) { + sim_->SetBreakpoint(reinterpret_cast(value), is_tbreak); + } else { + PrintF("%s unrecognized\n", arg1); + } + } else { + sim_->ListBreakpoints(); + PrintF("Use `break
` to set or disable a breakpoint\n"); + PrintF("Use `tbreak
` to set or disable a temporary breakpoint\n"); + } + } else if (strcmp(cmd, "flags") == 0) { + PrintF("No flags on RISC-V !\n"); + } else if (strcmp(cmd, "stop") == 0) { + int64_t value; + if (argc == 3) { + // Print information about all/the specified breakpoint(s). + if (strcmp(arg1, "info") == 0) { + if (strcmp(arg2, "all") == 0) { + PrintF("Stop information:\n"); + for (uint32_t i = kMaxWatchpointCode + 1; i <= kMaxStopCode; + i++) { + sim_->PrintStopInfo(i); + } + } else if (GetValue(arg2, &value)) { + sim_->PrintStopInfo(value); + } else { + PrintF("Unrecognized argument.\n"); + } + } else if (strcmp(arg1, "enable") == 0) { + // Enable all/the specified breakpoint(s). + if (strcmp(arg2, "all") == 0) { + for (uint32_t i = kMaxWatchpointCode + 1; i <= kMaxStopCode; + i++) { + sim_->EnableStop(i); + } + } else if (GetValue(arg2, &value)) { + sim_->EnableStop(value); + } else { + PrintF("Unrecognized argument.\n"); + } + } else if (strcmp(arg1, "disable") == 0) { + // Disable all/the specified breakpoint(s). + if (strcmp(arg2, "all") == 0) { + for (uint32_t i = kMaxWatchpointCode + 1; i <= kMaxStopCode; + i++) { + sim_->DisableStop(i); + } + } else if (GetValue(arg2, &value)) { + sim_->DisableStop(value); + } else { + PrintF("Unrecognized argument.\n"); + } + } + } else { + PrintF("Wrong usage. Use help command for more information.\n"); + } + } else if ((strcmp(cmd, "stat") == 0) || (strcmp(cmd, "st") == 0)) { + // Print registers and disassemble. + PrintAllRegs(); + PrintF("\n"); + + disasm::NameConverter converter; + disasm::Disassembler dasm(converter); + // Use a reasonably large buffer. + v8::internal::EmbeddedVector buffer; + + byte* cur = nullptr; + byte* end = nullptr; + + if (argc == 1) { + cur = reinterpret_cast(sim_->get_pc()); + end = cur + (10 * kInstrSize); + } else if (argc == 2) { + int64_t value; + if (GetValue(arg1, &value)) { + cur = reinterpret_cast(value); + // no length parameter passed, assume 10 instructions + end = cur + (10 * kInstrSize); + } + } else { + int64_t value1; + int64_t value2; + if (GetValue(arg1, &value1) && GetValue(arg2, &value2)) { + cur = reinterpret_cast(value1); + end = cur + (value2 * kInstrSize); + } + } + + while (cur < end) { + dasm.InstructionDecode(buffer, cur); + PrintF(" 0x%08" PRIxPTR " %s\n", reinterpret_cast(cur), + buffer.begin()); + cur += kInstrSize; + } + } else if ((strcmp(cmd, "h") == 0) || (strcmp(cmd, "help") == 0)) { + PrintF("cont (alias 'c')\n"); + PrintF(" Continue execution\n"); + PrintF("stepi (alias 'si')\n"); + PrintF(" Step one instruction\n"); + PrintF("print (alias 'p')\n"); + PrintF(" print \n"); + PrintF(" Print register content\n"); + PrintF(" Use register name 'all' to print all GPRs\n"); + PrintF(" Use register name 'allf' to print all GPRs and FPRs\n"); + PrintF("printobject (alias 'po')\n"); + PrintF(" printobject \n"); + PrintF(" Print an object from a register\n"); + PrintF("stack\n"); + PrintF(" stack []\n"); + PrintF(" Dump stack content, default dump 10 words)\n"); + PrintF("mem\n"); + PrintF(" mem
[]\n"); + PrintF(" Dump memory content, default dump 10 words)\n"); + PrintF("flags\n"); + PrintF(" print flags\n"); + PrintF("disasm (alias 'di')\n"); + PrintF(" disasm []\n"); + PrintF(" disasm [
] (e.g., disasm pc) \n"); + PrintF(" disasm [[
] ]\n"); + PrintF(" Disassemble code, default is 10 instructions\n"); + PrintF(" from pc\n"); + PrintF("gdb \n"); + PrintF(" Return to gdb if the simulator was started with gdb\n"); + PrintF("break (alias 'b')\n"); + PrintF(" break : list all breakpoints\n"); + PrintF(" break
: set / enable / disable a breakpoint.\n"); + PrintF("tbreak\n"); + PrintF(" tbreak : list all breakpoints\n"); + PrintF(" tbreak
: set / enable / disable a temporary breakpoint.\n"); + PrintF(" Set a breakpoint enabled only for one stop. \n"); + PrintF("stop feature:\n"); + PrintF(" Description:\n"); + PrintF(" Stops are debug instructions inserted by\n"); + PrintF(" the Assembler::stop() function.\n"); + PrintF(" When hitting a stop, the Simulator will\n"); + PrintF(" stop and give control to the Debugger.\n"); + PrintF(" All stop codes are watched:\n"); + PrintF(" - They can be enabled / disabled: the Simulator\n"); + PrintF(" will / won't stop when hitting them.\n"); + PrintF(" - The Simulator keeps track of how many times they \n"); + PrintF(" are met. (See the info command.) Going over a\n"); + PrintF(" disabled stop still increases its counter. \n"); + PrintF(" Commands:\n"); + PrintF(" stop info all/ : print infos about number \n"); + PrintF(" or all stop(s).\n"); + PrintF(" stop enable/disable all/ : enables / disables\n"); + PrintF(" all or number stop(s)\n"); + } else { + PrintF("Unknown command: %s\n", cmd); + } + } + } + +#undef COMMAND_SIZE +#undef ARG_SIZE + +#undef STR +#undef XSTR +} + +void Simulator::SetBreakpoint(Instruction* location, bool is_tbreak) { + for (unsigned i = 0; i < breakpoints_.size(); i++) { + if (breakpoints_.at(i).location == location) { + if (breakpoints_.at(i).is_tbreak != is_tbreak) { + PrintF("Change breakpoint at %p to %s breakpoint\n", + reinterpret_cast(location), + is_tbreak ? "temporary" : "regular"); + breakpoints_.at(i).is_tbreak = is_tbreak; + return; + } + PrintF("Existing breakpoint at %p was %s\n", + reinterpret_cast(location), + breakpoints_.at(i).enabled ? "disabled" : "enabled"); + breakpoints_.at(i).enabled = !breakpoints_.at(i).enabled; + return; + } + } + Breakpoint new_breakpoint = {location, true, is_tbreak}; + breakpoints_.push_back(new_breakpoint); + PrintF("Set a %sbreakpoint at %p\n", + is_tbreak ? "temporary " : "", + reinterpret_cast(location)); +} + +void Simulator::ListBreakpoints() { + PrintF("Breakpoints:\n"); + for (unsigned i = 0; i < breakpoints_.size(); i++) { + PrintF("%p : %s %s\n", + reinterpret_cast(breakpoints_.at(i).location), + breakpoints_.at(i).enabled ? "enabled" : "disabled", + breakpoints_.at(i).is_tbreak ? ": temporary" : ""); + } +} + +void Simulator::CheckBreakpoints() { + bool hit_a_breakpoint = false; + bool is_tbreak = false; + Instruction* pc_ = reinterpret_cast(get_pc()); + for (unsigned i = 0; i < breakpoints_.size(); i++) { + if ((breakpoints_.at(i).location == pc_) && breakpoints_.at(i).enabled) { + hit_a_breakpoint = true; + if (breakpoints_.at(i).is_tbreak) { + // Disable a temporary breakpoint. + is_tbreak = true; + breakpoints_.at(i).enabled = false; + } + break; + } + } + if (hit_a_breakpoint) { + PrintF("Hit %sa breakpoint at %p.\n", + is_tbreak ? "and disabled " : "", + reinterpret_cast(pc_)); + RiscvDebugger dbg(this); + dbg.Debug(); + } +} + +bool Simulator::ICacheMatch(void* one, void* two) { + DCHECK_EQ(reinterpret_cast(one) & CachePage::kPageMask, 0); + DCHECK_EQ(reinterpret_cast(two) & CachePage::kPageMask, 0); + return one == two; +} + +static uint32_t ICacheHash(void* key) { + return static_cast(reinterpret_cast(key)) >> 2; +} + +static bool AllOnOnePage(uintptr_t start, size_t size) { + intptr_t start_page = (start & ~CachePage::kPageMask); + intptr_t end_page = ((start + size) & ~CachePage::kPageMask); + return start_page == end_page; +} + +void Simulator::set_last_debugger_input(char* input) { + DeleteArray(last_debugger_input_); + last_debugger_input_ = input; +} + +void Simulator::SetRedirectInstruction(Instruction* instruction) { + instruction->SetInstructionBits(rtCallRedirInstr); +} + +void Simulator::FlushICache(base::CustomMatcherHashMap* i_cache, + void* start_addr, size_t size) { + int64_t start = reinterpret_cast(start_addr); + int64_t intra_line = (start & CachePage::kLineMask); + start -= intra_line; + size += intra_line; + size = ((size - 1) | CachePage::kLineMask) + 1; + int offset = (start & CachePage::kPageMask); + while (!AllOnOnePage(start, size - 1)) { + int bytes_to_flush = CachePage::kPageSize - offset; + FlushOnePage(i_cache, start, bytes_to_flush); + start += bytes_to_flush; + size -= bytes_to_flush; + DCHECK_EQ((int64_t)0, start & CachePage::kPageMask); + offset = 0; + } + if (size != 0) { + FlushOnePage(i_cache, start, size); + } +} + +CachePage* Simulator::GetCachePage(base::CustomMatcherHashMap* i_cache, + void* page) { + base::HashMap::Entry* entry = i_cache->LookupOrInsert(page, ICacheHash(page)); + if (entry->value == nullptr) { + CachePage* new_page = new CachePage(); + entry->value = new_page; + } + return reinterpret_cast(entry->value); +} + +// Flush from start up to and not including start + size. +void Simulator::FlushOnePage(base::CustomMatcherHashMap* i_cache, + intptr_t start, size_t size) { + DCHECK_LE(size, CachePage::kPageSize); + DCHECK(AllOnOnePage(start, size - 1)); + DCHECK_EQ(start & CachePage::kLineMask, 0); + DCHECK_EQ(size & CachePage::kLineMask, 0); + void* page = reinterpret_cast(start & (~CachePage::kPageMask)); + int offset = (start & CachePage::kPageMask); + CachePage* cache_page = GetCachePage(i_cache, page); + char* valid_bytemap = cache_page->ValidityByte(offset); + memset(valid_bytemap, CachePage::LINE_INVALID, size >> CachePage::kLineShift); +} + +void Simulator::CheckICache(base::CustomMatcherHashMap* i_cache, + Instruction* instr) { + int64_t address = reinterpret_cast(instr); + void* page = reinterpret_cast(address & (~CachePage::kPageMask)); + void* line = reinterpret_cast(address & (~CachePage::kLineMask)); + int offset = (address & CachePage::kPageMask); + CachePage* cache_page = GetCachePage(i_cache, page); + char* cache_valid_byte = cache_page->ValidityByte(offset); + bool cache_hit = (*cache_valid_byte == CachePage::LINE_VALID); + char* cached_line = cache_page->CachedData(offset & ~CachePage::kLineMask); + if (cache_hit) { + // Check that the data in memory matches the contents of the I-cache. + CHECK_EQ(0, memcmp(reinterpret_cast(instr), + cache_page->CachedData(offset), kInstrSize)); + } else { + // Cache miss. Load memory into the cache. + memcpy(cached_line, line, CachePage::kLineLength); + *cache_valid_byte = CachePage::LINE_VALID; + } +} + +Simulator::Simulator(Isolate* isolate) : isolate_(isolate) { + // Set up simulator support first. Some of this information is needed to + // setup the architecture state. + stack_size_ = FLAG_sim_stack_size * KB; + stack_ = reinterpret_cast(malloc(stack_size_)); + pc_modified_ = false; + icount_ = 0; + break_count_ = 0; + // Reset debug helpers. + breakpoints_.clear(); + // TODO: 'next' command + //break_on_next_ = false; + + // Set up architecture state. + // All registers are initialized to zero to start with. + for (int i = 0; i < kNumSimuRegisters; i++) { + registers_[i] = 0; + } + + for (int i = 0; i < kNumFPURegisters; i++) { + FPUregisters_[i] = 0; + } + + FCSR_ = 0; + + // The sp is initialized to point to the bottom (high address) of the + // allocated stack area. To be safe in potential stack underflows we leave + // some buffer below. + registers_[sp] = reinterpret_cast(stack_) + stack_size_ - 64; + // The ra and pc are initialized to a known bad value that will cause an + // access violation if the simulator ever tries to execute it. + registers_[pc] = bad_ra; + registers_[ra] = bad_ra; + + last_debugger_input_ = nullptr; +} + +Simulator::~Simulator() { + GlobalMonitor::Get()->RemoveLinkedAddress(&global_monitor_thread_); + free(stack_); +} + +// Get the active Simulator for the current thread. +Simulator* Simulator::current(Isolate* isolate) { + v8::internal::Isolate::PerIsolateThreadData* isolate_data = + isolate->FindOrAllocatePerThreadDataForThisThread(); + DCHECK_NOT_NULL(isolate_data); + + Simulator* sim = isolate_data->simulator(); + if (sim == nullptr) { + // TODO(146): delete the simulator object when a thread/isolate goes away. + sim = new Simulator(isolate); + isolate_data->set_simulator(sim); + } + return sim; +} + +// Sets the register in the architecture state. It will also deal with +// updating Simulator internal state for special registers such as PC. +void Simulator::set_register(int reg, int64_t value) { + DCHECK((reg >= 0) && (reg < kNumSimuRegisters)); + if (reg == pc) { + pc_modified_ = true; + } + + // Zero register always holds 0. + registers_[reg] = (reg == 0) ? 0 : value; +} + +void Simulator::set_dw_register(int reg, const int* dbl) { + DCHECK((reg >= 0) && (reg < kNumSimuRegisters)); + registers_[reg] = dbl[1]; + registers_[reg] = registers_[reg] << 32; + registers_[reg] += dbl[0]; +} + +void Simulator::set_fpu_register(int fpureg, int64_t value) { + DCHECK((fpureg >= 0) && (fpureg < kNumFPURegisters)); + FPUregisters_[fpureg] = value; +} + +void Simulator::set_fpu_register_word(int fpureg, int32_t value) { + // Set ONLY lower 32-bits, leaving upper bits untouched. + DCHECK((fpureg >= 0) && (fpureg < kNumFPURegisters)); + int32_t* pword; + if (kArchEndian == kLittle) { + pword = reinterpret_cast(&FPUregisters_[fpureg]); + } else { + pword = reinterpret_cast(&FPUregisters_[fpureg]) + 1; + } + *pword = value; +} + +void Simulator::set_fpu_register_hi_word(int fpureg, int32_t value) { + // Set ONLY upper 32-bits, leaving lower bits untouched. + DCHECK((fpureg >= 0) && (fpureg < kNumFPURegisters)); + int32_t* phiword; + if (kArchEndian == kLittle) { + phiword = (reinterpret_cast(&FPUregisters_[fpureg])) + 1; + } else { + phiword = reinterpret_cast(&FPUregisters_[fpureg]); + } + *phiword = value; +} + +void Simulator::set_fpu_register_float(int fpureg, float value) { + DCHECK((fpureg >= 0) && (fpureg < kNumFPURegisters)); + FPUregisters_[fpureg] = box_float(value); +} + +void Simulator::set_fpu_register_double(int fpureg, double value) { + DCHECK((fpureg >= 0) && (fpureg < kNumFPURegisters)); + *bit_cast(&FPUregisters_[fpureg]) = value; +} + +// Get the register from the architecture state. This function does handle +// the special case of accessing the PC register. +int64_t Simulator::get_register(int reg) const { + DCHECK((reg >= 0) && (reg < kNumSimuRegisters)); + if (reg == 0) + return 0; + else + return registers_[reg] + ((reg == pc) ? Instruction::kPCReadOffset : 0); +} + +double Simulator::get_double_from_register_pair(int reg) { + // TODO(plind): bad ABI stuff, refactor or remove. + DCHECK((reg >= 0) && (reg < kNumSimuRegisters) && ((reg % 2) == 0)); + + double dm_val = 0.0; + // Read the bits from the unsigned integer register_[] array + // into the double precision floating point value and return it. + char buffer[sizeof(registers_[0])]; + memcpy(buffer, ®isters_[reg], sizeof(registers_[0])); + memcpy(&dm_val, buffer, sizeof(registers_[0])); + return (dm_val); +} + +int64_t Simulator::get_fpu_register(int fpureg) const { + DCHECK((fpureg >= 0) && (fpureg < kNumFPURegisters)); + return FPUregisters_[fpureg]; +} + +int32_t Simulator::get_fpu_register_word(int fpureg) const { + DCHECK((fpureg >= 0) && (fpureg < kNumFPURegisters)); + return static_cast(FPUregisters_[fpureg] & 0xFFFFFFFF); +} + +int32_t Simulator::get_fpu_register_signed_word(int fpureg) const { + DCHECK((fpureg >= 0) && (fpureg < kNumFPURegisters)); + return static_cast(FPUregisters_[fpureg] & 0xFFFFFFFF); +} + +int32_t Simulator::get_fpu_register_hi_word(int fpureg) const { + DCHECK((fpureg >= 0) && (fpureg < kNumFPURegisters)); + return static_cast((FPUregisters_[fpureg] >> 32) & 0xFFFFFFFF); +} + +float Simulator::get_fpu_register_float(int fpureg) const { + DCHECK((fpureg >= 0) && (fpureg < kNumFPURegisters)); + if (!is_boxed_float(FPUregisters_[fpureg])) { + return std::numeric_limits::quiet_NaN(); + } + return *bit_cast(const_cast(&FPUregisters_[fpureg])); +} + +double Simulator::get_fpu_register_double(int fpureg) const { + DCHECK((fpureg >= 0) && (fpureg < kNumFPURegisters)); + return *bit_cast(&FPUregisters_[fpureg]); +} + +// Runtime FP routines take up to two double arguments and zero +// or one integer arguments. All are constructed here, +// from fa0, fa1, and a0. +void Simulator::GetFpArgs(double* x, double* y, int32_t* z) { + *x = get_fpu_register_double(fa0); + *y = get_fpu_register_double(fa1); + *z = static_cast(get_register(a0)); +} + +// The return value is in fa0. +void Simulator::SetFpResult(const double& result) { + set_fpu_register_double(fa0, result); +} + +// helper functions to read/write/set/clear CRC values/bits +uint32_t Simulator::read_csr_value(uint32_t csr) { + switch (csr) { + case csr_fflags: // Floating-Point Accrued Exceptions (RW) + return (FCSR_ & kFcsrFlagsMask); + case csr_frm: // Floating-Point Dynamic Rounding Mode (RW) + return (FCSR_ & kFcsrFrmMask) >> kFcsrFrmShift; + case csr_fcsr: // Floating-Point Control and Status Register (RW) + return (FCSR_ & kFcsrMask); + default: + UNIMPLEMENTED(); + } +} + +uint32_t Simulator::get_dynamic_rounding_mode() { + return read_csr_value(csr_frm); +} + +void Simulator::write_csr_value(uint32_t csr, uint64_t val) { + uint32_t value = (uint32_t)val; + switch (csr) { + case csr_fflags: // Floating-Point Accrued Exceptions (RW) + DCHECK(value <= ((1 << kFcsrFlagsBits) - 1)); + FCSR_ = (FCSR_ & (~kFcsrFlagsMask)) | value; + break; + case csr_frm: // Floating-Point Dynamic Rounding Mode (RW) + DCHECK(value <= ((1 << kFcsrFrmBits) - 1)); + FCSR_ = (FCSR_ & (~kFcsrFrmMask)) | (value << kFcsrFrmShift); + break; + case csr_fcsr: // Floating-Point Control and Status Register (RW) + DCHECK(value <= ((1 << kFcsrBits) - 1)); + FCSR_ = (FCSR_ & (~kFcsrMask)) | value; + break; + default: + UNIMPLEMENTED(); + } +} + +void Simulator::set_csr_bits(uint32_t csr, uint64_t val) { + uint32_t value = (uint32_t)val; + switch (csr) { + case csr_fflags: // Floating-Point Accrued Exceptions (RW) + DCHECK(value <= ((1 << kFcsrFlagsBits) - 1)); + FCSR_ = FCSR_ | value; + break; + case csr_frm: // Floating-Point Dynamic Rounding Mode (RW) + DCHECK(value <= ((1 << kFcsrFrmBits) - 1)); + FCSR_ = FCSR_ | (value << kFcsrFrmShift); + break; + case csr_fcsr: // Floating-Point Control and Status Register (RW) + DCHECK(value <= ((1 << kFcsrBits) - 1)); + FCSR_ = FCSR_ | value; + break; + default: + UNIMPLEMENTED(); + } +} + +void Simulator::clear_csr_bits(uint32_t csr, uint64_t val) { + uint32_t value = (uint32_t)val; + switch (csr) { + case csr_fflags: // Floating-Point Accrued Exceptions (RW) + DCHECK(value <= ((1 << kFcsrFlagsBits) - 1)); + FCSR_ = FCSR_ & (~value); + break; + case csr_frm: // Floating-Point Dynamic Rounding Mode (RW) + DCHECK(value <= ((1 << kFcsrFrmBits) - 1)); + FCSR_ = FCSR_ & (~(value << kFcsrFrmShift)); + break; + case csr_fcsr: // Floating-Point Control and Status Register (RW) + DCHECK(value <= ((1 << kFcsrBits) - 1)); + FCSR_ = FCSR_ & (~value); + break; + default: + UNIMPLEMENTED(); + } +} + +bool Simulator::test_fflags_bits(uint32_t mask) { + return (FCSR_ & kFcsrFlagsMask & mask) != 0; +} + +template +T Simulator::FMaxMinHelper(T a, T b, MaxMinKind kind) { + // set invalid bit for signaling nan + if ((a == std::numeric_limits::signaling_NaN()) || + (b == std::numeric_limits::signaling_NaN())) { + // FIXME: NV -> kInvalidOperation + set_csr_bits(csr_fflags, kInvalidOperation); + } + + T result = 0; + if (std::isnan(a) && std::isnan(b)) { + result = a; + } else if (std::isnan(a)) { + result = b; + } else if (std::isnan(b)) { + result = a; + } else if (b == a) { // Handle -0.0 == 0.0 case. + if (kind == MaxMinKind::kMax) { + result = std::signbit(b) ? a : b; + } else { + result = std::signbit(b) ? b : a; + } + } else { + result = (kind == MaxMinKind::kMax) ? fmax(a, b) : fmin(a, b); + } + + return result; +} + +// Raw access to the PC register. +void Simulator::set_pc(int64_t value) { + pc_modified_ = true; + registers_[pc] = value; + DCHECK(has_bad_pc() || ((value % kInstrSize) == 0)); +} + +bool Simulator::has_bad_pc() const { + return ((registers_[pc] == bad_ra) || (registers_[pc] == end_sim_pc)); +} + +// Raw access to the PC register without the special adjustment when reading. +int64_t Simulator::get_pc() const { return registers_[pc]; } + +// The RISC-V spec leaves it open to the implementation on how to handle +// unaligned reads and writes. For now, we simply disallow unaligned reads but +// at some point, we may want to implement some other behavior. + +// TODO(plind): refactor this messy debug code when we do unaligned access. +void Simulator::DieOrDebug() { + if ((1)) { // Flag for this was removed. + RiscvDebugger dbg(this); + dbg.Debug(); + } else { + base::OS::Abort(); + } +} + +void Simulator::TraceRegWr(int64_t value, TraceType t) { + if (::v8::internal::FLAG_trace_sim) { + union { + int64_t fmt_int64; + int32_t fmt_int32[2]; + float fmt_float[2]; + double fmt_double; + } v; + v.fmt_int64 = value; + + switch (t) { + case WORD: + SNPrintF(trace_buf_, + "%016" PRIx64 " (%" PRId64 ") int32:%" PRId32 + " uint32:%" PRIu32, + v.fmt_int64, icount_, v.fmt_int32[0], v.fmt_int32[0]); + break; + case DWORD: + SNPrintF(trace_buf_, + "%016" PRIx64 " (%" PRId64 ") int64:%" PRId64 + " uint64:%" PRIu64, + value, icount_, value, value); + break; + case FLOAT: + SNPrintF(trace_buf_, "%016" PRIx64 " (%" PRId64 ") flt:%e", + v.fmt_int64, icount_, v.fmt_float[0]); + break; + case DOUBLE: + SNPrintF(trace_buf_, "%016" PRIx64 " (%" PRId64 ") dbl:%e", + v.fmt_int64, icount_, v.fmt_double); + break; + default: + UNREACHABLE(); + } + } +} + +// TODO(plind): consider making icount_ printing a flag option. +template +void Simulator::TraceMemRd(int64_t addr, T value, int64_t reg_value) { + if (::v8::internal::FLAG_trace_sim) { + if (std::is_integral::value) { + switch (sizeof(T)) { + case 1: + SNPrintF(trace_buf_, + "%016" PRIx64 " (%" PRId64 ") int8:%" PRId8 + " uint8:%" PRIu8 " <-- [addr: %" PRIx64 "]", + reg_value, icount_, static_cast(value), + static_cast(value), addr); + break; + case 2: + SNPrintF(trace_buf_, + "%016" PRIx64 " (%" PRId64 ") int16:%" PRId16 + " uint16:%" PRIu16 " <-- [addr: %" PRIx64 "]", + reg_value, icount_, static_cast(value), + static_cast(value), addr); + break; + case 4: + SNPrintF(trace_buf_, + "%016" PRIx64 " (%" PRId64 ") int32:%" PRId32 + " uint32:%" PRIu32 " <-- [addr: %" PRIx64 "]", + reg_value, icount_, static_cast(value), + static_cast(value), addr); + break; + case 8: + SNPrintF(trace_buf_, + "%016" PRIx64 " (%" PRId64 ") int64:%" PRId64 + " uint64:%" PRIu64 " <-- [addr: %" PRIx64 "]", + reg_value, icount_, static_cast(value), + static_cast(value), addr); + break; + default: + UNREACHABLE(); + } + } else if (std::is_same::value) { + SNPrintF(trace_buf_, + "%016" PRIx64 " (%" PRId64 ") flt:%e <-- [addr: %" PRIx64 + "]", + reg_value, icount_, static_cast(value), addr); + } else if (std::is_same::value) { + SNPrintF(trace_buf_, + "%016" PRIx64 " (%" PRId64 ") dbl:%e <-- [addr: %" PRIx64 + "]", + reg_value, icount_, static_cast(value), addr); + } else { + UNREACHABLE(); + } + } +} + +template +void Simulator::TraceMemWr(int64_t addr, T value) { + if (::v8::internal::FLAG_trace_sim) { + switch (sizeof(T)) { + case 1: + SNPrintF(trace_buf_, + " (%" PRIu64 ") int8:%" PRId8 + " uint8:%" PRIu8 " --> [addr: %" PRIx64 "]", + icount_, static_cast(value), + static_cast(value), addr); + break; + case 2: + SNPrintF(trace_buf_, + " (%" PRIu64 ") int16:%" PRId16 + " uint16:%" PRIu16 " --> [addr: %" PRIx64 "]", + icount_, static_cast(value), + static_cast(value), addr); + break; + case 4: + if (std::is_integral::value) { + SNPrintF(trace_buf_, + " (%" PRIu64 ") int32:%" PRId32 + " uint32:%" PRIu32 " --> [addr: %" PRIx64 "]", + icount_, static_cast(value), + static_cast(value), addr); + } else { + SNPrintF(trace_buf_, + " (%" PRIu64 + ") flt:%e --> [addr: %" PRIx64 "]", + icount_, static_cast(value), addr); + } + break; + case 8: + if (std::is_integral::value) { + SNPrintF(trace_buf_, + " (%" PRIu64 ") int64:%" PRId64 + " uint64:%" PRIu64 " --> [addr: %" PRIx64 "]", + icount_, static_cast(value), + static_cast(value), addr); + } else { + SNPrintF(trace_buf_, + " (%" PRIu64 + ") dbl:%e --> [addr: %" PRIx64 "]", + icount_, static_cast(value), addr); + } + break; + default: + UNREACHABLE(); + } + } +} + +// RISCV Memory Read/Write functions + +// FIXME (RISCV): check whether the specific board supports unaligned load/store +// (determined by EEI). For now, we assume the board does not support unaligned +// load/store (e.g., trapping) +template +T Simulator::ReadMem(int64_t addr, Instruction* instr) { + if (addr >= 0 && addr < 0x400) { + // This has to be a nullptr-dereference, drop into debugger. + PrintF("Memory read from bad address: 0x%08" PRIx64 " , pc=0x%08" PRIxPTR + " \n", + addr, reinterpret_cast(instr)); + DieOrDebug(); + } + + // check for natural alignment + if ((addr & (sizeof(T) - 1)) != 0) { + PrintF("Unaligned read at 0x%08" PRIx64 " , pc=0x%08" V8PRIxPTR "\n", addr, + reinterpret_cast(instr)); + DieOrDebug(); + } + + T* ptr = reinterpret_cast(addr); + T value = *ptr; + return value; +} + +template +void Simulator::WriteMem(int64_t addr, T value, Instruction* instr) { + if (addr >= 0 && addr < 0x400) { + // This has to be a nullptr-dereference, drop into debugger. + PrintF("Memory write to bad address: 0x%08" PRIx64 " , pc=0x%08" PRIxPTR + " \n", + addr, reinterpret_cast(instr)); + DieOrDebug(); + } + + // check for natural alignment + if ((addr & (sizeof(T) - 1)) != 0) { + PrintF("Unaligned write at 0x%08" PRIx64 " , pc=0x%08" V8PRIxPTR "\n", addr, + reinterpret_cast(instr)); + DieOrDebug(); + } + + T* ptr = reinterpret_cast(addr); + TraceMemWr(addr, value); + *ptr = value; +} + +// Returns the limit of the stack area to enable checking for stack overflows. +uintptr_t Simulator::StackLimit(uintptr_t c_limit) const { + // The simulator uses a separate JS stack. If we have exhausted the C stack, + // we also drop down the JS limit to reflect the exhaustion on the JS stack. + if (GetCurrentStackPosition() < c_limit) { + return reinterpret_cast(get_sp()); + } + + // Otherwise the limit is the JS stack. Leave a safety margin of 1024 bytes + // to prevent overrunning the stack when pushing values. + return reinterpret_cast(stack_) + 1024; +} + +// Unsupported instructions use Format to print an error and stop execution. +void Simulator::Format(Instruction* instr, const char* format) { + PrintF("Simulator found unsupported instruction:\n 0x%08" PRIxPTR " : %s\n", + reinterpret_cast(instr), format); + UNIMPLEMENTED_RISCV(); +} + +// Calls into the V8 runtime are based on this very simple interface. +// Note: To be able to return two values from some calls the code in +// runtime.cc uses the ObjectPair which is essentially two 32-bit values +// stuffed into a 64-bit value. With the code below we assume that all runtime +// calls return 64 bits of result. If they don't, the a1 result register +// contains a bogus value, which is fine because it is caller-saved. + +using SimulatorRuntimeCall = ObjectPair (*)(int64_t arg0, int64_t arg1, + int64_t arg2, int64_t arg3, + int64_t arg4, int64_t arg5, + int64_t arg6, int64_t arg7, + int64_t arg8, int64_t arg9); + +// These prototypes handle the four types of FP calls. +using SimulatorRuntimeCompareCall = int64_t (*)(double darg0, double darg1); +using SimulatorRuntimeFPFPCall = double (*)(double darg0, double darg1); +using SimulatorRuntimeFPCall = double (*)(double darg0); +using SimulatorRuntimeFPIntCall = double (*)(double darg0, int32_t arg0); + +// This signature supports direct call in to API function native callback +// (refer to InvocationCallback in v8.h). +using SimulatorRuntimeDirectApiCall = void (*)(int64_t arg0); +using SimulatorRuntimeProfilingApiCall = void (*)(int64_t arg0, void* arg1); + +// This signature supports direct call to accessor getter callback. +using SimulatorRuntimeDirectGetterCall = void (*)(int64_t arg0, int64_t arg1); +using SimulatorRuntimeProfilingGetterCall = void (*)(int64_t arg0, int64_t arg1, + void* arg2); + +// Software interrupt instructions are used by the simulator to call into the +// C-based V8 runtime. They are also used for debugging with simulator. +void Simulator::SoftwareInterrupt() { + // There are two instructions that could get us here, the ebreak or ecall + // instructions are "SYSTEM" class opcode distinuished by Imm12Value field w/ + // the rest of instruction fields being zero + int32_t func = instr_.Imm12Value(); + // We first check if we met a call_rt_redirected. + if (instr_.InstructionBits() == rtCallRedirInstr) { // ECALL + Redirection* redirection = Redirection::FromInstruction(instr_.instr()); + + int64_t* stack_pointer = reinterpret_cast(get_register(sp)); + + int64_t arg0 = get_register(a0); + int64_t arg1 = get_register(a1); + int64_t arg2 = get_register(a2); + int64_t arg3 = get_register(a3); + int64_t arg4 = get_register(a4); + int64_t arg5 = get_register(a5); + int64_t arg6 = get_register(a6); + int64_t arg7 = get_register(a7); + int64_t arg8 = stack_pointer[0]; + int64_t arg9 = stack_pointer[1]; + STATIC_ASSERT(kMaxCParameters == 10); + + bool fp_call = + (redirection->type() == ExternalReference::BUILTIN_FP_FP_CALL) || + (redirection->type() == ExternalReference::BUILTIN_COMPARE_CALL) || + (redirection->type() == ExternalReference::BUILTIN_FP_CALL) || + (redirection->type() == ExternalReference::BUILTIN_FP_INT_CALL); + + // This is dodgy but it works because the C entry stubs are never moved. + // See comment in codegen-arm.cc and bug 1242173. + int64_t saved_ra = get_register(ra); + + intptr_t external = + reinterpret_cast(redirection->external_function()); + + if (fp_call) { + double dval0, dval1; // one or two double parameters + int32_t ival; // zero or one integer parameters + int64_t iresult = 0; // integer return value + double dresult = 0; // double return value + GetFpArgs(&dval0, &dval1, &ival); + SimulatorRuntimeCall generic_target = + reinterpret_cast(external); + if (::v8::internal::FLAG_trace_sim) { + switch (redirection->type()) { + case ExternalReference::BUILTIN_FP_FP_CALL: + case ExternalReference::BUILTIN_COMPARE_CALL: + PrintF("Call to host function at %p with args %f, %f", + reinterpret_cast(FUNCTION_ADDR(generic_target)), + dval0, dval1); + break; + case ExternalReference::BUILTIN_FP_CALL: + PrintF("Call to host function at %p with arg %f", + reinterpret_cast(FUNCTION_ADDR(generic_target)), + dval0); + break; + case ExternalReference::BUILTIN_FP_INT_CALL: + PrintF("Call to host function at %p with args %f, %d", + reinterpret_cast(FUNCTION_ADDR(generic_target)), + dval0, ival); + break; + default: + UNREACHABLE(); + break; + } + } + switch (redirection->type()) { + case ExternalReference::BUILTIN_COMPARE_CALL: { + SimulatorRuntimeCompareCall target = + reinterpret_cast(external); + iresult = target(dval0, dval1); + set_register(a0, static_cast(iresult)); + // set_register(a1, static_cast(iresult >> 32)); + break; + } + case ExternalReference::BUILTIN_FP_FP_CALL: { + SimulatorRuntimeFPFPCall target = + reinterpret_cast(external); + dresult = target(dval0, dval1); + SetFpResult(dresult); + break; + } + case ExternalReference::BUILTIN_FP_CALL: { + SimulatorRuntimeFPCall target = + reinterpret_cast(external); + dresult = target(dval0); + SetFpResult(dresult); + break; + } + case ExternalReference::BUILTIN_FP_INT_CALL: { + SimulatorRuntimeFPIntCall target = + reinterpret_cast(external); + dresult = target(dval0, ival); + SetFpResult(dresult); + break; + } + default: + UNREACHABLE(); + break; + } + if (::v8::internal::FLAG_trace_sim) { + switch (redirection->type()) { + case ExternalReference::BUILTIN_COMPARE_CALL: + PrintF("Returned %08x\n", static_cast(iresult)); + break; + case ExternalReference::BUILTIN_FP_FP_CALL: + case ExternalReference::BUILTIN_FP_CALL: + case ExternalReference::BUILTIN_FP_INT_CALL: + PrintF("Returned %f\n", dresult); + break; + default: + UNREACHABLE(); + break; + } + } + } else if (redirection->type() == ExternalReference::DIRECT_API_CALL) { + if (::v8::internal::FLAG_trace_sim) { + PrintF("Call to host function at %p args %08" PRIx64 " \n", + reinterpret_cast(external), arg0); + } + SimulatorRuntimeDirectApiCall target = + reinterpret_cast(external); + target(arg0); + } else if (redirection->type() == ExternalReference::PROFILING_API_CALL) { + if (::v8::internal::FLAG_trace_sim) { + PrintF("Call to host function at %p args %08" PRIx64 " %08" PRIx64 + " \n", + reinterpret_cast(external), arg0, arg1); + } + SimulatorRuntimeProfilingApiCall target = + reinterpret_cast(external); + target(arg0, Redirection::ReverseRedirection(arg1)); + } else if (redirection->type() == ExternalReference::DIRECT_GETTER_CALL) { + if (::v8::internal::FLAG_trace_sim) { + PrintF("Call to host function at %p args %08" PRIx64 " %08" PRIx64 + " \n", + reinterpret_cast(external), arg0, arg1); + } + SimulatorRuntimeDirectGetterCall target = + reinterpret_cast(external); + target(arg0, arg1); + } else if (redirection->type() == + ExternalReference::PROFILING_GETTER_CALL) { + if (::v8::internal::FLAG_trace_sim) { + PrintF("Call to host function at %p args %08" PRIx64 " %08" PRIx64 + " %08" PRIx64 " \n", + reinterpret_cast(external), arg0, arg1, arg2); + } + SimulatorRuntimeProfilingGetterCall target = + reinterpret_cast(external); + target(arg0, arg1, Redirection::ReverseRedirection(arg2)); + } else { + DCHECK(redirection->type() == ExternalReference::BUILTIN_CALL || + redirection->type() == ExternalReference::BUILTIN_CALL_PAIR); + SimulatorRuntimeCall target = + reinterpret_cast(external); + if (::v8::internal::FLAG_trace_sim) { + PrintF( + "Call to host function at %p " + "args %08" PRIx64 " , %08" PRIx64 " , %08" PRIx64 " , %08" PRIx64 + " , %08" PRIx64 " , %08" PRIx64 " , %08" PRIx64 " , %08" PRIx64 + " , %08" PRIx64 " , %08" PRIx64 " \n", + reinterpret_cast(FUNCTION_ADDR(target)), arg0, arg1, arg2, + arg3, arg4, arg5, arg6, arg7, arg8, arg9); + } + ObjectPair result = + target(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); + set_register(a0, (int64_t)(result.x)); + set_register(a1, (int64_t)(result.y)); + } + if (::v8::internal::FLAG_trace_sim) { + PrintF("Returned %08" PRIx64 " : %08" PRIx64 " \n", get_register(a1), + get_register(a0)); + } + set_register(ra, saved_ra); + set_pc(get_register(ra)); + + } else if (func == 1) { // EBREAK + int32_t code = get_ebreak_code(instr_.instr()); + set_pc(get_pc() + kInstrSize * 2); + if (code != -1 && static_cast(code) <= kMaxStopCode) { + if (IsWatchpoint(code)) { + PrintWatchpoint(code); + } else { + IncreaseStopCounter(code); + HandleStop(code); + } + } else { + // All remaining break_ codes, and all traps are handled here. + RiscvDebugger dbg(this); + dbg.Debug(); + } + } else { + UNREACHABLE(); + } +} + +// Stop helper functions. +bool Simulator::IsWatchpoint(uint64_t code) { + return (code <= kMaxWatchpointCode); +} + +void Simulator::PrintWatchpoint(uint64_t code) { + RiscvDebugger dbg(this); + ++break_count_; + PrintF("\n---- watchpoint %" PRId64 " marker: %3d (instr count: %8" PRId64 + " ) ----------" + "----------------------------------", + code, break_count_, icount_); + dbg.PrintAllRegs(); // Print registers and continue running. +} + +void Simulator::HandleStop(uint64_t code) { + // Stop if it is enabled, otherwise go on jumping over the stop + // and the message address. + if (IsEnabledStop(code)) { + RiscvDebugger dbg(this); + PrintF("Simulator hit stop (%" PRId64 ")\n", code); + dbg.Debug(); + } +} + +bool Simulator::IsStopInstruction(Instruction* instr) { + if (instr->InstructionBits() != kBreakInstr) return false; + int32_t code = get_ebreak_code(instr); + return code != -1 && static_cast(code) > kMaxWatchpointCode && + static_cast(code) <= kMaxStopCode; +} + +bool Simulator::IsEnabledStop(uint64_t code) { + DCHECK_LE(code, kMaxStopCode); + DCHECK_GT(code, kMaxWatchpointCode); + return !(watched_stops_[code].count & kStopDisabledBit); +} + +void Simulator::EnableStop(uint64_t code) { + if (!IsEnabledStop(code)) { + watched_stops_[code].count &= ~kStopDisabledBit; + } +} + +void Simulator::DisableStop(uint64_t code) { + if (IsEnabledStop(code)) { + watched_stops_[code].count |= kStopDisabledBit; + } +} + +void Simulator::IncreaseStopCounter(uint64_t code) { + DCHECK_LE(code, kMaxStopCode); + if ((watched_stops_[code].count & ~(1 << 31)) == 0x7FFFFFFF) { + PrintF("Stop counter for code %" PRId64 + " has overflowed.\n" + "Enabling this code and reseting the counter to 0.\n", + code); + watched_stops_[code].count = 0; + EnableStop(code); + } else { + watched_stops_[code].count++; + } +} + +// Print a stop status. +void Simulator::PrintStopInfo(uint64_t code) { + if (code <= kMaxWatchpointCode) { + PrintF("That is a watchpoint, not a stop.\n"); + return; + } else if (code > kMaxStopCode) { + PrintF("Code too large, only %u stops can be used\n", kMaxStopCode + 1); + return; + } + const char* state = IsEnabledStop(code) ? "Enabled" : "Disabled"; + int32_t count = watched_stops_[code].count & ~kStopDisabledBit; + // Don't print the state of unused breakpoints. + if (count != 0) { + if (watched_stops_[code].desc) { + PrintF("stop %" PRId64 " - 0x%" PRIx64 " : \t%s, \tcounter = %i, \t%s\n", + code, code, state, count, watched_stops_[code].desc); + } else { + PrintF("stop %" PRId64 " - 0x%" PRIx64 " : \t%s, \tcounter = %i\n", code, + code, state, count); + } + } +} + +void Simulator::SignalException(Exception e) { + FATAL("Error: Exception %i raised.", static_cast(e)); +} + +// RISCV Instruction Decode Routine +void Simulator::DecodeRVRType() { + switch (instr_.InstructionBits() & kRTypeMask) { + case RO_ADD: { + set_rd(sext_xlen(rs1() + rs2())); + break; + } + case RO_SUB: { + set_rd(sext_xlen(rs1() - rs2())); + break; + } + case RO_SLL: { + set_rd(sext_xlen(rs1() << (rs2() & (xlen - 1)))); + break; + } + case RO_SLT: { + set_rd(sreg_t(rs1()) < sreg_t(rs2())); + break; + } + case RO_SLTU: { + set_rd(reg_t(rs1()) < reg_t(rs2())); + break; + } + case RO_XOR: { + set_rd(rs1() ^ rs2()); + break; + } + case RO_SRL: { + set_rd(sext_xlen(zext_xlen(rs1()) >> (rs2() & (xlen - 1)))); + break; + } + case RO_SRA: { + set_rd(sext_xlen(sext_xlen(rs1()) >> (rs2() & (xlen - 1)))); + break; + } + case RO_OR: { + set_rd(rs1() | rs2()); + break; + } + case RO_AND: { + set_rd(rs1() & rs2()); + break; + } +#ifdef V8_TARGET_ARCH_64_BIT + case RO_ADDW: { + set_rd(sext32(rs1() + rs2())); + break; + } + case RO_SUBW: { + set_rd(sext32(rs1() - rs2())); + break; + } + case RO_SLLW: { + set_rd(sext32(rs1() << (rs2() & 0x1F))); + break; + } + case RO_SRLW: { + set_rd(sext32(uint32_t(rs1()) >> (rs2() & 0x1F))); + break; + } + case RO_SRAW: { + set_rd(sext32(int32_t(rs1()) >> (rs2() & 0x1F))); + break; + } +#endif /* V8_TARGET_ARCH_64_BIT */ + // TODO: Add RISCV M extension macro + case RO_MUL: { + set_rd(rs1() * rs2()); + break; + } + case RO_MULH: { + set_rd(mulh(rs1(), rs2())); + break; + } + case RO_MULHSU: { + set_rd(mulhsu(rs1(), rs2())); + break; + } + case RO_MULHU: { + set_rd(mulhu(rs1(), rs2())); + break; + } + case RO_DIV: { + sreg_t lhs = sext_xlen(rs1()); + sreg_t rhs = sext_xlen(rs2()); + if (rhs == 0) { + set_rd(-1); + } else if (lhs == INT64_MIN && rhs == -1) { + set_rd(lhs); + } else { + set_rd(sext_xlen(lhs / rhs)); + } + break; + } + case RO_DIVU: { + reg_t lhs = zext_xlen(rs1()); + reg_t rhs = zext_xlen(rs2()); + if (rhs == 0) { + set_rd(UINT64_MAX); + } else { + set_rd(zext_xlen(lhs / rhs)); + } + break; + } + case RO_REM: { + sreg_t lhs = sext_xlen(rs1()); + sreg_t rhs = sext_xlen(rs2()); + if (rhs == 0) { + set_rd(lhs); + } else if (lhs == INT64_MIN && rhs == -1) { + set_rd(0); + } else { + set_rd(sext_xlen(lhs % rhs)); + } + break; + } + case RO_REMU: { + reg_t lhs = zext_xlen(rs1()); + reg_t rhs = zext_xlen(rs2()); + if (rhs == 0) { + set_rd(lhs); + } else { + set_rd(zext_xlen(lhs % rhs)); + } + break; + } +#ifdef V8_TARGET_ARCH_64_BIT + case RO_MULW: { + set_rd(sext32(sext32(rs1()) * sext32(rs2()))); + break; + } + case RO_DIVW: { + sreg_t lhs = sext32(rs1()); + sreg_t rhs = sext32(rs2()); + if (rhs == 0) { + set_rd(-1); + } else if (lhs == INT32_MIN && rhs == -1) { + set_rd(lhs); + } else { + set_rd(sext32(lhs / rhs)); + } + break; + } + case RO_DIVUW: { + reg_t lhs = zext32(rs1()); + reg_t rhs = zext32(rs2()); + if (rhs == 0) { + set_rd(UINT32_MAX); + } else { + set_rd(zext32(lhs / rhs)); + } + break; + } + case RO_REMW: { + sreg_t lhs = sext32(rs1()); + sreg_t rhs = sext32(rs2()); + if (rhs == 0) { + set_rd(lhs); + } else if (lhs == INT32_MIN && rhs == -1) { + set_rd(0); + } else { + set_rd(sext32(lhs % rhs)); + } + break; + } + case RO_REMUW: { + reg_t lhs = zext32(rs1()); + reg_t rhs = zext32(rs2()); + if (rhs == 0) { + set_rd(zext32(lhs)); + } else { + set_rd(zext32(lhs % rhs)); + } + break; + } +#endif /*V8_TARGET_ARCH_64_BIT*/ + // TODO: End Add RISCV M extension macro + default: { + switch (instr_.BaseOpcode()) { + case AMO: + DecodeRVRAType(); + break; + case OP_FP: + DecodeRVRFPType(); + break; + default: + UNSUPPORTED(); + } + } + } +} + +float Simulator::RoundF2FHelper(float input_val, int rmode) { + if (rmode == DYN) rmode = get_dynamic_rounding_mode(); + + float rounded = 0; + switch (rmode) { + case RNE: { // Round to Nearest, tiest to Even + int curr_mode = fegetround(); + fesetround(FE_TONEAREST); + rounded = std::nearbyintf(input_val); + fesetround(curr_mode); + break; + } + case RTZ: // Round towards Zero + rounded = std::truncf(input_val); + break; + case RDN: // Round Down (towards -infinity) + rounded = floorf(input_val); + break; + case RUP: // Round Up (towards +infinity) + rounded = ceilf(input_val); + break; + case RMM: // Round to Nearest, tiest to Max Magnitude + rounded = std::roundf(input_val); + break; + default: + UNREACHABLE(); + } + + return rounded; +} + +double Simulator::RoundF2FHelper(double input_val, int rmode) { + if (rmode == DYN) rmode = get_dynamic_rounding_mode(); + + double rounded = 0; + switch (rmode) { + case RNE: { // Round to Nearest, tiest to Even + int curr_mode = fegetround(); + fesetround(FE_TONEAREST); + rounded = std::nearbyint(input_val); + fesetround(curr_mode); + break; + } + case RTZ: // Round towards Zero + rounded = std::trunc(input_val); + break; + case RDN: // Round Down (towards -infinity) + rounded = std::floor(input_val); + break; + case RUP: // Round Up (towards +infinity) + rounded = std::ceil(input_val); + break; + case RMM: // Round to Nearest, tiest to Max Magnitude + rounded = std::round(input_val); + break; + default: + UNREACHABLE(); + } + return rounded; +} + +// convert rounded floating-point to integer types, handle input values that +// are out-of-range, underflow, or NaN, and set appropriate fflags +template +I_TYPE Simulator::RoundF2IHelper(F_TYPE original, int rmode) { + DCHECK(std::is_integral::value); + + DCHECK((std::is_same::value || + std::is_same::value)); + + I_TYPE max_i = std::numeric_limits::max(); + I_TYPE min_i = std::numeric_limits::min(); + + if (!std::isfinite(original)) { + set_fflags(kInvalidOperation); + if (std::isnan(original) || + original == std::numeric_limits::infinity()) { + return max_i; + } else { + DCHECK(original == -std::numeric_limits::infinity()); + return min_i; + } + } + + F_TYPE rounded = RoundF2FHelper(original, rmode); + if (original != rounded) set_fflags(kInexact); + + if (!std::isfinite(rounded)) { + set_fflags(kInvalidOperation); + if (std::isnan(rounded) || + rounded == std::numeric_limits::infinity()) { + return max_i; + } else { + DCHECK(rounded == -std::numeric_limits::infinity()); + return min_i; + } + } + + // FIXME (RISCV): comparison of rounded (float) and max_i (integer) may not + // be precise because max_i is promoted to floating point during comparison. + // Rounding up may happen when converting max_i to floating-point, e.g., + // max is 9223372036854775807 vs. (double)max is + // 9223372036854775808.00000000000000 + + // Since integer max values are either all 1s (for unsigned) or all 1s + // except for sign-bit (for signed), they cannot be represented precisely in + // floating point, in order to precisely tell whether the rounded floating + // point is within the max range, we compare against (max_i+1) which would + // have a single 1 w/ many trailing zeros + float max_i_plus_1 = + std::is_same::value + ? 0x1p64f // uint64_t::max + 1 cannot be represented in integers, + // so use its float representation directly + : static_cast(static_cast(max_i) + 1); + if (rounded >= max_i_plus_1) { + set_fflags(kOverflow | kInvalidOperation); + return max_i; + } + + // Since min_i (either 0 for unsigned, or for signed) is represented + // precisely in floating-point, comparing rounded directly against min_i + if (rounded <= min_i) { + if (rounded < min_i) set_fflags(kOverflow | kInvalidOperation); + return min_i; + } + + F_TYPE underflow_fval = + std::is_same::value ? FLT_MIN : DBL_MIN; + if (rounded < underflow_fval && rounded > -underflow_fval && rounded != 0) { + set_fflags(kUnderflow); + } + + return static_cast(rounded); +} + +template +static int64_t FclassHelper(T value) { + switch (std::fpclassify(value)) { + case FP_INFINITE: + return (std::signbit(value) ? kNegativeInfinity : kPositiveInfinity); + case FP_NAN: + return (isSnan(value) ? kSignalingNaN : kQuietNaN); + case FP_NORMAL: + return (std::signbit(value) ? kNegativeNormalNumber + : kPositiveNormalNumber); + case FP_SUBNORMAL: + return (std::signbit(value) ? kNegativeSubnormalNumber + : kPositiveSubnormalNumber); + case FP_ZERO: + return (std::signbit(value) ? kNegativeZero : kPositiveZero); + default: + UNREACHABLE(); + } +} + +template +bool Simulator::CompareFHelper(T input1, T input2, FPUCondition cc) { + DCHECK(std::is_floating_point::value); + bool result = false; + switch (cc) { + case LT: + case LE: + // FLT, FLE are signaling compares + if (std::isnan(input1) || std::isnan(input2)) { + set_fflags(kInvalidOperation); + result = false; + } else { + result = (cc == LT) ? (input1 < input2) : (input1 <= input2); + } + break; + + case EQ: + if (std::numeric_limits::signaling_NaN() == input1 || + std::numeric_limits::signaling_NaN() == input2) { + set_fflags(kInvalidOperation); + } + if (std::isnan(input1) || std::isnan(input2)) { + result = false; + } else { + result = (input1 == input2); + } + break; + + default: + UNREACHABLE(); + } + return result; +} + +template +static inline bool is_invalid_fmul(T src1, T src2) { + return (isinf(src1) && src2 == static_cast(0.0)) || + (src1 == static_cast(0.0) && isinf(src2)); +} + +template +static inline bool is_invalid_fadd(T src1, T src2) { + return (isinf(src1) && isinf(src2) && + std::signbit(src1) != std::signbit(src2)); +} + +template +static inline bool is_invalid_fsub(T src1, T src2) { + return (isinf(src1) && isinf(src2) && + std::signbit(src1) == std::signbit(src2)); +} + +template +static inline bool is_invalid_fdiv(T src1, T src2) { + return ((src1 == 0 && src2 == 0) || (isinf(src1) && isinf(src2))); +} + +template +static inline bool is_invalid_fsqrt(T src1) { + return (src1 < 0); +} + +void Simulator::DecodeRVRAType() { + // TODO: Add macro for RISCV A extension + // Special handling for A extension instructions because it uses func5 + // For all A extension instruction, V8 simulator is pure sequential. No + // Memory address lock or other synchronizaiton behaviors. + switch (instr_.InstructionBits() & kRATypeMask) { + case RO_LR_W: { + base::MutexGuard lock_guard(&GlobalMonitor::Get()->mutex); + int64_t addr = rs1(); + auto val = ReadMem(addr, instr_.instr()); + set_rd(sext32(val), false); + TraceMemRd(addr, val, get_register(rd_reg())); + local_monitor_.NotifyLoadLinked(addr, TransactionSize::Word); + GlobalMonitor::Get()->NotifyLoadLinked_Locked(addr, + &global_monitor_thread_); + break; + } + case RO_SC_W: { + int64_t addr = rs1(); + base::MutexGuard lock_guard(&GlobalMonitor::Get()->mutex); + if (local_monitor_.NotifyStoreConditional(addr, TransactionSize::Word) && + GlobalMonitor::Get()->NotifyStoreConditional_Locked( + addr, &global_monitor_thread_)) { + local_monitor_.NotifyStore(); + GlobalMonitor::Get()->NotifyStore_Locked(&global_monitor_thread_); + WriteMem(rs1(), (int32_t)rs2(), instr_.instr()); + set_rd(0, false); + } else { + set_rd(1, false); + } + break; + } + case RO_AMOSWAP_W: { + set_rd(sext32(amo( + rs1(), [&](uint32_t lhs) { return (uint32_t)rs2(); }, instr_.instr(), + WORD))); + break; + } + case RO_AMOADD_W: { + set_rd(sext32(amo( + rs1(), [&](uint32_t lhs) { return lhs + (uint32_t)rs2(); }, + instr_.instr(), WORD))); + break; + } + case RO_AMOXOR_W: { + set_rd(sext32(amo( + rs1(), [&](uint32_t lhs) { return lhs ^ (uint32_t)rs2(); }, + instr_.instr(), WORD))); + break; + } + case RO_AMOAND_W: { + set_rd(sext32(amo( + rs1(), [&](uint32_t lhs) { return lhs & (uint32_t)rs2(); }, + instr_.instr(), WORD))); + break; + } + case RO_AMOOR_W: { + set_rd(sext32(amo( + rs1(), [&](uint32_t lhs) { return lhs | (uint32_t)rs2(); }, + instr_.instr(), WORD))); + break; + } + case RO_AMOMIN_W: { + set_rd(sext32(amo( + rs1(), [&](int32_t lhs) { return std::min(lhs, (int32_t)rs2()); }, + instr_.instr(), WORD))); + break; + } + case RO_AMOMAX_W: { + set_rd(sext32(amo( + rs1(), [&](int32_t lhs) { return std::max(lhs, (int32_t)rs2()); }, + instr_.instr(), WORD))); + break; + } + case RO_AMOMINU_W: { + set_rd(sext32(amo( + rs1(), [&](uint32_t lhs) { return std::min(lhs, (uint32_t)rs2()); }, + instr_.instr(), WORD))); + break; + } + case RO_AMOMAXU_W: { + set_rd(sext32(amo( + rs1(), [&](uint32_t lhs) { return std::max(lhs, (uint32_t)rs2()); }, + instr_.instr(), WORD))); + break; + } +#ifdef V8_TARGET_ARCH_64_BIT + case RO_LR_D: { + base::MutexGuard lock_guard(&GlobalMonitor::Get()->mutex); + int64_t addr = rs1(); + auto val = ReadMem(addr, instr_.instr()); + set_rd(val, false); + TraceMemRd(addr, val, get_register(rd_reg())); + local_monitor_.NotifyLoadLinked(addr, TransactionSize::DoubleWord); + GlobalMonitor::Get()->NotifyLoadLinked_Locked(addr, + &global_monitor_thread_); + break; + } + case RO_SC_D: { + int64_t addr = rs1(); + base::MutexGuard lock_guard(&GlobalMonitor::Get()->mutex); + if (local_monitor_.NotifyStoreConditional(addr, + TransactionSize::DoubleWord) && + (GlobalMonitor::Get()->NotifyStoreConditional_Locked( + addr, &global_monitor_thread_))) { + GlobalMonitor::Get()->NotifyStore_Locked(&global_monitor_thread_); + WriteMem(rs1(), rs2(), instr_.instr()); + set_rd(0, false); + } else { + set_rd(1, false); + } + break; + } + case RO_AMOSWAP_D: { + set_rd(amo( + rs1(), [&](int64_t lhs) { return rs2(); }, instr_.instr(), DWORD)); + break; + } + case RO_AMOADD_D: { + set_rd(amo( + rs1(), [&](int64_t lhs) { return lhs + rs2(); }, instr_.instr(), + DWORD)); + break; + } + case RO_AMOXOR_D: { + set_rd(amo( + rs1(), [&](int64_t lhs) { return lhs ^ rs2(); }, instr_.instr(), + DWORD)); + break; + } + case RO_AMOAND_D: { + set_rd(amo( + rs1(), [&](int64_t lhs) { return lhs & rs2(); }, instr_.instr(), + DWORD)); + break; + } + case RO_AMOOR_D: { + set_rd(amo( + rs1(), [&](int64_t lhs) { return lhs | rs2(); }, instr_.instr(), + DWORD)); + break; + } + case RO_AMOMIN_D: { + set_rd(amo( + rs1(), [&](int64_t lhs) { return std::min(lhs, rs2()); }, + instr_.instr(), DWORD)); + break; + } + case RO_AMOMAX_D: { + set_rd(amo( + rs1(), [&](int64_t lhs) { return std::max(lhs, rs2()); }, + instr_.instr(), DWORD)); + break; + } + case RO_AMOMINU_D: { + set_rd(amo( + rs1(), [&](uint64_t lhs) { return std::min(lhs, (uint64_t)rs2()); }, + instr_.instr(), DWORD)); + break; + } + case RO_AMOMAXU_D: { + set_rd(amo( + rs1(), [&](uint64_t lhs) { return std::max(lhs, (uint64_t)rs2()); }, + instr_.instr(), DWORD)); + break; + } +#endif /*V8_TARGET_ARCH_64_BIT*/ + // TODO: End Add macro for RISCV A extension + default: { + UNSUPPORTED(); + } + } +} + +void Simulator::DecodeRVRFPType() { + // OP_FP instructions (F/D) uses func7 first. Some further uses fun3 and + // rs2() + + // kRATypeMask is only for func7 + switch (instr_.InstructionBits() & kRFPTypeMask) { + // TODO: Add macro for RISCV F extension + case RO_FADD_S: { + // TODO: use rm value (round mode) + auto fn = [this](float frs1, float frs2) { + if (is_invalid_fadd(frs1, frs2)) { + this->set_fflags(kInvalidOperation); + return std::numeric_limits::quiet_NaN(); + } else { + return frs1 + frs2; + } + }; + set_frd(CanonicalizeFPUOp2(fn)); + break; + } + case RO_FSUB_S: { + // TODO: use rm value (round mode) + auto fn = [this](float frs1, float frs2) { + if (is_invalid_fsub(frs1, frs2)) { + this->set_fflags(kInvalidOperation); + return std::numeric_limits::quiet_NaN(); + } else { + return frs1 - frs2; + } + }; + set_frd(CanonicalizeFPUOp2(fn)); + break; + } + case RO_FMUL_S: { + // TODO: use rm value (round mode) + auto fn = [this](float frs1, float frs2) { + if (is_invalid_fmul(frs1, frs2)) { + this->set_fflags(kInvalidOperation); + return std::numeric_limits::quiet_NaN(); + } else { + return frs1 * frs2; + } + }; + set_frd(CanonicalizeFPUOp2(fn)); + break; + } + case RO_FDIV_S: { + // TODO: use rm value (round mode) + auto fn = [this](float frs1, float frs2) { + if (is_invalid_fdiv(frs1, frs2)) { + this->set_fflags(kInvalidOperation); + return std::numeric_limits::quiet_NaN(); + } else if (frs2 == 0.0f) { + this->set_fflags(kDivideByZero); + return (std::signbit(frs1) == std::signbit(frs2) + ? std::numeric_limits::infinity() + : -std::numeric_limits::infinity()); + } else { + return frs1 / frs2; + } + }; + set_frd(CanonicalizeFPUOp2(fn)); + break; + } + case RO_FSQRT_S: { + if (instr_.Rs2Value() == 0b00000) { + // TODO: use rm value (round mode) + auto fn = [this](float frs) { + if (is_invalid_fsqrt(frs)) { + this->set_fflags(kInvalidOperation); + return std::numeric_limits::quiet_NaN(); + } else { + return std::sqrt(frs); + } + }; + set_frd(CanonicalizeFPUOp1(fn)); + } else { + UNSUPPORTED(); + } + break; + } + case RO_FSGNJ_S: { // RO_FSGNJN_S RO_FSQNJX_S + switch (instr_.Funct3Value()) { + case 0b000: { // RO_FSGNJ_S + set_frd(fsgnj32(frs1(), frs2(), false, false)); + break; + } + case 0b001: { // RO_FSGNJN_S + set_frd(fsgnj32(frs1(), frs2(), true, false)); + break; + } + case 0b010: { // RO_FSQNJX_S + set_frd(fsgnj32(frs1(), frs2(), false, true)); + break; + } + default: { + UNSUPPORTED(); + } + } + break; + } + case RO_FMIN_S: { // RO_FMAX_S + switch (instr_.Funct3Value()) { + case 0b000: { // RO_FMIN_S + set_frd(FMaxMinHelper(frs1(), frs2(), MaxMinKind::kMin)); + break; + } + case 0b001: { // RO_FMAX_S + set_frd(FMaxMinHelper(frs1(), frs2(), MaxMinKind::kMax)); + break; + } + default: { + UNSUPPORTED(); + } + } + break; + } + case RO_FCVT_W_S: { // RO_FCVT_WU_S , 64F RO_FCVT_L_S RO_FCVT_LU_S + float original_val = frs1(); + switch (instr_.Rs2Value()) { + case 0b00000: { // RO_FCVT_W_S + set_rd(RoundF2IHelper(original_val, instr_.RoundMode())); + break; + } + case 0b00001: { // RO_FCVT_WU_S + set_rd(RoundF2IHelper(original_val, instr_.RoundMode())); + break; + } +#ifdef V8_TARGET_ARCH_64_BIT + case 0b00010: { // RO_FCVT_L_S + set_rd(RoundF2IHelper(original_val, instr_.RoundMode())); + break; + } + case 0b00011: { // RO_FCVT_LU_S + set_rd(RoundF2IHelper(original_val, instr_.RoundMode())); + break; + } +#endif /* V8_TARGET_ARCH_64_BIT */ + default: { + UNSUPPORTED(); + } + } + break; + } + case RO_FMV: { // RO_FCLASS_S + switch (instr_.Funct3Value()) { + case 0b000: { + if (instr_.Rs2Value() == 0b00000) { + // RO_FMV_X_W + set_rd(sext_xlen(get_fpu_register_word(rs1_reg()))); + } else { + UNSUPPORTED(); + } + break; + } + case 0b001: { // RO_FCLASS_S + set_rd(FclassHelper(frs1())); + break; + } + default: { + UNSUPPORTED(); + } + } + break; + } + // FIXME (RISCV): implement handling of NaN (quiet and signalling) + case RO_FLE_S: { // RO_FEQ_S RO_FLT_S RO_FLE_S + switch (instr_.Funct3Value()) { + case 0b010: { // RO_FEQ_S + set_rd(CompareFHelper(frs1(), frs2(), EQ)); + break; + } + case 0b001: { // RO_FLT_S + set_rd(CompareFHelper(frs1(), frs2(), LT)); + break; + } + case 0b000: { // RO_FLE_S + set_rd(CompareFHelper(frs1(), frs2(), LE)); + break; + } + default: { + UNSUPPORTED(); + } + } + break; + } + case RO_FCVT_S_W: { // RO_FCVT_S_WU , 64F RO_FCVT_S_L RO_FCVT_S_LU + switch (instr_.Rs2Value()) { + case 0b00000: { // RO_FCVT_S_W + set_frd((float)(int32_t)rs1()); + break; + } + case 0b00001: { // RO_FCVT_S_WU + set_frd((float)(uint32_t)rs1()); + break; + } +#ifdef V8_TARGET_ARCH_64_BIT + case 0b00010: { // RO_FCVT_S_L + set_frd((float)(int64_t)rs1()); + break; + } + case 0b00011: { // RO_FCVT_S_LU + set_frd((float)(uint64_t)rs1()); + break; + } +#endif /* V8_TARGET_ARCH_64_BIT */ + default: { + UNSUPPORTED(); + } + } + break; + } + case RO_FMV_W_X: { + if (instr_.Funct3Value() == 0b000) { + // since FMV preserves source bit-pattern, no need to canonize + set_frd(bit_cast((uint32_t)rs1())); + } else { + UNSUPPORTED(); + } + break; + } + // TODO: Add macro for RISCV D extension + case RO_FADD_D: { + // TODO: use rm value (round mode) + auto fn = [this](double drs1, double drs2) { + if (is_invalid_fadd(drs1, drs2)) { + this->set_fflags(kInvalidOperation); + return std::numeric_limits::quiet_NaN(); + } else { + return drs1 + drs2; + } + }; + set_drd(CanonicalizeFPUOp2(fn)); + break; + } + case RO_FSUB_D: { + // TODO: use rm value (round mode) + auto fn = [this](double drs1, double drs2) { + if (is_invalid_fsub(drs1, drs2)) { + this->set_fflags(kInvalidOperation); + return std::numeric_limits::quiet_NaN(); + } else { + return drs1 - drs2; + } + }; + set_drd(CanonicalizeFPUOp2(fn)); + break; + } + case RO_FMUL_D: { + // TODO: use rm value (round mode) + auto fn = [this](double drs1, double drs2) { + if (is_invalid_fmul(drs1, drs2)) { + this->set_fflags(kInvalidOperation); + return std::numeric_limits::quiet_NaN(); + } else { + return drs1 * drs2; + } + }; + set_drd(CanonicalizeFPUOp2(fn)); + break; + } + case RO_FDIV_D: { + // TODO: use rm value (round mode) + auto fn = [this](double drs1, double drs2) { + if (is_invalid_fdiv(drs1, drs2)) { + this->set_fflags(kInvalidOperation); + return std::numeric_limits::quiet_NaN(); + } else if (drs2 == 0.0) { + this->set_fflags(kDivideByZero); + return (std::signbit(drs1) == std::signbit(drs2) + ? std::numeric_limits::infinity() + : -std::numeric_limits::infinity()); + } else { + return drs1 / drs2; + } + }; + set_drd(CanonicalizeFPUOp2(fn)); + break; + } + case RO_FSQRT_D: { + if (instr_.Rs2Value() == 0b00000) { + // TODO: use rm value (round mode) + auto fn = [this](double drs) { + if (is_invalid_fsqrt(drs)) { + this->set_fflags(kInvalidOperation); + return std::numeric_limits::quiet_NaN(); + } else { + return std::sqrt(drs); + } + }; + set_drd(CanonicalizeFPUOp1(fn)); + } else { + UNSUPPORTED(); + } + break; + } + case RO_FSGNJ_D: { // RO_FSGNJN_D RO_FSQNJX_D + switch (instr_.Funct3Value()) { + case 0b000: { // RO_FSGNJ_D + set_drd(fsgnj64(drs1(), drs2(), false, false)); + break; + } + case 0b001: { // RO_FSGNJN_D + set_drd(fsgnj64(drs1(), drs2(), true, false)); + break; + } + case 0b010: { // RO_FSQNJX_D + set_drd(fsgnj64(drs1(), drs2(), false, true)); + break; + } + default: { + UNSUPPORTED(); + } + } + break; + } + case RO_FMIN_D: { // RO_FMAX_D + switch (instr_.Funct3Value()) { + case 0b000: { // RO_FMIN_D + set_drd(FMaxMinHelper(drs1(), drs2(), MaxMinKind::kMin)); + break; + } + case 0b001: { // RO_FMAX_D + set_drd(FMaxMinHelper(drs1(), drs2(), MaxMinKind::kMax)); + break; + } + default: { + UNSUPPORTED(); + } + } + break; + } + case (RO_FCVT_S_D & kRFPTypeMask): { + if (instr_.Rs2Value() == 0b00001) { + auto fn = [](double drs) { return (float)drs; }; + set_frd(CanonicalizeDoubleToFloatOperation(fn)); + } else { + UNSUPPORTED(); + } + break; + } + case RO_FCVT_D_S: { + if (instr_.Rs2Value() == 0b00000) { + auto fn = [](float frs) { return (double)frs; }; + set_drd(CanonicalizeFloatToDoubleOperation(fn)); + } else { + UNSUPPORTED(); + } + break; + } + case RO_FLE_D: { // RO_FEQ_D RO_FLT_D RO_FLE_D + switch (instr_.Funct3Value()) { + case 0b010: { // RO_FEQ_S + set_rd(CompareFHelper(drs1(), drs2(), EQ)); + break; + } + case 0b001: { // RO_FLT_D + set_rd(CompareFHelper(drs1(), drs2(), LT)); + break; + } + case 0b000: { // RO_FLE_D + set_rd(CompareFHelper(drs1(), drs2(), LE)); + break; + } + default: { + UNSUPPORTED(); + } + } + break; + } + case (RO_FCLASS_D & kRFPTypeMask): { // RO_FCLASS_D , 64D RO_FMV_X_D + if (instr_.Rs2Value() != 0b00000) { + UNSUPPORTED(); + break; + } + switch (instr_.Funct3Value()) { + case 0b001: { // RO_FCLASS_D + set_rd(FclassHelper(drs1())); + break; + } +#ifdef V8_TARGET_ARCH_64_BIT + case 0b000: { // RO_FMV_X_D + set_rd(bit_cast(drs1())); + break; + } +#endif /* V8_TARGET_ARCH_64_BIT */ + default: { + UNSUPPORTED(); + } + } + break; + } + case RO_FCVT_W_D: { // RO_FCVT_WU_D , 64F RO_FCVT_L_D RO_FCVT_LU_D + double original_val = drs1(); + switch (instr_.Rs2Value()) { + case 0b00000: { // RO_FCVT_W_D + set_rd(RoundF2IHelper(original_val, instr_.RoundMode())); + break; + } + case 0b00001: { // RO_FCVT_WU_D + set_rd(RoundF2IHelper(original_val, instr_.RoundMode())); + break; + } +#ifdef V8_TARGET_ARCH_64_BIT + case 0b00010: { // RO_FCVT_L_D + set_rd(RoundF2IHelper(original_val, instr_.RoundMode())); + break; + } + case 0b00011: { // RO_FCVT_LU_D + set_rd(RoundF2IHelper(original_val, instr_.RoundMode())); + break; + } +#endif /* V8_TARGET_ARCH_64_BIT */ + default: { + UNSUPPORTED(); + } + } + break; + } + case RO_FCVT_D_W: { // RO_FCVT_D_WU , 64F RO_FCVT_D_L RO_FCVT_D_LU + switch (instr_.Rs2Value()) { + case 0b00000: { // RO_FCVT_D_W + set_drd((int32_t)rs1()); + break; + } + case 0b00001: { // RO_FCVT_D_WU + set_drd((uint32_t)rs1()); + break; + } +#ifdef V8_TARGET_ARCH_64_BIT + case 0b00010: { // RO_FCVT_D_L + set_drd((int64_t)rs1()); + break; + } + case 0b00011: { // RO_FCVT_D_LU + set_drd((uint64_t)rs1()); + break; + } +#endif /* V8_TARGET_ARCH_64_BIT */ + default: { + UNSUPPORTED(); + } + } + break; + } +#ifdef V8_TARGET_ARCH_64_BIT + case RO_FMV_D_X: { + if (instr_.Funct3Value() == 0b000 && instr_.Rs2Value() == 0b00000) { + // Since FMV preserves source bit-pattern, no need to canonize + set_drd(bit_cast(rs1())); + } else { + UNSUPPORTED(); + } + break; + } +#endif /* V8_TARGET_ARCH_64_BIT */ + default: { + UNSUPPORTED(); + } + } +} + +void Simulator::DecodeRVR4Type() { + switch (instr_.InstructionBits() & kR4TypeMask) { + // TODO: use F Extension macro block + case RO_FMADD_S: { + // TODO: use rm value (round mode) + auto fn = [this](float frs1, float frs2, float frs3) { + if (is_invalid_fmul(frs1, frs2) || is_invalid_fadd(frs1 * frs2, frs3)) { + this->set_fflags(kInvalidOperation); + return std::numeric_limits::quiet_NaN(); + } else { + return std::fma(frs1, frs2, frs3); + } + }; + set_frd(CanonicalizeFPUOp3(fn)); + break; + } + case RO_FMSUB_S: { + // TODO: use rm value (round mode) + auto fn = [this](float frs1, float frs2, float frs3) { + if (is_invalid_fmul(frs1, frs2) || is_invalid_fsub(frs1 * frs2, frs3)) { + this->set_fflags(kInvalidOperation); + return std::numeric_limits::quiet_NaN(); + } else { + return std::fma(frs1, frs2, -frs3); + } + }; + set_frd(CanonicalizeFPUOp3(fn)); + break; + } + case RO_FNMSUB_S: { + // TODO: use rm value (round mode) + auto fn = [this](float frs1, float frs2, float frs3) { + if (is_invalid_fmul(frs1, frs2) || is_invalid_fsub(frs3, frs1 * frs2)) { + this->set_fflags(kInvalidOperation); + return std::numeric_limits::quiet_NaN(); + } else { + return -std::fma(frs1, frs2, -frs3); + } + }; + set_frd(CanonicalizeFPUOp3(fn)); + break; + } + case RO_FNMADD_S: { + // TODO: use rm value (round mode) + auto fn = [this](float frs1, float frs2, float frs3) { + if (is_invalid_fmul(frs1, frs2) || is_invalid_fadd(frs1 * frs2, frs3)) { + this->set_fflags(kInvalidOperation); + return std::numeric_limits::quiet_NaN(); + } else { + return -std::fma(frs1, frs2, frs3); + } + }; + set_frd(CanonicalizeFPUOp3(fn)); + break; + } + // TODO: use F Extension macro block + case RO_FMADD_D: { + // TODO: use rm value (round mode) + auto fn = [this](double drs1, double drs2, double drs3) { + if (is_invalid_fmul(drs1, drs2) || is_invalid_fadd(drs1 * drs2, drs3)) { + this->set_fflags(kInvalidOperation); + return std::numeric_limits::quiet_NaN(); + } else { + return std::fma(drs1, drs2, drs3); + } + }; + set_drd(CanonicalizeFPUOp3(fn)); + break; + } + case RO_FMSUB_D: { + // TODO: use rm value (round mode) + auto fn = [this](double drs1, double drs2, double drs3) { + if (is_invalid_fmul(drs1, drs2) || is_invalid_fsub(drs1 * drs2, drs3)) { + this->set_fflags(kInvalidOperation); + return std::numeric_limits::quiet_NaN(); + } else { + return std::fma(drs1, drs2, -drs3); + } + }; + set_drd(CanonicalizeFPUOp3(fn)); + break; + } + case RO_FNMSUB_D: { + // TODO: use rm value (round mode) + auto fn = [this](double drs1, double drs2, double drs3) { + if (is_invalid_fmul(drs1, drs2) || is_invalid_fsub(drs3, drs1 * drs2)) { + this->set_fflags(kInvalidOperation); + return std::numeric_limits::quiet_NaN(); + } else { + return -std::fma(drs1, drs2, -drs3); + } + }; + set_drd(CanonicalizeFPUOp3(fn)); + break; + } + case RO_FNMADD_D: { + // TODO: use rm value (round mode) + auto fn = [this](double drs1, double drs2, double drs3) { + if (is_invalid_fmul(drs1, drs2) || is_invalid_fadd(drs1 * drs2, drs3)) { + this->set_fflags(kInvalidOperation); + return std::numeric_limits::quiet_NaN(); + } else { + return -std::fma(drs1, drs2, drs3); + } + }; + set_drd(CanonicalizeFPUOp3(fn)); + break; + } + default: + UNSUPPORTED(); + } +} + +void Simulator::DecodeRVIType() { + switch (instr_.InstructionBits() & kITypeMask) { + case RO_JALR: { + set_rd(get_pc() + kInstrSize); + // Note: No need to shift 2 for JALR's imm12, but set lowest bit to 0. + int64_t next_pc = (rs1() + imm12()) & ~reg_t(1); + set_pc(next_pc); + break; + } + case RO_LB: { + int64_t addr = rs1() + imm12(); + int8_t val = ReadMem(addr, instr_.instr()); + set_rd(sext_xlen(val), false); + TraceMemRd(addr, val, get_register(rd_reg())); + break; + } + case RO_LH: { + int64_t addr = rs1() + imm12(); + int16_t val = ReadMem(addr, instr_.instr()); + set_rd(sext_xlen(val), false); + TraceMemRd(addr, val, get_register(rd_reg())); + break; + } + case RO_LW: { + int64_t addr = rs1() + imm12(); + int32_t val = ReadMem(addr, instr_.instr()); + set_rd(sext_xlen(val), false); + TraceMemRd(addr, val, get_register(rd_reg())); + break; + } + case RO_LBU: { + int64_t addr = rs1() + imm12(); + uint8_t val = ReadMem(addr, instr_.instr()); + set_rd(zext_xlen(val), false); + TraceMemRd(addr, val, get_register(rd_reg())); + break; + } + case RO_LHU: { + int64_t addr = rs1() + imm12(); + uint16_t val = ReadMem(addr, instr_.instr()); + set_rd(zext_xlen(val), false); + TraceMemRd(addr, val, get_register(rd_reg())); + break; + } +#ifdef V8_TARGET_ARCH_64_BIT + case RO_LWU: { + int64_t addr = rs1() + imm12(); + uint32_t val = ReadMem(addr, instr_.instr()); + set_rd(zext_xlen(val), false); + TraceMemRd(addr, val, get_register(rd_reg())); + break; + } + case RO_LD: { + int64_t addr = rs1() + imm12(); + int64_t val = ReadMem(addr, instr_.instr()); + set_rd(sext_xlen(val), false); + TraceMemRd(addr, val, get_register(rd_reg())); + break; + } +#endif /*V8_TARGET_ARCH_64_BIT*/ + case RO_ADDI: { + set_rd(sext_xlen(rs1() + imm12())); + break; + } + case RO_SLTI: { + set_rd(sreg_t(rs1()) < sreg_t(imm12())); + break; + } + case RO_SLTIU: { + set_rd(reg_t(rs1()) < reg_t(imm12())); + break; + } + case RO_XORI: { + set_rd(imm12() ^ rs1()); + break; + } + case RO_ORI: { + set_rd(imm12() | rs1()); + break; + } + case RO_ANDI: { + set_rd(imm12() & rs1()); + break; + } + case RO_SLLI: { + require(shamt() < xlen); + set_rd(sext_xlen(rs1() << shamt())); + break; + } + case RO_SRLI: { // RO_SRAI + if (!instr_.IsArithShift()) { + require(shamt() < xlen); + set_rd(sext_xlen(zext_xlen(rs1()) >> shamt())); + } else { + require(shamt() < xlen); + set_rd(sext_xlen(sext_xlen(rs1()) >> shamt())); + } + break; + } +#ifdef V8_TARGET_ARCH_64_BIT + case RO_ADDIW: { + set_rd(sext32(rs1() + imm12())); + break; + } + case RO_SLLIW: { + set_rd(sext32(rs1() << shamt32())); + break; + } + case RO_SRLIW: { // RO_SRAIW + if (!instr_.IsArithShift()) { + set_rd(sext32(uint32_t(rs1()) >> shamt32())); + } else { + set_rd(sext32(int32_t(rs1()) >> shamt32())); + } + break; + } +#endif /*V8_TARGET_ARCH_64_BIT*/ + case RO_FENCE: { + // DO nothing in sumulator + break; + } + case RO_ECALL: { // RO_EBREAK + if (instr_.Imm12Value() == 0) { // ECALL + SoftwareInterrupt(); + } else if (instr_.Imm12Value() == 1) { // EBREAK + SoftwareInterrupt(); + } else { + UNSUPPORTED(); + } + break; + } + // TODO: use Zifencei Standard Extension macro block + case RO_FENCE_I: { + // spike: flush icache. + break; + } + // TODO: use Zicsr Standard Extension macro block + case RO_CSRRW: { + if (rd_reg() != zero_reg) { + set_rd(zext_xlen(read_csr_value(csr_reg()))); + } + write_csr_value(csr_reg(), rs1()); + break; + } + case RO_CSRRS: { + set_rd(zext_xlen(read_csr_value(csr_reg()))); + if (rs1_reg() != zero_reg) { + set_csr_bits(csr_reg(), rs1()); + } + break; + } + case RO_CSRRC: { + set_rd(zext_xlen(read_csr_value(csr_reg()))); + if (rs1_reg() != zero_reg) { + clear_csr_bits(csr_reg(), rs1()); + } + break; + } + case RO_CSRRWI: { + if (rd_reg() != zero_reg) { + set_rd(zext_xlen(read_csr_value(csr_reg()))); + } + write_csr_value(csr_reg(), imm5CSR()); + break; + } + case RO_CSRRSI: { + set_rd(zext_xlen(read_csr_value(csr_reg()))); + if (imm5CSR() != 0) { + set_csr_bits(csr_reg(), imm5CSR()); + } + break; + } + case RO_CSRRCI: { + set_rd(zext_xlen(read_csr_value(csr_reg()))); + if (imm5CSR() != 0) { + clear_csr_bits(csr_reg(), imm5CSR()); + } + break; + } + // TODO: use F Extension macro block + case RO_FLW: { + int64_t addr = rs1() + imm12(); + float val = ReadMem(addr, instr_.instr()); + set_frd(val, false); + TraceMemRd(addr, val, get_fpu_register(frd_reg())); + break; + } + // TODO: use D Extension macro block + case RO_FLD: { + int64_t addr = rs1() + imm12(); + double val = ReadMem(addr, instr_.instr()); + set_drd(val, false); + TraceMemRd(addr, val, get_fpu_register(frd_reg())); + break; + } + default: + UNSUPPORTED(); + } +} + +void Simulator::DecodeRVSType() { + switch (instr_.InstructionBits() & kSTypeMask) { + case RO_SB: + WriteMem(rs1() + s_imm12(), (uint8_t)rs2(), instr_.instr()); + break; + case RO_SH: + WriteMem(rs1() + s_imm12(), (uint16_t)rs2(), instr_.instr()); + break; + case RO_SW: + WriteMem(rs1() + s_imm12(), (uint32_t)rs2(), instr_.instr()); + break; +#ifdef V8_TARGET_ARCH_64_BIT + case RO_SD: + WriteMem(rs1() + s_imm12(), (uint64_t)rs2(), instr_.instr()); + break; +#endif /*V8_TARGET_ARCH_64_BIT*/ + // TODO: use F Extension macro block + case RO_FSW: { + WriteMem(rs1() + s_imm12(), + (uint32_t)get_fpu_register_word(rs2_reg()), + instr_.instr()); + break; + } + // TODO: use D Extension macro block + case RO_FSD: { + WriteMem(rs1() + s_imm12(), drs2(), instr_.instr()); + break; + } + default: + UNSUPPORTED(); + } +} + +void Simulator::DecodeRVBType() { + switch (instr_.InstructionBits() & kBTypeMask) { + case RO_BEQ: + if (rs1() == rs2()) { + int64_t next_pc = get_pc() + boffset(); + set_pc(next_pc); + } + break; + case RO_BNE: + if (rs1() != rs2()) { + int64_t next_pc = get_pc() + boffset(); + set_pc(next_pc); + } + break; + case RO_BLT: + if (rs1() < rs2()) { + int64_t next_pc = get_pc() + boffset(); + set_pc(next_pc); + } + break; + case RO_BGE: + if (rs1() >= rs2()) { + int64_t next_pc = get_pc() + boffset(); + set_pc(next_pc); + } + break; + case RO_BLTU: + if ((reg_t)rs1() < (reg_t)rs2()) { + int64_t next_pc = get_pc() + boffset(); + set_pc(next_pc); + } + break; + case RO_BGEU: + if ((reg_t)rs1() >= (reg_t)rs2()) { + int64_t next_pc = get_pc() + boffset(); + set_pc(next_pc); + } + break; + default: + UNSUPPORTED(); + } +} +void Simulator::DecodeRVUType() { + // U Type doesn't have additoinal mask + switch (instr_.BaseOpcodeFieldRaw()) { + case RO_LUI: + set_rd(u_imm()); + break; + case RO_AUIPC: + set_rd(sext_xlen(u_imm() + get_pc())); + break; + default: + UNSUPPORTED(); + } +} +void Simulator::DecodeRVJType() { + // J Type doesn't have additional mask + switch (instr_.BaseOpcodeValue()) { + case RO_JAL: { + set_rd(get_pc() + kInstrSize); + int64_t next_pc = get_pc() + imm20J(); + set_pc(next_pc); + break; + } + default: + UNSUPPORTED(); + } +} + +// Executes the current instruction. +void Simulator::InstructionDecode(Instruction* instr) { + if (v8::internal::FLAG_check_icache) { + CheckICache(i_cache(), instr); + } + pc_modified_ = false; + + v8::internal::EmbeddedVector buffer; + + if (::v8::internal::FLAG_trace_sim) { + SNPrintF(trace_buf_, " "); + disasm::NameConverter converter; + disasm::Disassembler dasm(converter); + // Use a reasonably large buffer. + dasm.InstructionDecode(buffer, reinterpret_cast(instr)); + + // PrintF("EXECUTING 0x%08" PRIxPTR " %-44s\n", + // reinterpret_cast(instr), buffer.begin()); + } + + instr_ = instr; + switch (instr_.InstructionType()) { + case Instruction::kRType: + DecodeRVRType(); + break; + case Instruction::kR4Type: + DecodeRVR4Type(); + break; + case Instruction::kIType: + DecodeRVIType(); + break; + case Instruction::kSType: + DecodeRVSType(); + break; + case Instruction::kBType: + DecodeRVBType(); + break; + case Instruction::kUType: + DecodeRVUType(); + break; + case Instruction::kJType: + DecodeRVJType(); + break; + default: + if (::v8::internal::FLAG_trace_sim) { + std::cout << "Unrecognized instruction [@pc=0x" << std::hex + << registers_[pc] << "]: 0x" << instr->InstructionBits() + << std::endl; + } + UNSUPPORTED(); + } + + if (::v8::internal::FLAG_trace_sim) { + PrintF(" 0x%012" PRIxPTR " %-44s %s\n", + reinterpret_cast(instr), buffer.begin(), + trace_buf_.begin()); + } + + if (!pc_modified_) { + set_register(pc, reinterpret_cast(instr) + kInstrSize); + } +} + +void Simulator::Execute() { + // Get the PC to simulate. Cannot use the accessor here as we need the + // raw PC value and not the one used as input to arithmetic instructions. + int64_t program_counter = get_pc(); + while (program_counter != end_sim_pc) { + Instruction* instr = reinterpret_cast(program_counter); + icount_++; + if (icount_ == static_cast(::v8::internal::FLAG_stop_sim_at)) { + RiscvDebugger dbg(this); + dbg.Debug(); + } else { + InstructionDecode(instr); + } + CheckBreakpoints(); + program_counter = get_pc(); + } +} + +void Simulator::CallInternal(Address entry) { + // Adjust JS-based stack limit to C-based stack limit. + isolate_->stack_guard()->AdjustStackLimitForSimulator(); + + // Prepare to execute the code at entry. + set_register(pc, static_cast(entry)); + // Put down marker for end of simulation. The simulator will stop simulation + // when the PC reaches this value. By saving the "end simulation" value into + // the LR the simulation stops when returning to this call point. + set_register(ra, end_sim_pc); + + // Remember the values of callee-saved registers. + // The code below assumes that r9 is not used as sb (static base) in + // simulator code and therefore is regarded as a callee-saved register. + int64_t s0_val = get_register(s0); + int64_t s1_val = get_register(s1); + int64_t s2_val = get_register(s2); + int64_t s3_val = get_register(s3); + int64_t s4_val = get_register(s4); + int64_t s5_val = get_register(s5); + int64_t s6_val = get_register(s6); + int64_t s7_val = get_register(s7); + int64_t gp_val = get_register(gp); + int64_t sp_val = get_register(sp); + int64_t fp_val = get_register(fp); + + // Set up the callee-saved registers with a known value. To be able to check + // that they are preserved properly across JS execution. + int64_t callee_saved_value = icount_; + set_register(s0, callee_saved_value); + set_register(s1, callee_saved_value); + set_register(s2, callee_saved_value); + set_register(s3, callee_saved_value); + set_register(s4, callee_saved_value); + set_register(s5, callee_saved_value); + set_register(s6, callee_saved_value); + set_register(s7, callee_saved_value); + set_register(gp, callee_saved_value); + set_register(fp, callee_saved_value); + + // Start the simulation. + Execute(); + + // Check that the callee-saved registers have been preserved. + CHECK_EQ(callee_saved_value, get_register(s0)); + CHECK_EQ(callee_saved_value, get_register(s1)); + CHECK_EQ(callee_saved_value, get_register(s2)); + CHECK_EQ(callee_saved_value, get_register(s3)); + CHECK_EQ(callee_saved_value, get_register(s4)); + CHECK_EQ(callee_saved_value, get_register(s5)); + CHECK_EQ(callee_saved_value, get_register(s6)); + CHECK_EQ(callee_saved_value, get_register(s7)); + CHECK_EQ(callee_saved_value, get_register(gp)); + CHECK_EQ(callee_saved_value, get_register(fp)); + + // Restore callee-saved registers with the original value. + set_register(s0, s0_val); + set_register(s1, s1_val); + set_register(s2, s2_val); + set_register(s3, s3_val); + set_register(s4, s4_val); + set_register(s5, s5_val); + set_register(s6, s6_val); + set_register(s7, s7_val); + set_register(gp, gp_val); + set_register(sp, sp_val); + set_register(fp, fp_val); +} + +intptr_t Simulator::CallImpl(Address entry, int argument_count, + const intptr_t* arguments) { + constexpr int kRegisterPassedArguments = 8; + // Set up arguments. + + // First four arguments passed in registers in both ABI's. + int reg_arg_count = std::min(kRegisterPassedArguments, argument_count); + if (reg_arg_count > 0) set_register(a0, arguments[0]); + if (reg_arg_count > 1) set_register(a1, arguments[1]); + if (reg_arg_count > 2) set_register(a2, arguments[2]); + if (reg_arg_count > 3) set_register(a3, arguments[3]); + + // Up to eight arguments passed in registers in N64 ABI. + // TODO(plind): N64 ABI calls these regs a4 - a7. Clarify this. + if (reg_arg_count > 4) set_register(a4, arguments[4]); + if (reg_arg_count > 5) set_register(a5, arguments[5]); + if (reg_arg_count > 6) set_register(a6, arguments[6]); + if (reg_arg_count > 7) set_register(a7, arguments[7]); + + if (::v8::internal::FLAG_trace_sim) { + std::cout << "CallImpl: reg_arg_count = " << reg_arg_count << std::hex + << " entry-pc (JSEntry) = 0x" << entry << " a0 (Isolate) = 0x" + << get_register(a0) << " a1 (orig_func/new_target) = 0x" + << get_register(a1) << " a2 (func/target) = 0x" + << get_register(a2) << " a3 (receiver) = 0x" << get_register(a3) + << " a4 (argc) = 0x" << get_register(a4) << " a5 (argv) = 0x" + << get_register(a5) << std::endl; + } + + // Remaining arguments passed on stack. + int64_t original_stack = get_register(sp); + // Compute position of stack on entry to generated code. + int stack_args_count = argument_count - reg_arg_count; + int stack_args_size = stack_args_count * sizeof(*arguments) + kCArgsSlotsSize; + int64_t entry_stack = original_stack - stack_args_size; + + if (base::OS::ActivationFrameAlignment() != 0) { + entry_stack &= -base::OS::ActivationFrameAlignment(); + } + // Store remaining arguments on stack, from low to high memory. + intptr_t* stack_argument = reinterpret_cast(entry_stack); + memcpy(stack_argument + kCArgSlotCount, arguments + reg_arg_count, + stack_args_count * sizeof(*arguments)); + set_register(sp, entry_stack); + + CallInternal(entry); + + // Pop stack passed arguments. + CHECK_EQ(entry_stack, get_register(sp)); + set_register(sp, original_stack); + + // return get_register(a0); + // RISCV uses a0 to return result + return get_register(a0); +} + +double Simulator::CallFP(Address entry, double d0, double d1) { + set_fpu_register_double(fa0, d0); + set_fpu_register_double(fa1, d1); + CallInternal(entry); + return get_fpu_register_double(fa0); +} + +uintptr_t Simulator::PushAddress(uintptr_t address) { + int64_t new_sp = get_register(sp) - sizeof(uintptr_t); + uintptr_t* stack_slot = reinterpret_cast(new_sp); + *stack_slot = address; + set_register(sp, new_sp); + return new_sp; +} + +uintptr_t Simulator::PopAddress() { + int64_t current_sp = get_register(sp); + uintptr_t* stack_slot = reinterpret_cast(current_sp); + uintptr_t address = *stack_slot; + set_register(sp, current_sp + sizeof(uintptr_t)); + return address; +} + +Simulator::LocalMonitor::LocalMonitor() + : access_state_(MonitorAccess::Open), + tagged_addr_(0), + size_(TransactionSize::None) {} + +void Simulator::LocalMonitor::Clear() { + access_state_ = MonitorAccess::Open; + tagged_addr_ = 0; + size_ = TransactionSize::None; +} + +void Simulator::LocalMonitor::NotifyLoad() { + if (access_state_ == MonitorAccess::RMW) { + // A non linked load could clear the local monitor. As a result, it's + // most strict to unconditionally clear the local monitor on load. + Clear(); + } +} + +void Simulator::LocalMonitor::NotifyLoadLinked(uintptr_t addr, + TransactionSize size) { + access_state_ = MonitorAccess::RMW; + tagged_addr_ = addr; + size_ = size; +} + +void Simulator::LocalMonitor::NotifyStore() { + if (access_state_ == MonitorAccess::RMW) { + // A non exclusive store could clear the local monitor. As a result, it's + // most strict to unconditionally clear the local monitor on store. + Clear(); + } +} + +bool Simulator::LocalMonitor::NotifyStoreConditional(uintptr_t addr, + TransactionSize size) { + if (access_state_ == MonitorAccess::RMW) { + if (addr == tagged_addr_ && size_ == size) { + Clear(); + return true; + } else { + return false; + } + } else { + DCHECK(access_state_ == MonitorAccess::Open); + return false; + } +} + +Simulator::GlobalMonitor::LinkedAddress::LinkedAddress() + : access_state_(MonitorAccess::Open), + tagged_addr_(0), + next_(nullptr), + prev_(nullptr), + failure_counter_(0) {} + +void Simulator::GlobalMonitor::LinkedAddress::Clear_Locked() { + access_state_ = MonitorAccess::Open; + tagged_addr_ = 0; +} + +void Simulator::GlobalMonitor::LinkedAddress::NotifyLoadLinked_Locked( + uintptr_t addr) { + access_state_ = MonitorAccess::RMW; + tagged_addr_ = addr; +} + +void Simulator::GlobalMonitor::LinkedAddress::NotifyStore_Locked() { + if (access_state_ == MonitorAccess::RMW) { + // A non exclusive store could clear the global monitor. As a result, it's + // most strict to unconditionally clear global monitors on store. + Clear_Locked(); + } +} + +bool Simulator::GlobalMonitor::LinkedAddress::NotifyStoreConditional_Locked( + uintptr_t addr, bool is_requesting_thread) { + if (access_state_ == MonitorAccess::RMW) { + if (is_requesting_thread) { + if (addr == tagged_addr_) { + Clear_Locked(); + // Introduce occasional sc/scd failures. This is to simulate the + // behavior of hardware, which can randomly fail due to background + // cache evictions. + if (failure_counter_++ >= kMaxFailureCounter) { + failure_counter_ = 0; + return false; + } else { + return true; + } + } + } else if ((addr & kExclusiveTaggedAddrMask) == + (tagged_addr_ & kExclusiveTaggedAddrMask)) { + // Check the masked addresses when responding to a successful lock by + // another thread so the implementation is more conservative (i.e. the + // granularity of locking is as large as possible.) + Clear_Locked(); + return false; + } + } + return false; +} + +void Simulator::GlobalMonitor::NotifyLoadLinked_Locked( + uintptr_t addr, LinkedAddress* linked_address) { + linked_address->NotifyLoadLinked_Locked(addr); + PrependProcessor_Locked(linked_address); +} + +void Simulator::GlobalMonitor::NotifyStore_Locked( + LinkedAddress* linked_address) { + // Notify each thread of the store operation. + for (LinkedAddress* iter = head_; iter; iter = iter->next_) { + iter->NotifyStore_Locked(); + } +} + +bool Simulator::GlobalMonitor::NotifyStoreConditional_Locked( + uintptr_t addr, LinkedAddress* linked_address) { + DCHECK(IsProcessorInLinkedList_Locked(linked_address)); + if (linked_address->NotifyStoreConditional_Locked(addr, true)) { + // Notify the other processors that this StoreConditional succeeded. + for (LinkedAddress* iter = head_; iter; iter = iter->next_) { + if (iter != linked_address) { + iter->NotifyStoreConditional_Locked(addr, false); + } + } + return true; + } else { + return false; + } +} + +bool Simulator::GlobalMonitor::IsProcessorInLinkedList_Locked( + LinkedAddress* linked_address) const { + return head_ == linked_address || linked_address->next_ || + linked_address->prev_; +} + +void Simulator::GlobalMonitor::PrependProcessor_Locked( + LinkedAddress* linked_address) { + if (IsProcessorInLinkedList_Locked(linked_address)) { + return; + } + + if (head_) { + head_->prev_ = linked_address; + } + linked_address->prev_ = nullptr; + linked_address->next_ = head_; + head_ = linked_address; +} + +void Simulator::GlobalMonitor::RemoveLinkedAddress( + LinkedAddress* linked_address) { + base::MutexGuard lock_guard(&mutex); + if (!IsProcessorInLinkedList_Locked(linked_address)) { + return; + } + + if (linked_address->prev_) { + linked_address->prev_->next_ = linked_address->next_; + } else { + head_ = linked_address->next_; + } + if (linked_address->next_) { + linked_address->next_->prev_ = linked_address->prev_; + } + linked_address->prev_ = nullptr; + linked_address->next_ = nullptr; +} + +#undef SScanF + +} // namespace internal +} // namespace v8 + +#endif // USE_SIMULATOR Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/execution/riscv64/simulator-riscv64.h =================================================================== --- /dev/null +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/execution/riscv64/simulator-riscv64.h @@ -0,0 +1,764 @@ +// 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. + +// Copyright(c) 2010 - 2017, +// The Regents of the University of California(Regents).All Rights Reserved. +// +// Redistribution and use in source and binary forms, +// with or without modification, +// are permitted provided that the following +// conditions are met : 1. Redistributions of source code must retain the +// above copyright notice, this list of conditions and the following +// disclaimer.2. Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer in +// the +// documentation and / +// or +// other materials provided with the distribution.3. Neither the name of +// the Regents nor the names of its contributors may be used to endorse +// or +// promote products derived from +// this software without specific prior written permission. +// +// IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, +// INDIRECT, SPECIAL, +// INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, +// ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, +// EVEN IF REGENTS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +// PARTICULAR PURPOSE.THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, +// IF ANY, +// PROVIDED HEREUNDER IS PROVIDED +// "AS IS".REGENTS HAS NO OBLIGATION TO PROVIDE MAINTENANCE, +// SUPPORT, UPDATES, ENHANCEMENTS, +// OR MODIFICATIONS. + +// The original source code covered by the above license above has been +// modified significantly by the v8 project authors. + +// Declares a Simulator for RISC-V instructions if we are not generating a +// native RISC-V binary. This Simulator allows us to run and debug RISC-V 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 RISC-V HW platform. + +#ifndef V8_EXECUTION_RISCV_SIMULATOR_RISCV_H_ +#define V8_EXECUTION_RISCV_SIMULATOR_RISCV_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) +// Running with a simulator. + +#include "src/base/hashmap.h" +#include "src/codegen/assembler.h" +#include "src/codegen/riscv64/constants-riscv64.h" +#include "src/execution/simulator-base.h" +#include "src/utils/allocation.h" + +namespace v8 { +namespace internal { + +// ----------------------------------------------------------------------------- +// Utility types and functions for RISCV +// TODO: Add Spike License here +#ifdef V8_TARGET_ARCH_32_BIT +using sreg_t = int32_t; +using reg_t = uint32_t; +#define xlen 32 +#elif V8_TARGET_ARCH_64_BIT +using sreg_t = int64_t; +using reg_t = uint64_t; +#define xlen 64 +#else +#error "Cannot detect Riscv's bitwidth" +#endif + +#define sext32(x) ((sreg_t)(int32_t)(x)) +#define zext32(x) ((reg_t)(uint32_t)(x)) +#define sext_xlen(x) (((sreg_t)(x) << (64 - xlen)) >> (64 - xlen)) +#define zext_xlen(x) (((reg_t)(x) << (64 - xlen)) >> (64 - xlen)) + +#define BIT(n) (0x1LL << n) +#define QUIET_BIT_S(nan) (bit_cast(nan) & BIT(22)) +#define QUIET_BIT_D(nan) (bit_cast(nan) & BIT(51)) +static inline bool isSnan(float fp) { return !QUIET_BIT_S(fp); } +static inline bool isSnan(double fp) { return !QUIET_BIT_D(fp); } +#undef QUIET_BIT_S +#undef QUIET_BIT_D + +inline uint64_t mulhu(uint64_t a, uint64_t b) { + __uint128_t full_result = ((__uint128_t)a) * ((__uint128_t)b); + return full_result >> 64; +} + +inline int64_t mulh(int64_t a, int64_t b) { + __int128_t full_result = ((__int128_t)a) * ((__int128_t)b); + return full_result >> 64; +} + +inline int64_t mulhsu(int64_t a, uint64_t b) { + __int128_t full_result = ((__int128_t)a) * ((__uint128_t)b); + return full_result >> 64; +} + +// Floating point helpers +#define F32_SIGN ((uint32_t)1 << 31) +union u32_f32 { + uint32_t u; + float f; +}; +inline float fsgnj32(float rs1, float rs2, bool n, bool x) { + u32_f32 a = {.f = rs1}, b = {.f = rs2}; + u32_f32 res; + res.u = + (a.u & ~F32_SIGN) | ((((x) ? a.u : (n) ? F32_SIGN : 0) ^ b.u) & F32_SIGN); + return res.f; +} +#define F64_SIGN ((uint64_t)1 << 63) +union u64_f64 { + uint64_t u; + double d; +}; +inline double fsgnj64(double rs1, double rs2, bool n, bool x) { + u64_f64 a = {.d = rs1}, b = {.d = rs2}; + u64_f64 res; + res.u = + (a.u & ~F64_SIGN) | ((((x) ? a.u : (n) ? F64_SIGN : 0) ^ b.u) & F64_SIGN); + return res.d; +} + +inline bool is_boxed_float(int64_t v) { + return (uint32_t)((v >> 32) + 1) == 0; +} +inline int64_t box_float(float v) { + return (0xFFFFFFFF00000000 | bit_cast(v)); +} + +// ----------------------------------------------------------------------------- +// Utility functions + +class CachePage { + public: + static const int LINE_VALID = 0; + static const int LINE_INVALID = 1; + + static const int kPageShift = 12; + static const int kPageSize = 1 << kPageShift; + static const int kPageMask = kPageSize - 1; + static const int kLineShift = 2; // The cache line is only 4 bytes right now. + static const int kLineLength = 1 << kLineShift; + static const int kLineMask = kLineLength - 1; + + CachePage() { memset(&validity_map_, LINE_INVALID, sizeof(validity_map_)); } + + char* ValidityByte(int offset) { + return &validity_map_[offset >> kLineShift]; + } + + char* CachedData(int offset) { return &data_[offset]; } + + private: + char data_[kPageSize]; // The cached data. + static const int kValidityMapSize = kPageSize >> kLineShift; + char validity_map_[kValidityMapSize]; // One byte per line. +}; + +class SimInstructionBase : public InstructionBase { + public: + Type InstructionType() const { return type_; } + inline Instruction* instr() const { return instr_; } + inline int32_t operand() const { return operand_; } + + protected: + SimInstructionBase() : operand_(-1), instr_(nullptr), type_(kUnsupported) {} + explicit SimInstructionBase(Instruction* instr) {} + + int32_t operand_; + Instruction* instr_; + Type type_; + + private: + DISALLOW_ASSIGN(SimInstructionBase); +}; + +class SimInstruction : public InstructionGetters { + public: + SimInstruction() {} + + explicit SimInstruction(Instruction* instr) { *this = instr; } + + SimInstruction& operator=(Instruction* instr) { + operand_ = *reinterpret_cast(instr); + instr_ = instr; + type_ = InstructionBase::InstructionType(); + DCHECK(reinterpret_cast(&operand_) == this); + return *this; + } +}; + +class Simulator : public SimulatorBase { + public: + friend class RiscvDebugger; + + // Registers are declared in order. See SMRL chapter 2. + enum Register { + no_reg = -1, + zero_reg = 0, + ra, + sp, + gp, + tp, + t0, + t1, + t2, + s0, + s1, + a0, + a1, + a2, + a3, + a4, + a5, + a6, + a7, + s2, + s3, + s4, + s5, + s6, + s7, + s8, + s9, + s10, + s11, + t3, + t4, + t5, + t6, + pc, // pc must be the last register. + kNumSimuRegisters, + // aliases + fp = s0 + }; + + // Coprocessor registers. + // Generated code will always use doubles. So we will only use even registers. + enum FPURegister { + ft0, + ft1, + ft2, + ft3, + ft4, + ft5, + ft6, + ft7, + fs0, + fs1, + fa0, + fa1, + fa2, + fa3, + fa4, + fa5, + fa6, + fa7, + fs2, + fs3, + fs4, + fs5, + fs6, + fs7, + fs8, + fs9, + fs10, + fs11, + ft8, + ft9, + ft10, + ft11, + kNumFPURegisters + }; + + explicit Simulator(Isolate* isolate); + ~Simulator(); + + // The currently executing Simulator instance. Potentially there can be one + // for each native thread. + V8_EXPORT_PRIVATE static Simulator* current(v8::internal::Isolate* isolate); + + // Accessors for register state. Reading the pc value adheres to the RISC-V + // architecture specification and is off by a 8 from the currently executing + // instruction. + void set_register(int reg, int64_t value); + void set_register_word(int reg, int32_t value); + void set_dw_register(int dreg, const int* dbl); + int64_t get_register(int reg) const; + double get_double_from_register_pair(int reg); + + // Same for FPURegisters. + void set_fpu_register(int fpureg, int64_t value); + void set_fpu_register_word(int fpureg, int32_t value); + void set_fpu_register_hi_word(int fpureg, int32_t value); + void set_fpu_register_float(int fpureg, float value); + void set_fpu_register_double(int fpureg, double value); + + int64_t get_fpu_register(int fpureg) const; + int32_t get_fpu_register_word(int fpureg) const; + int32_t get_fpu_register_signed_word(int fpureg) const; + int32_t get_fpu_register_hi_word(int fpureg) const; + float get_fpu_register_float(int fpureg) const; + double get_fpu_register_double(int fpureg) const; + + // RV CSR manipulation + uint32_t read_csr_value(uint32_t csr); + void write_csr_value(uint32_t csr, uint64_t value); + void set_csr_bits(uint32_t csr, uint64_t flags); + void clear_csr_bits(uint32_t csr, uint64_t flags); + + void set_fflags(uint32_t flags) { set_csr_bits(csr_fflags, flags); } + void clear_fflags(int32_t flags) { clear_csr_bits(csr_fflags, flags); } + + inline uint32_t get_dynamic_rounding_mode(); + inline bool test_fflags_bits(uint32_t mask); + + float RoundF2FHelper(float input_val, int rmode); + double RoundF2FHelper(double input_val, int rmode); + template + I_TYPE RoundF2IHelper(F_TYPE original, int rmode); + + template + T FMaxMinHelper(T a, T b, MaxMinKind kind); + + template + bool CompareFHelper(T input1, T input2, FPUCondition cc); + + // Special case of set_register and get_register to access the raw PC value. + void set_pc(int64_t value); + int64_t get_pc() const; + + Address get_sp() const { return static_cast
(get_register(sp)); } + + // Accessor to the internal simulator stack area. + uintptr_t StackLimit(uintptr_t c_limit) const; + + // Executes RISC-V instructions until the PC reaches end_sim_pc. + void Execute(); + + template + Return Call(Address entry, Args... args) { + return VariadicCall(this, &Simulator::CallImpl, entry, args...); + } + + // Alternative: call a 2-argument double function. + double CallFP(Address entry, double d0, double d1); + + // Push an address onto the JS stack. + uintptr_t PushAddress(uintptr_t address); + + // Pop an address from the JS stack. + uintptr_t PopAddress(); + + // Debugger input. + void set_last_debugger_input(char* input); + char* last_debugger_input() { return last_debugger_input_; } + + // Redirection support. + static void SetRedirectInstruction(Instruction* instruction); + + // ICache checking. + static bool ICacheMatch(void* one, void* two); + static void FlushICache(base::CustomMatcherHashMap* i_cache, void* start, + size_t size); + + // Returns true if pc register contains one of the 'special_values' defined + // below (bad_ra, end_sim_pc). + bool has_bad_pc() const; + + private: + enum special_values { + // Known bad pc value to ensure that the simulator does not execute + // without being properly setup. + bad_ra = -1, + // A pc value used to signal the simulator to stop execution. Generally + // the ra is set to this value on transition from native C code to + // simulated execution, so that the simulator can "return" to the native + // C code. + end_sim_pc = -2, + // Unpredictable value. + Unpredictable = 0xbadbeaf + }; + + V8_EXPORT_PRIVATE intptr_t CallImpl(Address entry, int argument_count, + const intptr_t* arguments); + + // Unsupported instructions use Format to print an error and stop execution. + void Format(Instruction* instr, const char* format); + + // Helpers for data value tracing. + enum TraceType { + BYTE, + HALF, + WORD, + DWORD, + FLOAT, + DOUBLE, + // FLOAT_DOUBLE, + // WORD_DWORD + }; + + // RISCV Memory read/write methods + template + T ReadMem(int64_t addr, Instruction* instr); + template + void WriteMem(int64_t addr, T value, Instruction* instr); + template + T amo(int64_t addr, OP f, Instruction* instr, TraceType t) { + auto lhs = ReadMem(addr, instr); + // FIXME: trace memory read for AMO + WriteMem(addr, (T)f(lhs), instr); + return lhs; + } + + // Helper for debugging memory access. + inline void DieOrDebug(); + + void TraceRegWr(int64_t value, TraceType t = DWORD); + void TraceMemWr(int64_t addr, int64_t value, TraceType t); + template + void TraceMemRd(int64_t addr, T value, int64_t reg_value); + template + void TraceMemWr(int64_t addr, T value); + + SimInstruction instr_; + + // RISCV utlity API to access register value + inline int32_t rs1_reg() const { return instr_.Rs1Value(); } + inline int64_t rs1() const { return get_register(rs1_reg()); } + inline float frs1() const { return get_fpu_register_float(rs1_reg()); } + inline double drs1() const { return get_fpu_register_double(rs1_reg()); } + inline int32_t rs2_reg() const { return instr_.Rs2Value(); } + inline int64_t rs2() const { return get_register(rs2_reg()); } + inline float frs2() const { return get_fpu_register_float(rs2_reg()); } + inline double drs2() const { return get_fpu_register_double(rs2_reg()); } + inline int32_t rs3_reg() const { return instr_.Rs3Value(); } + inline int64_t rs3() const { return get_register(rs3_reg()); } + inline float frs3() const { return get_fpu_register_float(rs3_reg()); } + inline double drs3() const { return get_fpu_register_double(rs3_reg()); } + inline int32_t rd_reg() const { return instr_.RdValue(); } + inline int32_t frd_reg() const { return instr_.RdValue(); } + inline int16_t boffset() const { return instr_.BranchOffset(); } + inline int16_t imm12() const { return instr_.Imm12Value(); } + inline int32_t imm20J() const { return instr_.Imm20JValue(); } + inline int32_t imm5CSR() const { return instr_.Rs1Value(); } + inline int16_t csr_reg() const { return instr_.CsrValue(); } + + inline void set_rd(int64_t value, bool trace = true) { + set_register(rd_reg(), value); + if (trace) TraceRegWr(get_register(rd_reg()), DWORD); + } + inline void set_frd(float value, bool trace = true) { + set_fpu_register_float(rd_reg(), value); + if (trace) TraceRegWr(get_fpu_register_word(rd_reg()), FLOAT); + } + inline void set_drd(double value, bool trace = true) { + set_fpu_register_double(rd_reg(), value); + if (trace) TraceRegWr(get_fpu_register(rd_reg()), DOUBLE); + } + inline int16_t shamt() const { return (imm12() & 0x3F); } + inline int16_t shamt32() const { return (imm12() & 0x1F); } + inline int32_t s_imm12() const { return instr_.StoreOffset(); } + inline int32_t u_imm() const { return instr_.Imm20UValue() << 12; } + inline void require(bool check) { + if (!check) { + SignalException(kIllegalInstruction); + } + } + + template + inline T CanonicalizeFPUOp3(Func fn) { + DCHECK(std::is_floating_point::value); + T src1 = std::is_same::value ? frs1() : drs1(); + T src2 = std::is_same::value ? frs2() : drs2(); + T src3 = std::is_same::value ? frs3() : drs3(); + auto alu_out = fn(src1, src2, src3); + // if any input or result is NaN, the result is quiet_NaN + if (std::isnan(alu_out) || std::isnan(src1) || std::isnan(src2) || + std::isnan(src3)) { + // signaling_nan sets kInvalidOperation bit + if (isSnan(alu_out) || isSnan(src1) || isSnan(src2) || isSnan(src3)) + set_fflags(kInvalidOperation); + alu_out = std::numeric_limits::quiet_NaN(); + } + return alu_out; + } + + template + inline T CanonicalizeFPUOp2(Func fn) { + DCHECK(std::is_floating_point::value); + T src1 = std::is_same::value ? frs1() : drs1(); + T src2 = std::is_same::value ? frs2() : drs2(); + auto alu_out = fn(src1, src2); + // if any input or result is NaN, the result is quiet_NaN + if (std::isnan(alu_out) || std::isnan(src1) || std::isnan(src2)) { + // signaling_nan sets kInvalidOperation bit + if (isSnan(alu_out) || isSnan(src1) || isSnan(src2)) + set_fflags(kInvalidOperation); + alu_out = std::numeric_limits::quiet_NaN(); + } + return alu_out; + } + + template + inline T CanonicalizeFPUOp1(Func fn) { + DCHECK(std::is_floating_point::value); + T src1 = std::is_same::value ? frs1() : drs1(); + auto alu_out = fn(src1); + // if any input or result is NaN, the result is quiet_NaN + if (std::isnan(alu_out) || std::isnan(src1)) { + // signaling_nan sets kInvalidOperation bit + if (isSnan(alu_out) || isSnan(src1)) set_fflags(kInvalidOperation); + alu_out = std::numeric_limits::quiet_NaN(); + } + return alu_out; + } + + template + inline float CanonicalizeDoubleToFloatOperation(Func fn) { + float alu_out = fn(drs1()); + if (std::isnan(alu_out) || std::isnan(drs1())) + alu_out = std::numeric_limits::quiet_NaN(); + return alu_out; + } + + template + inline float CanonicalizeFloatToDoubleOperation(Func fn) { + double alu_out = fn(frs1()); + if (std::isnan(alu_out) || std::isnan(frs1())) + alu_out = std::numeric_limits::quiet_NaN(); + return alu_out; + } + + // RISCV decoding routine + void DecodeRVRType(); + void DecodeRVR4Type(); + void DecodeRVRFPType(); // Special routine for R/OP_FP type + void DecodeRVRAType(); // Special routine for R/AMO type + void DecodeRVIType(); + void DecodeRVSType(); + void DecodeRVBType(); + void DecodeRVUType(); + void DecodeRVJType(); + + // Used for breakpoints and traps. + void SoftwareInterrupt(); + + // Debug helpers + + // Simulator breakpoints. + struct Breakpoint { + Instruction* location; + bool enabled; + bool is_tbreak; + }; + std::vector breakpoints_; + void SetBreakpoint(Instruction* breakpoint, bool is_tbreak); + void ListBreakpoints(); + void CheckBreakpoints(); + + // Stop helper functions. + bool IsWatchpoint(uint64_t code); + void PrintWatchpoint(uint64_t code); + void HandleStop(uint64_t code); + bool IsStopInstruction(Instruction* instr); + bool IsEnabledStop(uint64_t code); + void EnableStop(uint64_t code); + void DisableStop(uint64_t code); + void IncreaseStopCounter(uint64_t code); + void PrintStopInfo(uint64_t code); + + // Executes one instruction. + void InstructionDecode(Instruction* instr); + + // ICache. + static void CheckICache(base::CustomMatcherHashMap* i_cache, + Instruction* instr); + static void FlushOnePage(base::CustomMatcherHashMap* i_cache, intptr_t start, + size_t size); + static CachePage* GetCachePage(base::CustomMatcherHashMap* i_cache, + void* page); + + enum Exception { + none, + kIntegerOverflow, + kIntegerUnderflow, + kDivideByZero, + kNumExceptions, + // RISCV illegual instruction exception + kIllegalInstruction, + }; + + // Exceptions. + void SignalException(Exception e); + + // Handle arguments and return value for runtime FP functions. + void GetFpArgs(double* x, double* y, int32_t* z); + void SetFpResult(const double& result); + + void CallInternal(Address entry); + + // Architecture state. + // Registers. + int64_t registers_[kNumSimuRegisters]; + // Coprocessor Registers. + int64_t FPUregisters_[kNumFPURegisters]; + // Floating-point control and status register. + uint32_t FCSR_; + + // Simulator support. + // Allocate 1MB for stack. + size_t stack_size_; + char* stack_; + bool pc_modified_; + int64_t icount_; + int break_count_; + EmbeddedVector trace_buf_; + + // Debugger input. + char* last_debugger_input_; + + v8::internal::Isolate* isolate_; + + // Stop is disabled if bit 31 is set. + static const uint32_t kStopDisabledBit = 1 << 31; + + // A stop is enabled, meaning the simulator will stop when meeting the + // instruction, if bit 31 of watched_stops_[code].count is unset. + // The value watched_stops_[code].count & ~(1 << 31) indicates how many times + // the breakpoint was hit or gone through. + struct StopCountAndDesc { + uint32_t count; + char* desc; + }; + StopCountAndDesc watched_stops_[kMaxStopCode + 1]; + + // Synchronization primitives. + enum class MonitorAccess { + Open, + RMW, + }; + + enum class TransactionSize { + None = 0, + Word = 4, + DoubleWord = 8, + }; + + // The least-significant bits of the address are ignored. The number of bits + // is implementation-defined, between 3 and minimum page size. + static const uintptr_t kExclusiveTaggedAddrMask = ~((1 << 3) - 1); + + class LocalMonitor { + public: + LocalMonitor(); + + // These functions manage the state machine for the local monitor, but do + // not actually perform loads and stores. NotifyStoreConditional only + // returns true if the store conditional is allowed; the global monitor will + // still have to be checked to see whether the memory should be updated. + void NotifyLoad(); + void NotifyLoadLinked(uintptr_t addr, TransactionSize size); + void NotifyStore(); + bool NotifyStoreConditional(uintptr_t addr, TransactionSize size); + + private: + void Clear(); + + MonitorAccess access_state_; + uintptr_t tagged_addr_; + TransactionSize size_; + }; + + class GlobalMonitor { + public: + class LinkedAddress { + public: + LinkedAddress(); + + private: + friend class GlobalMonitor; + // These functions manage the state machine for the global monitor, but do + // not actually perform loads and stores. + void Clear_Locked(); + void NotifyLoadLinked_Locked(uintptr_t addr); + void NotifyStore_Locked(); + bool NotifyStoreConditional_Locked(uintptr_t addr, + bool is_requesting_thread); + + MonitorAccess access_state_; + uintptr_t tagged_addr_; + LinkedAddress* next_; + LinkedAddress* prev_; + // A scd can fail due to background cache evictions. Rather than + // simulating this, we'll just occasionally introduce cases where an + // store conditional fails. This will happen once after every + // kMaxFailureCounter exclusive stores. + static const int kMaxFailureCounter = 5; + int failure_counter_; + }; + + // Exposed so it can be accessed by Simulator::{Read,Write}Ex*. + base::Mutex mutex; + + void NotifyLoadLinked_Locked(uintptr_t addr, LinkedAddress* linked_address); + void NotifyStore_Locked(LinkedAddress* linked_address); + bool NotifyStoreConditional_Locked(uintptr_t addr, + LinkedAddress* linked_address); + + // Called when the simulator is destroyed. + void RemoveLinkedAddress(LinkedAddress* linked_address); + + static GlobalMonitor* Get(); + + private: + // Private constructor. Call {GlobalMonitor::Get()} to get the singleton. + GlobalMonitor() = default; + friend class base::LeakyObject; + + bool IsProcessorInLinkedList_Locked(LinkedAddress* linked_address) const; + void PrependProcessor_Locked(LinkedAddress* linked_address); + + LinkedAddress* head_ = nullptr; + }; + + LocalMonitor local_monitor_; + GlobalMonitor::LinkedAddress global_monitor_thread_; +}; + +} // namespace internal +} // namespace v8 + +#endif // defined(USE_SIMULATOR) +#endif // V8_EXECUTION_RISCV_SIMULATOR_RISCV_H_ Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/execution/simulator-base.h =================================================================== --- qtwebengine-everywhere-src-5.15.3.orig/src/3rdparty/chromium/v8/src/execution/simulator-base.h +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/execution/simulator-base.h @@ -89,9 +89,10 @@ 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 - // The MIPS64 calling convention is to sign extend all values, even unsigned - // ones. +#if V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_RISCV64 + // The MIPS64 and RISCV64 calling convention is to sign extend all values, + // even unsigned ones. + // FIXME (RISCV): what about RISCV calling contention? using signed_t = typename std::make_signed::type; return static_cast(static_cast(arg)); #else @@ -125,6 +126,7 @@ class SimulatorBase { // - V8_TARGET_ARCH_PPC: svc (Supervisor Call) // - V8_TARGET_ARCH_PPC64: svc (Supervisor Call) // - V8_TARGET_ARCH_S390: svc (Supervisor Call) +// - V8_TARGET_ARCH_RISCV64: ecall (Supervisor Call) class Redirection { public: Redirection(Address external_function, ExternalReference::Type type); Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/execution/simulator.h =================================================================== --- qtwebengine-everywhere-src-5.15.3.orig/src/3rdparty/chromium/v8/src/execution/simulator.h +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/execution/simulator.h @@ -26,6 +26,10 @@ #include "src/execution/mips64/simulator-mips64.h" #elif V8_TARGET_ARCH_S390 #include "src/execution/s390/simulator-s390.h" +#elif V8_TARGET_ARCH_RISCV64 +#include "src/execution/riscv64/simulator-riscv64.h" +#elif V8_TARGET_ARCH_RISCV +#include "src/execution/riscv/simulator-riscv.h" #else #error Unsupported target architecture. #endif Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/flags/flag-definitions.h =================================================================== --- qtwebengine-everywhere-src-5.15.3.orig/src/3rdparty/chromium/v8/src/flags/flag-definitions.h +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/flags/flag-definitions.h @@ -1182,6 +1182,7 @@ DEFINE_BOOL(partial_constant_pool, true, DEFINE_STRING(sim_arm64_optional_features, "none", "enable optional features on the simulator for testing: none or " "all") +DEFINE_BOOL(debug_riscv, false, "enable debug prints") // Controlling source positions for Torque/CSA code. DEFINE_BOOL(enable_source_at_csa_bind, false, @@ -1372,7 +1373,7 @@ DEFINE_BOOL(check_icache, false, "Check icache flushes in ARM and MIPS simulator") 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_PPC64) || defined(V8_TARGET_ARCH_RISCV64) 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.") Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/heap/base/asm/riscv64/push_registers_asm.cc =================================================================== --- /dev/null +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/heap/base/asm/riscv64/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( + ".global PushAllRegistersAndIterateStack \n" + ".type PushAllRegistersAndIterateStack, %function \n" + ".hidden PushAllRegistersAndIterateStack \n" + "PushAllRegistersAndIterateStack: \n" + // Push all callee-saved registers and save return address. + " addi sp, sp, -96 \n" + " sd ra, 88(sp) \n" + " sd s8, 80(sp) \n" + " sd sp, 72(sp) \n" + " sd gp, 64(sp) \n" + " sd s7, 56(sp) \n" + " sd s6, 48(sp) \n" + " sd s5, 40(sp) \n" + " sd s4, 32(sp) \n" + " sd s3, 24(sp) \n" + " sd s2, 16(sp) \n" + " sd s1, 8(sp) \n" + " sd s0, 0(sp) \n" + // Maintain frame pointer. + " mv s8, sp \n" + // Pass 1st parameter (a0) unchanged (Stack*). + // Pass 2nd parameter (a1) unchanged (StackVisitor*). + // Save 3rd parameter (a2; IterateStackCallback). + " mv a3, a2 \n" + " mv a2, sp \n" + // Call the callback. + " jalr a3 \n" + // Load return address. + " ld ra, 88(sp) \n" + // Restore frame pointer. + " ld s8, 80(sp) \n" + " addi sp, sp, 96 \n" + " jr ra \n"); + Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/interpreter/interpreter-assembler.cc =================================================================== --- qtwebengine-everywhere-src-5.15.3.orig/src/3rdparty/chromium/v8/src/interpreter/interpreter-assembler.cc +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/interpreter/interpreter-assembler.cc @@ -1348,7 +1348,8 @@ void InterpreterAssembler::TraceBytecode // static bool InterpreterAssembler::TargetSupportsUnalignedAccess() { -#if V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 +#if V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_RISCV64 || \ + V8_TARGET_ARCH_RISCV 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 || \ Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/libsampler/sampler.cc =================================================================== --- qtwebengine-everywhere-src-5.15.3.orig/src/3rdparty/chromium/v8/src/libsampler/sampler.cc +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/libsampler/sampler.cc @@ -442,6 +442,12 @@ void SignalHandler::FillRegisterState(vo state->sp = reinterpret_cast(ucontext->uc_mcontext.gregs[15]); state->fp = reinterpret_cast(ucontext->uc_mcontext.gregs[11]); state->lr = reinterpret_cast(ucontext->uc_mcontext.gregs[14]); +#elif V8_HOST_ARCH_RISCV64 || V8_HOST_ARCH_RISCV + // Spec CH.25 RISC-V Assembly Programmer’s Handbook + state->pc = reinterpret_cast(mcontext.__gregs[REG_PC]); + state->sp = reinterpret_cast(mcontext.__gregs[REG_SP]); + state->fp = reinterpret_cast(mcontext.__gregs[REG_S0]); + state->lr = reinterpret_cast(mcontext.__gregs[REG_RA]); #endif // V8_HOST_ARCH_* #elif V8_OS_IOS Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/logging/log.cc =================================================================== --- qtwebengine-everywhere-src-5.15.3.orig/src/3rdparty/chromium/v8/src/logging/log.cc +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/logging/log.cc @@ -594,6 +594,14 @@ void LowLevelLogger::LogCodeInfo() { const char arch[] = "arm64"; #elif V8_TARGET_ARCH_S390 const char arch[] = "s390"; +#elif V8_TARGET_ARCH_RISCV64 + // FIXME (RISCV) porting: need more specific arch strings based on cpu + // features + const char arch[] = "riscv64"; +#elif V8_TARGET_ARCH_RISCV + // FIXME (RISCV) porting: need more specific arch strings based on cpu + // features + const char arch[] = "riscv"; #else const char arch[] = "unknown"; #endif Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/objects/backing-store.cc =================================================================== --- qtwebengine-everywhere-src-5.15.3.orig/src/3rdparty/chromium/v8/src/objects/backing-store.cc +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/objects/backing-store.cc @@ -29,7 +29,8 @@ constexpr bool kUseGuardRegions = true; constexpr bool kUseGuardRegions = false; #endif -#if V8_TARGET_ARCH_MIPS64 +#if V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_RISCV64 +// FIXME(RISCV): Check this value // MIPS64 has a user space of 2^40 bytes on most processors, // address space limits needs to be smaller. constexpr size_t kAddressSpaceLimit = 0x8000000000L; // 512 GiB @@ -39,6 +40,10 @@ constexpr size_t kAddressSpaceLimit = 0x constexpr size_t kAddressSpaceLimit = 0xC0000000; // 3 GiB #endif +#if V8_TARGET_ARCH_RISCV +#erro RISCV(32) architecture not supported +#endif + constexpr uint64_t kOneGiB = 1024 * 1024 * 1024; constexpr uint64_t kNegativeGuardSize = 2 * kOneGiB; Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/objects/code.cc =================================================================== --- qtwebengine-everywhere-src-5.15.3.orig/src/3rdparty/chromium/v8/src/objects/code.cc +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/objects/code.cc @@ -234,7 +234,7 @@ bool Code::IsIsolateIndependent(Isolate* RelocInfo::ModeMask(RelocInfo::WASM_STUB_CALL))); #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_RISCV64) return RelocIterator(*this, kModeMask).done(); #elif defined(V8_TARGET_ARCH_X64) || defined(V8_TARGET_ARCH_ARM64) || \ defined(V8_TARGET_ARCH_ARM) || defined(V8_TARGET_ARCH_MIPS) || \ Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/objects/code.h =================================================================== --- qtwebengine-everywhere-src-5.15.3.orig/src/3rdparty/chromium/v8/src/objects/code.h +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/objects/code.h @@ -461,6 +461,10 @@ class Code : public HeapObject { : (COMPRESS_POINTERS_BOOL ? 16 : 28); #elif V8_TARGET_ARCH_S390X static constexpr int kHeaderPaddingSize = COMPRESS_POINTERS_BOOL ? 16 : 28; +#elif V8_TARGET_ARCH_RISCV64 + static constexpr int kHeaderPaddingSize = 28; +#elif V8_TARGET_ARCH_RISCV +#error riscv32 unsupported yet #else #error Unknown architecture. #endif Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/profiler/tick-sample.cc =================================================================== --- qtwebengine-everywhere-src-5.15.3.orig/src/3rdparty/chromium/v8/src/profiler/tick-sample.cc +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/profiler/tick-sample.cc @@ -125,6 +125,13 @@ bool SimulatorHelper::FillRegisters(Isol state->sp = reinterpret_cast(simulator->get_register(Simulator::sp)); state->fp = reinterpret_cast(simulator->get_register(Simulator::fp)); state->lr = reinterpret_cast(simulator->get_register(Simulator::ra)); +#elif V8_TARGET_ARCH_RISCV64 || V8_TARGET_ARCH_RISCV + if (!simulator->has_bad_pc()) { + state->pc = reinterpret_cast(simulator->get_pc()); + } + state->sp = reinterpret_cast(simulator->get_register(Simulator::sp)); + state->fp = reinterpret_cast(simulator->get_register(Simulator::fp)); + state->lr = reinterpret_cast(simulator->get_register(Simulator::ra)); #endif if (state->sp == 0 || state->fp == 0) { // It possible that the simulator is interrupted while it is updating Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/regexp/regexp-macro-assembler-arch.h =================================================================== --- qtwebengine-everywhere-src-5.15.3.orig/src/3rdparty/chromium/v8/src/regexp/regexp-macro-assembler-arch.h +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/regexp/regexp-macro-assembler-arch.h @@ -23,6 +23,10 @@ #include "src/regexp/mips64/regexp-macro-assembler-mips64.h" #elif V8_TARGET_ARCH_S390 #include "src/regexp/s390/regexp-macro-assembler-s390.h" +#elif V8_TARGET_ARCH_RISCV64 +#include "src/regexp/riscv64/regexp-macro-assembler-riscv64.h" +#elif V8_TARGET_ARCH_RISCV +#include "src/regexp/riscv/regexp-macro-assembler-riscv.h" #else #error Unsupported target architecture. #endif Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/regexp/regexp-macro-assembler.h =================================================================== --- qtwebengine-everywhere-src-5.15.3.orig/src/3rdparty/chromium/v8/src/regexp/regexp-macro-assembler.h +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/regexp/regexp-macro-assembler.h @@ -44,6 +44,7 @@ class RegExpMacroAssembler { kARMImplementation, kARM64Implementation, kMIPSImplementation, + kRISCVImplementation, kS390Implementation, kPPCImplementation, kX64Implementation, Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/regexp/regexp.cc =================================================================== --- qtwebengine-everywhere-src-5.15.3.orig/src/3rdparty/chromium/v8/src/regexp/regexp.cc +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/regexp/regexp.cc @@ -815,6 +815,9 @@ bool RegExpImpl::Compile(Isolate* isolat #elif V8_TARGET_ARCH_MIPS64 macro_assembler.reset(new RegExpMacroAssemblerMIPS(isolate, zone, mode, output_register_count)); +#elif V8_TARGET_ARCH_RISCV64 || V8_TARGET_ARCH_RISCV + macro_assembler.reset(new RegExpMacroAssemblerRISCV(isolate, zone, mode, + output_register_count)); #else #error "Unsupported architecture" #endif Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/regexp/riscv64/regexp-macro-assembler-riscv64.cc =================================================================== --- /dev/null +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/regexp/riscv64/regexp-macro-assembler-riscv64.cc @@ -0,0 +1,1260 @@ +// 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_RISCV64 + +#include "src/regexp/riscv64/regexp-macro-assembler-riscv64.h" + +#include "src/codegen/assembler-inl.h" +#include "src/codegen/macro-assembler.h" +#include "src/logging/log.h" +#include "src/objects/objects-inl.h" +#include "src/regexp/regexp-macro-assembler.h" +#include "src/regexp/regexp-stack.h" +#include "src/snapshot/embedded/embedded-data.h" +#include "src/strings/unicode.h" + +namespace v8 { +namespace internal { + +/* clang-format off + * + * This assembler uses the following register assignment convention + * - t4 : Temporarily stores the index of capture start after a matching pass + * for a global regexp. + * - a5 : Pointer to current Code object including heap object tag. + * - a6 : Current position in input, as negative offset from end of string. + * Please notice that this is the byte offset, not the character offset! + * - a7 : Currently loaded character. Must be loaded using + * LoadCurrentCharacter before using any of the dispatch methods. + * - t0 : Points to tip of backtrack stack + * - t1 : Unused. + * - t2 : 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. + * + * The stack will have the following structure: + * FIXME(RISCV): Verify that this stack description is correct + * + * - 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] fp..s7 Callee-saved registers fp..s7. + * --- frame pointer ---- + * - fp[-8] direct_call (1 = direct call from JS, 0 = from runtime) kDirectCall + * - fp[-16] stack_base (Top of backtracking stack). kStackHighEnd + * - fp[-24] capture array size (may fit multiple sets of matches) kNumOutputRegisters + * - fp[-32] int* capture_array (int[num_saved_registers_], for output). kRegisterOutput + * - fp[-40] end of input (address of end of string). kInputEnd + * - fp[-48] start of input (address of first character in string). kInputStart + * - fp[-56] start index (character index of start). kStartIndex + * - fp[-64] void* input_string (location of a handle containing the string). kInputString + * - fp[-72] success counter (only for global regexps to count matches). kSuccessfulCaptures + * - fp[-80] 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[-88] 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, + * byte* stack_area_base, + * 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 RegExpMacroAssemblerRISCV::kRegExpCodeSize; + +RegExpMacroAssemblerRISCV::RegExpMacroAssemblerRISCV(Isolate* isolate, + Zone* zone, Mode mode, + int registers_to_save) + : NativeRegExpMacroAssembler(isolate, zone), + masm_(new MacroAssembler(isolate, CodeObjectRequired::kYes, + NewAssemblerBuffer(kRegExpCodeSize))), + 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_() { + masm_->set_root_array_available(false); + + 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(a0, Operand(FAILURE)); + __ Ret(); + __ bind(&start_label_); // And then continue from here. +} + +RegExpMacroAssemblerRISCV::~RegExpMacroAssemblerRISCV() { + delete masm_; + // Unuse labels in case we throw away the assembler without calling GetCode. + entry_label_.Unuse(); + start_label_.Unuse(); + success_label_.Unuse(); + backtrack_label_.Unuse(); + exit_label_.Unuse(); + check_preempt_label_.Unuse(); + stack_overflow_label_.Unuse(); + internal_failure_label_.Unuse(); +} + +int RegExpMacroAssemblerRISCV::stack_limit_slack() { + return RegExpStack::kStackLimitSlack; +} + +void RegExpMacroAssemblerRISCV::AdvanceCurrentPosition(int by) { + if (by != 0) { + __ Add64(current_input_offset(), current_input_offset(), + Operand(by * char_size())); + } +} + +void RegExpMacroAssemblerRISCV::AdvanceRegister(int reg, int by) { + DCHECK_LE(0, reg); + DCHECK_GT(num_registers_, reg); + if (by != 0) { + __ Ld(a0, register_location(reg)); + __ Add64(a0, a0, Operand(by)); + __ Sd(a0, register_location(reg)); + } +} + +void RegExpMacroAssemblerRISCV::Backtrack() { + CheckPreemption(); + if (has_backtrack_limit()) { + Label next; + __ Ld(a0, MemOperand(frame_pointer(), kBacktrackCount)); + __ Add64(a0, a0, Operand(1)); + __ Sd(a0, MemOperand(frame_pointer(), kBacktrackCount)); + __ Branch(&next, ne, a0, Operand(backtrack_limit())); + + // Exceeded limits are treated as a failed match. + Fail(); + + __ bind(&next); + } + // Pop Code offset from backtrack stack, add Code and jump to location. + Pop(a0); + __ Add64(a0, a0, code_pointer()); + __ Jump(a0); +} + +void RegExpMacroAssemblerRISCV::Bind(Label* label) { __ bind(label); } + +void RegExpMacroAssemblerRISCV::CheckCharacter(uint32_t c, Label* on_equal) { + BranchOrBacktrack(on_equal, eq, current_character(), Operand(c)); +} + +void RegExpMacroAssemblerRISCV::CheckCharacterGT(uc16 limit, + Label* on_greater) { + BranchOrBacktrack(on_greater, gt, current_character(), Operand(limit)); +} + +void RegExpMacroAssemblerRISCV::CheckAtStart(int cp_offset, + Label* on_at_start) { + __ Ld(a1, MemOperand(frame_pointer(), kStringStartMinusOne)); + __ Add64(a0, current_input_offset(), + Operand(-char_size() + cp_offset * char_size())); + BranchOrBacktrack(on_at_start, eq, a0, Operand(a1)); +} + +void RegExpMacroAssemblerRISCV::CheckNotAtStart(int cp_offset, + Label* on_not_at_start) { + __ Ld(a1, MemOperand(frame_pointer(), kStringStartMinusOne)); + __ Add64(a0, current_input_offset(), + Operand(-char_size() + cp_offset * char_size())); + BranchOrBacktrack(on_not_at_start, ne, a0, Operand(a1)); +} + +void RegExpMacroAssemblerRISCV::CheckCharacterLT(uc16 limit, Label* on_less) { + BranchOrBacktrack(on_less, lt, current_character(), Operand(limit)); +} + +void RegExpMacroAssemblerRISCV::CheckGreedyLoop(Label* on_equal) { + Label backtrack_non_equal; + __ Lw(a0, MemOperand(backtrack_stackpointer(), 0)); + __ Branch(&backtrack_non_equal, ne, current_input_offset(), Operand(a0)); + __ Add64(backtrack_stackpointer(), backtrack_stackpointer(), + Operand(kIntSize)); + __ bind(&backtrack_non_equal); + BranchOrBacktrack(on_equal, eq, current_input_offset(), Operand(a0)); +} + +void RegExpMacroAssemblerRISCV::CheckNotBackReferenceIgnoreCase( + int start_reg, bool read_backward, bool unicode, Label* on_no_match) { + Label fallthrough; + __ Ld(a0, register_location(start_reg)); // Index of start of capture. + __ Ld(a1, register_location(start_reg + 1)); // Index of end of capture. + __ Sub64(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) { + __ Ld(t1, MemOperand(frame_pointer(), kStringStartMinusOne)); + __ Add64(t1, t1, a1); + BranchOrBacktrack(on_no_match, le, current_input_offset(), Operand(t1)); + } else { + __ Add64(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. + __ Add64(a0, a0, Operand(end_of_input_address())); + __ Add64(a2, end_of_input_address(), Operand(current_input_offset())); + if (read_backward) { + __ Sub64(a2, a2, Operand(a1)); + } + __ Add64(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); + __ Lbu(a3, MemOperand(a0, 0)); + __ addi(a0, a0, char_size()); + __ Lbu(a4, MemOperand(a2, 0)); + __ addi(a2, a2, char_size()); + + __ 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)); + __ Sub64(a3, a3, Operand('a')); + __ Branch(&loop_check, Uless_equal, a3, Operand('z' - 'a')); + // Latin-1: Check for values in range [224,254] but not 247. + __ Sub64(a3, a3, Operand(224 - 'a')); + // Weren't Latin-1 letters. + __ Branch(&fail, Ugreater, 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. + __ Sub64(current_input_offset(), a2, end_of_input_address()); + if (read_backward) { + __ Ld(t1, register_location(start_reg)); // Index of start of capture. + __ Ld(a2, register_location(start_reg + 1)); // Index of end of capture. + __ Add64(current_input_offset(), current_input_offset(), Operand(t1)); + __ Sub64(current_input_offset(), current_input_offset(), Operand(a2)); + } + } else { + DCHECK(mode_ == UC16); + // Put regexp engine registers on stack. + RegList regexp_registers_to_retain = current_input_offset().bit() | + current_character().bit() | + backtrack_stackpointer().bit(); + __ MultiPush(regexp_registers_to_retain); + + 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. + __ Add64(a0, a0, Operand(end_of_input_address())); + // Length of capture. + __ mv(a2, a1); + // Save length in callee-save register for use on return. + __ mv(s3, a1); + // Address of current input position. + __ Add64(a1, current_input_offset(), Operand(end_of_input_address())); + if (read_backward) { + __ Sub64(a1, a1, Operand(s3)); + } + // Isolate. + __ li(a3, Operand(ExternalReference::isolate_address(masm_->isolate()))); + + { + AllowExternalCallThatCantCauseGC scope(masm_); + ExternalReference function = + unicode ? ExternalReference::re_case_insensitive_compare_unicode( + isolate()) + : ExternalReference::re_case_insensitive_compare_non_unicode( + isolate()); + __ CallCFunction(function, argument_count); + } + + // Restore regexp engine registers. + __ MultiPop(regexp_registers_to_retain); + __ li(code_pointer(), Operand(masm_->CodeObject()), CONSTANT_SIZE); + __ Ld(end_of_input_address(), MemOperand(frame_pointer(), kInputEnd)); + + // Check if function returned non-zero for success or zero for failure. + BranchOrBacktrack(on_no_match, eq, a0, Operand(zero_reg)); + // On success, increment position by length of capture. + if (read_backward) { + __ Sub64(current_input_offset(), current_input_offset(), Operand(s3)); + } else { + __ Add64(current_input_offset(), current_input_offset(), Operand(s3)); + } + } + + __ bind(&fallthrough); +} + +void RegExpMacroAssemblerRISCV::CheckNotBackReference(int start_reg, + bool read_backward, + Label* on_no_match) { + Label fallthrough; + + // Find length of back-referenced capture. + __ Ld(a0, register_location(start_reg)); + __ Ld(a1, register_location(start_reg + 1)); + __ Sub64(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) { + __ Ld(t1, MemOperand(frame_pointer(), kStringStartMinusOne)); + __ Add64(t1, t1, a1); + BranchOrBacktrack(on_no_match, le, current_input_offset(), Operand(t1)); + } else { + __ Add64(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. + __ Add64(a0, a0, Operand(end_of_input_address())); + __ Add64(a2, end_of_input_address(), Operand(current_input_offset())); + if (read_backward) { + __ Sub64(a2, a2, Operand(a1)); + } + __ Add64(a1, a1, Operand(a0)); + + Label loop; + __ bind(&loop); + if (mode_ == LATIN1) { + __ Lbu(a3, MemOperand(a0, 0)); + __ addi(a0, a0, char_size()); + __ Lbu(a4, MemOperand(a2, 0)); + __ addi(a2, a2, char_size()); + } else { + DCHECK(mode_ == UC16); + __ Lhu(a3, MemOperand(a0, 0)); + __ addi(a0, a0, char_size()); + __ Lhu(a4, MemOperand(a2, 0)); + __ addi(a2, a2, char_size()); + } + BranchOrBacktrack(on_no_match, ne, a3, Operand(a4)); + __ Branch(&loop, lt, a0, Operand(a1)); + + // Move current character position to position after match. + __ Sub64(current_input_offset(), a2, end_of_input_address()); + if (read_backward) { + __ Ld(t1, register_location(start_reg)); // Index of start of capture. + __ Ld(a2, register_location(start_reg + 1)); // Index of end of capture. + __ Add64(current_input_offset(), current_input_offset(), Operand(t1)); + __ Sub64(current_input_offset(), current_input_offset(), Operand(a2)); + } + __ bind(&fallthrough); +} + +void RegExpMacroAssemblerRISCV::CheckNotCharacter(uint32_t c, + Label* on_not_equal) { + BranchOrBacktrack(on_not_equal, ne, current_character(), Operand(c)); +} + +void RegExpMacroAssemblerRISCV::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 RegExpMacroAssemblerRISCV::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 RegExpMacroAssemblerRISCV::CheckNotCharacterAfterMinusAnd( + uc16 c, uc16 minus, uc16 mask, Label* on_not_equal) { + DCHECK_GT(String::kMaxUtf16CodeUnit, minus); + __ Sub64(a0, current_character(), Operand(minus)); + __ And(a0, a0, Operand(mask)); + BranchOrBacktrack(on_not_equal, ne, a0, Operand(c)); +} + +void RegExpMacroAssemblerRISCV::CheckCharacterInRange(uc16 from, uc16 to, + Label* on_in_range) { + __ Sub64(a0, current_character(), Operand(from)); + // Unsigned lower-or-same condition. + BranchOrBacktrack(on_in_range, Uless_equal, a0, Operand(to - from)); +} + +void RegExpMacroAssemblerRISCV::CheckCharacterNotInRange( + uc16 from, uc16 to, Label* on_not_in_range) { + __ Sub64(a0, current_character(), Operand(from)); + // Unsigned higher condition. + BranchOrBacktrack(on_not_in_range, Ugreater, a0, Operand(to - from)); +} + +void RegExpMacroAssemblerRISCV::CheckBitInTable(Handle table, + Label* on_bit_set) { + __ li(a0, Operand(table)); + if (mode_ != LATIN1 || kTableMask != String::kMaxOneByteCharCode) { + __ And(a1, current_character(), Operand(kTableSize - 1)); + __ Add64(a0, a0, a1); + } else { + __ Add64(a0, a0, current_character()); + } + + __ Lbu(a0, FieldMemOperand(a0, ByteArray::kHeaderSize)); + BranchOrBacktrack(on_bit_set, ne, a0, Operand(zero_reg)); +} + +bool RegExpMacroAssemblerRISCV::CheckSpecialCharacterClass(uc16 type, + Label* on_no_match) { + // Range checks (c in min..max) are generally implemented by an unsigned + // (c - min) <= (max - min) check. + switch (type) { + case 's': + // Match space-characters. + if (mode_ == LATIN1) { + // One byte space characters are '\t'..'\r', ' ' and \u00a0. + Label success; + __ Branch(&success, eq, current_character(), Operand(' ')); + // Check range 0x09..0x0D. + __ Sub64(a0, current_character(), Operand('\t')); + __ Branch(&success, Uless_equal, a0, Operand('\r' - '\t')); + // \u00a0 (NBSP). + BranchOrBacktrack(on_no_match, ne, a0, Operand(0x00A0 - '\t')); + __ bind(&success); + return true; + } + return false; + case 'S': + // The emitted code for generic character classes is good enough. + return false; + case 'd': + // Match Latin1 digits ('0'..'9'). + __ Sub64(a0, current_character(), Operand('0')); + BranchOrBacktrack(on_no_match, Ugreater, a0, Operand('9' - '0')); + return true; + case 'D': + // Match non Latin1-digits. + __ Sub64(a0, current_character(), Operand('0')); + BranchOrBacktrack(on_no_match, Uless_equal, a0, Operand('9' - '0')); + return true; + case '.': { + // 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. + __ Sub64(a0, a0, Operand(0x0B)); + BranchOrBacktrack(on_no_match, Uless_equal, 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. + __ Sub64(a0, a0, Operand(0x2028 - 0x0B)); + BranchOrBacktrack(on_no_match, Uless_equal, a0, Operand(1)); + } + return true; + } + case 'n': { + // 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. + __ Sub64(a0, a0, Operand(0x0B)); + if (mode_ == LATIN1) { + BranchOrBacktrack(on_no_match, Ugreater, a0, Operand(0x0C - 0x0B)); + } else { + Label done; + BranchOrBacktrack(&done, Uless_equal, 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. + __ Sub64(a0, a0, Operand(0x2028 - 0x0B)); + BranchOrBacktrack(on_no_match, Ugreater, a0, Operand(1)); + __ bind(&done); + } + return true; + } + case 'w': { + if (mode_ != LATIN1) { + // Table is 256 entries, so all Latin1 characters can be tested. + BranchOrBacktrack(on_no_match, Ugreater, current_character(), + Operand('z')); + } + ExternalReference map = + ExternalReference::re_word_character_map(isolate()); + __ li(a0, Operand(map)); + __ Add64(a0, a0, current_character()); + __ Lbu(a0, MemOperand(a0, 0)); + BranchOrBacktrack(on_no_match, eq, a0, Operand(zero_reg)); + return true; + } + case 'W': { + Label done; + if (mode_ != LATIN1) { + // Table is 256 entries, so all Latin1 characters can be tested. + __ Branch(&done, Ugreater, current_character(), Operand('z')); + } + ExternalReference map = + ExternalReference::re_word_character_map(isolate()); + __ li(a0, Operand(map)); + __ Add64(a0, a0, current_character()); + __ Lbu(a0, MemOperand(a0, 0)); + BranchOrBacktrack(on_no_match, ne, a0, Operand(zero_reg)); + if (mode_ != LATIN1) { + __ bind(&done); + } + return true; + } + case '*': + // Match any character. + return true; + // No custom implementation (yet): s(UC16), S(UC16). + default: + return false; + } +} + +void RegExpMacroAssemblerRISCV::Fail() { + __ li(a0, Operand(FAILURE)); + __ jmp(&exit_label_); +} + +Handle RegExpMacroAssemblerRISCV::GetCode(Handle source) { + Label return_a0; + 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_, 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 fp..s11, but ONLY use s3 here - use the regs + // or dont save. + // + // FIXME (RISCV): how about saving only s*registers that are actually used? + RegList registers_to_retain = + fp.bit() | s1.bit() | s2.bit() | s3.bit() | s4.bit() | s5.bit() | + s6.bit() | s7.bit() | s8.bit() /*| s9.bit() | s10.bit() | s11.bit()*/; + DCHECK(NumRegs(registers_to_retain) == kNumCalleeRegsToRetain); + + // 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, // a0 + // int start_index, // a1 + // Address start, // a2 + // Address end, // a3 + // int*capture_output_array, // a4 + // int num_capture_registers, // a5 + // byte* stack_area_base, // a6 + // bool direct_call = false, // a7 + // Isolate * isolate); // on the stack + RegList argument_registers = a0.bit() | a1.bit() | a2.bit() | a3.bit() | + a4.bit() | a5.bit() | a6.bit() | a7.bit(); + + // According to MultiPush implementation, registers will be pushed in the + // order of ra, fp, then s8, ..., s1, and finally a7,...a0 + __ MultiPush(ra.bit() | registers_to_retain | argument_registers); + + // Set frame pointer in space for it if this is not a direct call + // from generated code. + __ Add64(frame_pointer(), sp, + Operand(NumRegs(argument_registers) * kPointerSize)); + + STATIC_ASSERT(kSuccessfulCaptures == kInputString - kSystemPointerSize); + __ mv(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 + + // Check if we have space on the stack for registers. + Label stack_limit_hit; + Label stack_ok; + + ExternalReference stack_limit = + ExternalReference::address_of_jslimit(masm_->isolate()); + __ li(a0, Operand(stack_limit)); + __ Ld(a0, MemOperand(a0)); + __ Sub64(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, Ugreater_equal, a0, + Operand(num_registers_ * kPointerSize)); + // Exit with OutOfMemory exception. There is not enough space on the stack + // for our working registers. + __ li(a0, Operand(EXCEPTION)); + __ jmp(&return_a0); + + __ bind(&stack_limit_hit); + CallCheckStackGuardState(a0); + // If returned value is non-zero, we exit with the returned value as result. + __ Branch(&return_a0, ne, a0, Operand(zero_reg)); + + __ bind(&stack_ok); + // Allocate space on stack for registers. + __ Sub64(sp, sp, Operand(num_registers_ * kPointerSize)); + // Load string end. + __ Ld(end_of_input_address(), MemOperand(frame_pointer(), kInputEnd)); + // Load input start. + __ Ld(a0, MemOperand(frame_pointer(), kInputStart)); + // Find negative length (offset of start relative to end). + __ Sub64(current_input_offset(), a0, end_of_input_address()); + // Set a0 to address of char before start of the input string + // (effectively string position -1). + __ Ld(a1, MemOperand(frame_pointer(), kStartIndex)); + __ Sub64(a0, current_input_offset(), Operand(char_size())); + __ slli(t1, a1, (mode_ == UC16) ? 1 : 0); + __ Sub64(a0, a0, t1); + // Store this value in a local variable, for use when clearing + // position registers. + __ Sd(a0, MemOperand(frame_pointer(), kStringStartMinusOne)); + + // Initialize code pointer register + __ li(code_pointer(), Operand(masm_->CodeObject()), CONSTANT_SIZE); + + Label load_char_start_regexp, 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. + __ Add64(a1, frame_pointer(), Operand(kRegisterZero)); + __ li(a2, Operand(num_saved_registers_)); + Label init_loop; + __ bind(&init_loop); + __ Sd(a0, MemOperand(a1)); + __ Add64(a1, a1, Operand(-kPointerSize)); + __ Sub64(a2, a2, Operand(1)); + __ Branch(&init_loop, ne, a2, Operand(zero_reg)); + } else { + for (int i = 0; i < num_saved_registers_; i++) { + __ Sd(a0, register_location(i)); + } + } + } + + // Initialize backtrack stack pointer. + __ Ld(backtrack_stackpointer(), MemOperand(frame_pointer(), kStackHighEnd)); + + __ jmp(&start_label_); + + // Exit code: + if (success_label_.is_linked()) { + // Save captures when successful. + __ bind(&success_label_); + if (num_saved_registers_ > 0) { + // Copy captures to output. + __ Ld(a1, MemOperand(frame_pointer(), kInputStart)); + __ Ld(a0, MemOperand(frame_pointer(), kRegisterOutput)); + __ Ld(a2, MemOperand(frame_pointer(), kStartIndex)); + __ Sub64(a1, end_of_input_address(), a1); + // a1 is length of input in bytes. + if (mode_ == UC16) { + __ srli(a1, a1, 1); + } + // a1 is length of input in characters. + __ Add64(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) { + __ Ld(a2, register_location(i)); + __ Ld(a3, register_location(i + 1)); + if (i == 0 && global_with_zero_length_check()) { + // Keep capture start in a4 for the zero-length check later. + __ mv(t4, a2); + } + if (mode_ == UC16) { + __ srai(a2, a2, 1); + __ Add64(a2, a2, a1); + __ srai(a3, a3, 1); + __ Add64(a3, a3, a1); + } else { + __ Add64(a2, a1, Operand(a2)); + __ Add64(a3, a1, Operand(a3)); + } + // V8 expects the output to be an int32_t array. + __ Sw(a2, MemOperand(a0)); + __ Add64(a0, a0, kIntSize); + __ Sw(a3, MemOperand(a0)); + __ Add64(a0, a0, kIntSize); + } + } + + if (global()) { + // Restart matching if the regular expression is flagged as global. + __ Ld(a0, MemOperand(frame_pointer(), kSuccessfulCaptures)); + __ Ld(a1, MemOperand(frame_pointer(), kNumOutputRegisters)); + __ Ld(a2, MemOperand(frame_pointer(), kRegisterOutput)); + // Increment success counter. + __ Add64(a0, a0, 1); + __ Sd(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. + __ Sub64(a1, a1, num_saved_registers_); + // Check whether we have enough room for another set of capture results. + __ Branch(&return_a0, lt, a1, Operand(num_saved_registers_)); + + __ Sd(a1, MemOperand(frame_pointer(), kNumOutputRegisters)); + // Advance the location for output. + __ Add64(a2, a2, num_saved_registers_ * kIntSize); + __ Sd(a2, MemOperand(frame_pointer(), kRegisterOutput)); + + // Prepare a0 to initialize registers with its value in the next run. + __ Ld(a0, MemOperand(frame_pointer(), kStringStartMinusOne)); + + if (global_with_zero_length_check()) { + // Special case for zero-length matches. + // t4: capture start index + // Not a zero-length match, restart. + __ Branch(&load_char_start_regexp, ne, current_input_offset(), + Operand(t4)); + // 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); + __ Add64(current_input_offset(), current_input_offset(), + Operand((mode_ == UC16) ? 2 : 1)); + if (global_unicode()) CheckNotInSurrogatePair(0, &advance); + } + + __ Branch(&load_char_start_regexp); + } else { + __ li(a0, Operand(SUCCESS)); + } + } + // Exit and return a0. + __ bind(&exit_label_); + if (global()) { + __ Ld(a0, MemOperand(frame_pointer(), kSuccessfulCaptures)); + } + + __ bind(&return_a0); + // Skip sp past regexp registers and local variables.. + __ mv(sp, frame_pointer()); + + // Restore registers fp..s11 and return (restoring ra to pc). + __ MultiPop(registers_to_retain | ra.bit()); + + __ 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_); + // Put regexp engine registers on stack. + RegList regexp_registers_to_retain = current_input_offset().bit() | + current_character().bit() | + backtrack_stackpointer().bit(); + __ MultiPush(regexp_registers_to_retain); + CallCheckStackGuardState(a0); + __ MultiPop(regexp_registers_to_retain); + // If returning non-zero, we should end execution with the given + // result as return value. + __ Branch(&return_a0, ne, a0, Operand(zero_reg)); + + // String might have moved: Reload end of string from frame. + __ Ld(end_of_input_address(), MemOperand(frame_pointer(), kInputEnd)); + __ li(code_pointer(), Operand(masm_->CodeObject()), CONSTANT_SIZE); + SafeReturn(); + } + + // Backtrack stack overflow code. + if (stack_overflow_label_.is_linked()) { + SafeCallTarget(&stack_overflow_label_); + // Reached if the backtrack-stack limit has been hit. + // Put regexp engine registers on stack first. + RegList regexp_registers = + current_input_offset().bit() | current_character().bit(); + __ MultiPush(regexp_registers); + + // Call GrowStack(backtrack_stackpointer(), &stack_base) + static const int num_arguments = 3; + __ PrepareCallCFunction(num_arguments, a0); + __ mv(a0, backtrack_stackpointer()); + __ Add64(a1, frame_pointer(), Operand(kStackHighEnd)); + __ li(a2, Operand(ExternalReference::isolate_address(masm_->isolate()))); + ExternalReference grow_stack = + ExternalReference::re_grow_stack(masm_->isolate()); + __ CallCFunction(grow_stack, num_arguments); + // Restore regexp registers. + __ MultiPop(regexp_registers); + // If return nullptr, we have failed to grow the stack, and + // must exit with a stack-overflow exception. + __ Branch(&exit_with_exception, eq, a0, Operand(zero_reg)); + // Otherwise use return value as new stack pointer. + __ mv(backtrack_stackpointer(), a0); + // Restore saved registers and continue. + __ li(code_pointer(), Operand(masm_->CodeObject()), CONSTANT_SIZE); + __ Ld(end_of_input_address(), MemOperand(frame_pointer(), kInputEnd)); + 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(a0, Operand(EXCEPTION)); + __ jmp(&return_a0); + } + } + + 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 RegExpMacroAssemblerRISCV::GoTo(Label* to) { + if (to == nullptr) { + Backtrack(); + return; + } + __ jmp(to); + return; +} + +void RegExpMacroAssemblerRISCV::IfRegisterGE(int reg, int comparand, + Label* if_ge) { + __ Ld(a0, register_location(reg)); + BranchOrBacktrack(if_ge, ge, a0, Operand(comparand)); +} + +void RegExpMacroAssemblerRISCV::IfRegisterLT(int reg, int comparand, + Label* if_lt) { + __ Ld(a0, register_location(reg)); + BranchOrBacktrack(if_lt, lt, a0, Operand(comparand)); +} + +void RegExpMacroAssemblerRISCV::IfRegisterEqPos(int reg, Label* if_eq) { + __ Ld(a0, register_location(reg)); + BranchOrBacktrack(if_eq, eq, a0, Operand(current_input_offset())); +} + +RegExpMacroAssembler::IrregexpImplementation +RegExpMacroAssemblerRISCV::Implementation() { + return kRISCVImplementation; +} + +void RegExpMacroAssemblerRISCV::PopCurrentPosition() { + Pop(current_input_offset()); +} + +void RegExpMacroAssemblerRISCV::PopRegister(int register_index) { + Pop(a0); + __ Sd(a0, register_location(register_index)); +} + +void RegExpMacroAssemblerRISCV::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_); + 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)) { + __ Lwu(a0, MemOperand(code_pointer(), cp_offset)); + } else { + __ Add64(a0, code_pointer(), cp_offset); + __ Lwu(a0, MemOperand(a0, 0)); + } + } + Push(a0); + CheckStackLimit(); +} + +void RegExpMacroAssemblerRISCV::PushCurrentPosition() { + Push(current_input_offset()); +} + +void RegExpMacroAssemblerRISCV::PushRegister(int register_index, + StackCheckFlag check_stack_limit) { + __ Ld(a0, register_location(register_index)); + Push(a0); + if (check_stack_limit) CheckStackLimit(); +} + +void RegExpMacroAssemblerRISCV::ReadCurrentPositionFromRegister(int reg) { + __ Ld(current_input_offset(), register_location(reg)); +} + +void RegExpMacroAssemblerRISCV::ReadStackPointerFromRegister(int reg) { + __ Ld(backtrack_stackpointer(), register_location(reg)); + __ Ld(a0, MemOperand(frame_pointer(), kStackHighEnd)); + __ Add64(backtrack_stackpointer(), backtrack_stackpointer(), Operand(a0)); +} + +void RegExpMacroAssemblerRISCV::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 RegExpMacroAssemblerRISCV::SetRegister(int register_index, int to) { + DCHECK(register_index >= num_saved_registers_); // Reserved for positions! + __ li(a0, Operand(to)); + __ Sd(a0, register_location(register_index)); +} + +bool RegExpMacroAssemblerRISCV::Succeed() { + __ jmp(&success_label_); + return global(); +} + +void RegExpMacroAssemblerRISCV::WriteCurrentPositionToRegister(int reg, + int cp_offset) { + if (cp_offset == 0) { + __ Sd(current_input_offset(), register_location(reg)); + } else { + __ Add64(a0, current_input_offset(), Operand(cp_offset * char_size())); + __ Sd(a0, register_location(reg)); + } +} + +void RegExpMacroAssemblerRISCV::ClearRegisters(int reg_from, int reg_to) { + DCHECK(reg_from <= reg_to); + __ Ld(a0, MemOperand(frame_pointer(), kStringStartMinusOne)); + for (int reg = reg_from; reg <= reg_to; reg++) { + __ Sd(a0, register_location(reg)); + } +} + +void RegExpMacroAssemblerRISCV::WriteStackPointerToRegister(int reg) { + __ Ld(a1, MemOperand(frame_pointer(), kStackHighEnd)); + __ Sub64(a0, backtrack_stackpointer(), a1); + __ Sd(a0, register_location(reg)); +} + +bool RegExpMacroAssemblerRISCV::CanReadUnaligned() { return false; } + +// Private methods: + +void RegExpMacroAssemblerRISCV::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. + __ mv(scratch, sp); + __ Sub64(sp, sp, Operand(kPointerSize)); + DCHECK(base::bits::IsPowerOfTwo(stack_alignment)); + __ And(sp, sp, Operand(-stack_alignment)); + __ Sd(scratch, MemOperand(sp)); + + __ mv(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)); + __ Sub64(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. + __ mv(a0, sp); + + ExternalReference stack_guard_check = + ExternalReference::re_check_stack_guard_state(masm_->isolate()); + __ li(t6, Operand(stack_guard_check)); + + EmbeddedData d = EmbeddedData::FromBlob(); + CHECK(Builtins::IsIsolateIndependent(Builtins::kDirectCEntry)); + Address entry = d.InstructionStartOfBuiltin(Builtins::kDirectCEntry); + __ li(kScratchReg, Operand(entry, RelocInfo::OFF_HEAP_TARGET)); + __ Call(kScratchReg); + + // DirectCEntry 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. + __ Ld(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 RegExpMacroAssemblerRISCV::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 RegExpMacroAssemblerRISCV::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 RegExpMacroAssemblerRISCV::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 { + __ Ld(a1, MemOperand(frame_pointer(), kStringStartMinusOne)); + __ Add64(a0, current_input_offset(), Operand(cp_offset * char_size())); + BranchOrBacktrack(on_outside_input, le, a0, Operand(a1)); + } +} + +void RegExpMacroAssemblerRISCV::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 RegExpMacroAssemblerRISCV::SafeCall(Label* to, Condition cond, Register rs, + const Operand& rt) { + __ BranchAndLink(to, cond, rs, rt); +} + +void RegExpMacroAssemblerRISCV::SafeReturn() { + __ pop(ra); + __ Add64(t1, ra, Operand(masm_->CodeObject())); + __ Jump(t1); +} + +void RegExpMacroAssemblerRISCV::SafeCallTarget(Label* name) { + __ bind(name); + __ Sub64(ra, ra, Operand(masm_->CodeObject())); + __ push(ra); +} + +void RegExpMacroAssemblerRISCV::Push(Register source) { + DCHECK(source != backtrack_stackpointer()); + __ Add64(backtrack_stackpointer(), backtrack_stackpointer(), + Operand(-kIntSize)); + __ Sw(source, MemOperand(backtrack_stackpointer())); +} + +void RegExpMacroAssemblerRISCV::Pop(Register target) { + DCHECK(target != backtrack_stackpointer()); + __ Lw(target, MemOperand(backtrack_stackpointer())); + __ Add64(backtrack_stackpointer(), backtrack_stackpointer(), kIntSize); +} + +void RegExpMacroAssemblerRISCV::CheckPreemption() { + // Check for preemption. + ExternalReference stack_limit = + ExternalReference::address_of_jslimit(masm_->isolate()); + __ li(a0, Operand(stack_limit)); + __ Ld(a0, MemOperand(a0)); + SafeCall(&check_preempt_label_, Uless_equal, sp, Operand(a0)); +} + +void RegExpMacroAssemblerRISCV::CheckStackLimit() { + ExternalReference stack_limit = + ExternalReference::address_of_regexp_stack_limit_address( + masm_->isolate()); + + __ li(a0, Operand(stack_limit)); + __ Ld(a0, MemOperand(a0)); + SafeCall(&stack_overflow_label_, Uless_equal, backtrack_stackpointer(), + Operand(a0)); +} + +void RegExpMacroAssemblerRISCV::LoadCurrentCharacterUnchecked(int cp_offset, + int characters) { + Register offset = current_input_offset(); + if (cp_offset != 0) { + // t4 is not being used to store the capture start index at this point. + __ Add64(t4, current_input_offset(), Operand(cp_offset * char_size())); + offset = t4; + } + // We assume that we cannot do unaligned loads on RISC-V, so this function + // must only be used to load a single character at a time. + DCHECK_EQ(1, characters); + __ Add64(t1, end_of_input_address(), Operand(offset)); + if (mode_ == LATIN1) { + __ Lbu(current_character(), MemOperand(t1, 0)); + } else { + DCHECK(mode_ == UC16); + __ Lhu(current_character(), MemOperand(t1, 0)); + } +} + +#undef __ + +} // namespace internal +} // namespace v8 + +#endif // V8_TARGET_ARCH_RISCV64 Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/regexp/riscv64/regexp-macro-assembler-riscv64.h =================================================================== --- /dev/null +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/regexp/riscv64/regexp-macro-assembler-riscv64.h @@ -0,0 +1,213 @@ +// 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_RISCV_REGEXP_MACRO_ASSEMBLER_RISCV_H_ +#define V8_REGEXP_RISCV_REGEXP_MACRO_ASSEMBLER_RISCV_H_ + +#include "src/codegen/macro-assembler.h" +#include "src/codegen/riscv64/assembler-riscv64.h" +#include "src/regexp/regexp-macro-assembler.h" + +namespace v8 { +namespace internal { + +class V8_EXPORT_PRIVATE RegExpMacroAssemblerRISCV + : public NativeRegExpMacroAssembler { + public: + RegExpMacroAssemblerRISCV(Isolate* isolate, Zone* zone, Mode mode, + int registers_to_save); + virtual ~RegExpMacroAssemblerRISCV(); + virtual int stack_limit_slack(); + virtual void AdvanceCurrentPosition(int by); + virtual void AdvanceRegister(int reg, int by); + virtual void Backtrack(); + virtual void Bind(Label* label); + virtual void CheckAtStart(int cp_offset, Label* on_at_start); + virtual void CheckCharacter(uint32_t c, Label* on_equal); + virtual void CheckCharacterAfterAnd(uint32_t c, uint32_t mask, + Label* on_equal); + virtual void CheckCharacterGT(uc16 limit, Label* on_greater); + virtual void CheckCharacterLT(uc16 limit, Label* on_less); + // A "greedy loop" is a loop that is both greedy and with a simple + // body. It has a particularly simple implementation. + virtual void CheckGreedyLoop(Label* on_tos_equals_current_position); + virtual void CheckNotAtStart(int cp_offset, Label* on_not_at_start); + virtual void CheckNotBackReference(int start_reg, bool read_backward, + Label* on_no_match); + virtual void CheckNotBackReferenceIgnoreCase(int start_reg, + bool read_backward, bool unicode, + Label* on_no_match); + virtual void CheckNotCharacter(uint32_t c, Label* on_not_equal); + virtual void CheckNotCharacterAfterAnd(uint32_t c, uint32_t mask, + Label* on_not_equal); + virtual void CheckNotCharacterAfterMinusAnd(uc16 c, uc16 minus, uc16 mask, + Label* on_not_equal); + virtual void CheckCharacterInRange(uc16 from, uc16 to, Label* on_in_range); + virtual void CheckCharacterNotInRange(uc16 from, uc16 to, + Label* on_not_in_range); + virtual void CheckBitInTable(Handle table, Label* on_bit_set); + + // Checks whether the given offset from the current position is before + // the end of the string. + virtual void CheckPosition(int cp_offset, Label* on_outside_input); + virtual bool CheckSpecialCharacterClass(uc16 type, Label* on_no_match); + virtual void Fail(); + virtual Handle GetCode(Handle source); + virtual void GoTo(Label* label); + virtual void IfRegisterGE(int reg, int comparand, Label* if_ge); + virtual void IfRegisterLT(int reg, int comparand, Label* if_lt); + virtual void IfRegisterEqPos(int reg, Label* if_eq); + virtual IrregexpImplementation Implementation(); + virtual void LoadCurrentCharacterUnchecked(int cp_offset, + int character_count); + virtual void PopCurrentPosition(); + virtual void PopRegister(int register_index); + virtual void PushBacktrack(Label* label); + virtual void PushCurrentPosition(); + virtual void PushRegister(int register_index, + StackCheckFlag check_stack_limit); + virtual void ReadCurrentPositionFromRegister(int reg); + virtual void ReadStackPointerFromRegister(int reg); + virtual void SetCurrentPositionFromEnd(int by); + virtual void SetRegister(int register_index, int to); + virtual bool Succeed(); + virtual void WriteCurrentPositionToRegister(int reg, int cp_offset); + virtual void ClearRegisters(int reg_from, int reg_to); + virtual void WriteStackPointerToRegister(int reg); + virtual bool CanReadUnaligned(); + + // 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. + // Registers s1 to s8, fp, and ra. + static const int kStoredRegisters = kFramePointer; + // Return address (stored from link register, read into pc on return). + + // This 9 is 8 s-regs (s1..s8) plus fp. + static const int kNumCalleeRegsToRetain = 9; + static const int kReturnAddress = + kStoredRegisters + kNumCalleeRegsToRetain * kPointerSize; + + // Stack frame header. + static const int kStackFrameHeader = kReturnAddress; + // Stack parameters placed by caller. + static const int kIsolate = kStackFrameHeader + kPointerSize; + + // Below the frame pointer. + // Register parameters stored by setup code. + static const int kDirectCall = kFramePointer - kPointerSize; + static const int kStackHighEnd = kDirectCall - kPointerSize; + static const int kNumOutputRegisters = kStackHighEnd - kPointerSize; + static const int kRegisterOutput = kNumOutputRegisters - kPointerSize; + static const int kInputEnd = kRegisterOutput - kPointerSize; + static const int kInputStart = kInputEnd - kPointerSize; + static const int kStartIndex = kInputStart - kPointerSize; + static const int kInputString = kStartIndex - kPointerSize; + // When adding local variables remember to push space for them in + // the frame in GetCode. + static const int kSuccessfulCaptures = kInputString - kPointerSize; + static const int kStringStartMinusOne = kSuccessfulCaptures - kPointerSize; + static const int kBacktrackCount = kStringStartMinusOne - kSystemPointerSize; + // First register address. Following registers are below it on the stack. + static const int kRegisterZero = kBacktrackCount - kSystemPointerSize; + + // Initial size of code buffer. + static const int kRegExpCodeSize = 1024; + + // Check whether preemption has been requested. + void CheckPreemption(); + + // Check whether we are exceeding the stack limit on the backtrack stack. + void CheckStackLimit(); + + // Generate a call to CheckStackGuardState. + void CallCheckStackGuardState(Register scratch); + + // The ebp-relative location of a regexp register. + MemOperand register_location(int register_index); + + // Register holding the current input position as negative offset from + // the end of the string. + inline Register current_input_offset() { return a6; } + + // The register containing the current character after LoadCurrentCharacter. + inline Register current_character() { return a7; } + + // Register holding address of the end of the input string. + inline Register end_of_input_address() { return t2; } + + // Register holding the frame address. Local variables, parameters and + // regexp registers are addressed relative to this. + inline Register frame_pointer() { return fp; } + + // The register containing the backtrack stack top. Provides a meaningful + // name to the register. + inline Register backtrack_stackpointer() { return t0; } + + // Register holding pointer to the current code object. + inline Register code_pointer() { return a5; } + + // Byte size of chars in the string to match (decided by the Mode argument). + inline int char_size() { return static_cast(mode_); } + + // Equivalent to a conditional branch to the label, unless the label + // is 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); + + Isolate* isolate() const { return masm_->isolate(); } + + MacroAssembler* masm_; + + // Which mode to generate code for (Latin1 or UC16). + Mode mode_; + + // One greater than maximal register index actually used. + int num_registers_; + + // Number of registers to output at the end (the saved registers + // are always 0..num_saved_registers_-1). + int num_saved_registers_; + + // Labels used internally. + Label entry_label_; + Label start_label_; + Label success_label_; + Label backtrack_label_; + Label exit_label_; + Label check_preempt_label_; + Label stack_overflow_label_; + Label internal_failure_label_; +}; + +} // namespace internal +} // namespace v8 + +#endif // V8_REGEXP_RISCV_REGEXP_MACRO_ASSEMBLER_RISCV_H_ Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/runtime/runtime-atomics.cc =================================================================== --- qtwebengine-everywhere-src-5.15.3.orig/src/3rdparty/chromium/v8/src/runtime/runtime-atomics.cc +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/runtime/runtime-atomics.cc @@ -19,7 +19,8 @@ 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_PPC || V8_TARGET_ARCH_S390 || V8_TARGET_ARCH_S390X || \ + V8_TARGET_ARCH_RISCV64 || V8_TARGET_ARCH_RISCV namespace { @@ -568,6 +569,6 @@ RUNTIME_FUNCTION(Runtime_AtomicsXor) { U #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_RISCV } // namespace internal } // namespace v8 Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/snapshot/deserializer.h =================================================================== --- qtwebengine-everywhere-src-5.15.3.orig/src/3rdparty/chromium/v8/src/snapshot/deserializer.h +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/snapshot/deserializer.h @@ -29,9 +29,10 @@ class Object; // Used for platforms with embedded constant pools to trigger deserialization // of objects found in code. -#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) || V8_EMBEDDED_CONSTANT_POOL +#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) || \ + defined(V8_TARGET_ARCH_RISCV) || V8_EMBEDDED_CONSTANT_POOL #define V8_CODE_EMBEDS_OBJECT_POINTER 1 #else #define V8_CODE_EMBEDS_OBJECT_POINTER 0 Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/wasm/baseline/liftoff-assembler-defs.h =================================================================== --- qtwebengine-everywhere-src-5.15.3.orig/src/3rdparty/chromium/v8/src/wasm/baseline/liftoff-assembler-defs.h +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/wasm/baseline/liftoff-assembler-defs.h @@ -69,6 +69,23 @@ constexpr RegList kLiftoffAssemblerFpCac d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d16, d17, d18, d19, d20, d21, d22, d23, d24, d25, d26, d27, d28, d29); +#elif V8_TARGET_ARCH_RISCV64 + +// Any change of kLiftoffAssemblerGpCacheRegs also need to update +// kPushedGpRegs in frame-constants-riscv64.h +constexpr RegList kLiftoffAssemblerGpCacheRegs = + Register::ListOf(a0, a1, a2, a3, a4, a5, a6, a7, t0, t1, t2, s7); + +// Any change of kLiftoffAssemblerGpCacheRegs also need to update +// kPushedFpRegs in frame-constants-riscv64.h +constexpr RegList kLiftoffAssemblerFpCacheRegs = + DoubleRegister::ListOf(ft0, ft1, ft2, ft3, ft4, ft5, ft6, ft7, fa0, fa1, + fa2, fa3, fa4, fa5, fa6, fa7, ft8, ft9, ft10, ft11); + +#elif V8_TARGET_ARCH_RISCV + +#error RISCV (32) architecture not supported yet + #else constexpr RegList kLiftoffAssemblerGpCacheRegs = 0xff; @@ -116,6 +133,18 @@ constexpr Condition kUnsignedLessEqual = constexpr Condition kUnsignedGreaterThan = hi; constexpr Condition kUnsignedGreaterEqual = hs; +#elif V8_TARGET_ARCH_RISCV64 || V8_TARGET_ARCH_RISCV +constexpr Condition kEqual = eq; +constexpr Condition kUnequal = ne; +constexpr Condition kSignedLessThan = lt; +constexpr Condition kSignedLessEqual = le; +constexpr Condition kSignedGreaterThan = gt; +constexpr Condition kSignedGreaterEqual = ge; +constexpr Condition kUnsignedLessThan = ult; +constexpr Condition kUnsignedLessEqual = ule; +constexpr Condition kUnsignedGreaterThan = ugt; +constexpr Condition kUnsignedGreaterEqual = uge; + #else // On unimplemented platforms, just make this compile. Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/wasm/baseline/liftoff-assembler.h =================================================================== --- qtwebengine-everywhere-src-5.15.3.orig/src/3rdparty/chromium/v8/src/wasm/baseline/liftoff-assembler.h +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/wasm/baseline/liftoff-assembler.h @@ -1353,6 +1353,10 @@ class LiftoffStackSlots { #include "src/wasm/baseline/mips64/liftoff-assembler-mips64.h" #elif V8_TARGET_ARCH_S390 #include "src/wasm/baseline/s390/liftoff-assembler-s390.h" +#elif V8_TARGET_ARCH_RISCV64 +#include "src/wasm/baseline/riscv64/liftoff-assembler-riscv64.h" +#elif V8_TARGET_ARCH_RISCV +#include "src/wasm/baseline/riscv/liftoff-assembler-riscv.h" #else #error Unsupported architecture. #endif Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/wasm/baseline/riscv64/liftoff-assembler-riscv64.h =================================================================== --- /dev/null +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/wasm/baseline/riscv64/liftoff-assembler-riscv64.h @@ -0,0 +1,2335 @@ +// 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_RISCV_LIFTOFF_ASSEMBLER_RISCV_H_ +#define V8_WASM_BASELINE_RISCV_LIFTOFF_ASSEMBLER_RISCV_H_ + +#include "src/wasm/baseline/liftoff-assembler.h" + +namespace v8 { +namespace internal { +namespace wasm { + +namespace liftoff { + +// 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 | 0xa: WASM | +// -2 | instance | +// -----+--------------------+--------------------------- +// -3 | slot 0 | ^ +// -4 | slot 1 | | +// | | Frame slots +// | | | +// | | v +// | optional padding slot to keep the stack 16 byte aligned. +// -----+--------------------+ <-- stack ptr (sp) +// + +// fp-8 holds the stack marker, fp-16 is the instance parameter. +constexpr int kInstanceOffset = 16; + +inline MemOperand GetStackSlot(int offset) { return MemOperand(fp, -offset); } + +inline MemOperand GetInstanceOperand() { return GetStackSlot(kInstanceOffset); } + +inline void Load(LiftoffAssembler* assm, LiftoffRegister dst, MemOperand src, + ValueType type) { + switch (type.kind()) { + case ValueType::kI32: + assm->Lw(dst.gp(), src); + break; + case ValueType::kI64: + assm->Ld(dst.gp(), src); + break; + case ValueType::kF32: + assm->LoadFloat(dst.fp(), src); + break; + case ValueType::kF64: + assm->LoadDouble(dst.fp(), src); + break; + default: + UNREACHABLE(); + } +} + +inline void Store(LiftoffAssembler* assm, Register base, int32_t offset, + LiftoffRegister src, ValueType type) { + MemOperand dst(base, offset); + switch (type.kind()) { + case ValueType::kI32: + assm->Usw(src.gp(), dst); + break; + case ValueType::kI64: + assm->Usd(src.gp(), dst); + break; + case ValueType::kF32: + assm->UStoreFloat(src.fp(), dst, t5); + break; + case ValueType::kF64: + assm->UStoreDouble(src.fp(), dst, t5); + break; + default: + UNREACHABLE(); + } +} + +inline void push(LiftoffAssembler* assm, LiftoffRegister reg, ValueType type) { + switch (type.kind()) { + case ValueType::kI32: + assm->addi(sp, sp, -kSystemPointerSize); + assm->Sw(reg.gp(), MemOperand(sp, 0)); + break; + case ValueType::kI64: + assm->push(reg.gp()); + break; + case ValueType::kF32: + assm->addi(sp, sp, -kSystemPointerSize); + assm->StoreFloat(reg.fp(), MemOperand(sp, 0)); + break; + case ValueType::kF64: + assm->addi(sp, sp, -kSystemPointerSize); + assm->StoreDouble(reg.fp(), MemOperand(sp, 0)); + break; + default: + UNREACHABLE(); + } +} + +#if defined(V8_TARGET_BIG_ENDIAN) +inline void ChangeEndiannessLoad(LiftoffAssembler* assm, LiftoffRegister dst, + LoadType type, LiftoffRegList pinned) { + bool is_float = false; + LiftoffRegister tmp = dst; + switch (type.value()) { + case LoadType::kI64Load8U: + case LoadType::kI64Load8S: + case LoadType::kI32Load8U: + case LoadType::kI32Load8S: + // No need to change endianness for byte size. + return; + case LoadType::kF32Load: + is_float = true; + tmp = assm->GetUnusedRegister(kGpReg, pinned); + assm->emit_type_conversion(kExprI32ReinterpretF32, tmp, dst); + V8_FALLTHROUGH; + case LoadType::kI64Load32U: + assm->TurboAssembler::ByteSwapUnsigned(tmp.gp(), tmp.gp(), 4); + break; + case LoadType::kI32Load: + case LoadType::kI64Load32S: + assm->TurboAssembler::ByteSwapSigned(tmp.gp(), tmp.gp(), 4); + break; + case LoadType::kI32Load16S: + case LoadType::kI64Load16S: + assm->TurboAssembler::ByteSwapSigned(tmp.gp(), tmp.gp(), 2); + break; + case LoadType::kI32Load16U: + case LoadType::kI64Load16U: + assm->TurboAssembler::ByteSwapUnsigned(tmp.gp(), tmp.gp(), 2); + break; + case LoadType::kF64Load: + is_float = true; + tmp = assm->GetUnusedRegister(kGpReg, pinned); + assm->emit_type_conversion(kExprI64ReinterpretF64, tmp, dst); + V8_FALLTHROUGH; + case LoadType::kI64Load: + assm->TurboAssembler::ByteSwapSigned(tmp.gp(), tmp.gp(), 8); + break; + default: + UNREACHABLE(); + } + + if (is_float) { + switch (type.value()) { + case LoadType::kF32Load: + assm->emit_type_conversion(kExprF32ReinterpretI32, dst, tmp); + break; + case LoadType::kF64Load: + assm->emit_type_conversion(kExprF64ReinterpretI64, dst, tmp); + break; + default: + UNREACHABLE(); + } + } +} + +inline void ChangeEndiannessStore(LiftoffAssembler* assm, LiftoffRegister src, + StoreType type, LiftoffRegList pinned) { + bool is_float = false; + LiftoffRegister tmp = src; + switch (type.value()) { + case StoreType::kI64Store8: + case StoreType::kI32Store8: + // No need to change endianness for byte size. + return; + case StoreType::kF32Store: + is_float = true; + tmp = assm->GetUnusedRegister(kGpReg, pinned); + assm->emit_type_conversion(kExprI32ReinterpretF32, tmp, src); + V8_FALLTHROUGH; + case StoreType::kI32Store: + assm->TurboAssembler::ByteSwapSigned(tmp.gp(), tmp.gp(), 4); + break; + case StoreType::kI32Store16: + assm->TurboAssembler::ByteSwapSigned(tmp.gp(), tmp.gp(), 2); + break; + case StoreType::kF64Store: + is_float = true; + tmp = assm->GetUnusedRegister(kGpReg, pinned); + assm->emit_type_conversion(kExprI64ReinterpretF64, tmp, src); + V8_FALLTHROUGH; + case StoreType::kI64Store: + assm->TurboAssembler::ByteSwapSigned(tmp.gp(), tmp.gp(), 8); + break; + case StoreType::kI64Store32: + assm->TurboAssembler::ByteSwapSigned(tmp.gp(), tmp.gp(), 4); + break; + case StoreType::kI64Store16: + assm->TurboAssembler::ByteSwapSigned(tmp.gp(), tmp.gp(), 2); + break; + default: + UNREACHABLE(); + } + + if (is_float) { + switch (type.value()) { + case StoreType::kF32Store: + assm->emit_type_conversion(kExprF32ReinterpretI32, src, tmp); + break; + case StoreType::kF64Store: + assm->emit_type_conversion(kExprF64ReinterpretI64, src, tmp); + break; + default: + UNREACHABLE(); + } + } +} +#endif // V8_TARGET_BIG_ENDIAN + +} // namespace liftoff + +int LiftoffAssembler::PrepareStackFrame() { + int offset = pc_offset(); + // When constant that represents size of stack frame can't be represented + // as 16bit we need three instructions to add it to sp, so we reserve space + // for this case. + Add64(sp, sp, Operand(0L)); + 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. + Ld(scratch, MemOperand(fp, 8)); + Push(scratch); + Ld(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) { + Ld(scratch, MemOperand(sp, i * 8)); + Sd(scratch, MemOperand(fp, (i - stack_param_delta) * 8)); + } + + // Set the new stack and frame pointer. + Add64(sp, fp, -stack_param_delta * 8); + Pop(ra, fp); +} + +void LiftoffAssembler::PatchPrepareStackFrame(int offset, int frame_size) { + // 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 bytes can be represented as 16bit, addi will be generated and two + // nops will stay untouched. Otherwise, lui-ori sequence will load it to + // register and, as third instruction, daddu will be generated. + patching_assembler.Add64(sp, sp, Operand(-frame_size)); +} + +void LiftoffAssembler::FinishCode() {} + +void LiftoffAssembler::AbortCompilation() {} + +// static +constexpr int LiftoffAssembler::StaticStackFrameSize() { + return liftoff::kInstanceOffset; +} + +int LiftoffAssembler::SlotSizeForType(ValueType type) { + switch (type.kind()) { + case ValueType::kS128: + return type.element_size_bytes(); + default: + return kStackSlotSize; + } +} + +bool LiftoffAssembler::NeedsAlignment(ValueType type) { + switch (type.kind()) { + case ValueType::kS128: + return true; + default: + // No alignment because all other types are kStackSlotSize. + return false; + } +} + +void LiftoffAssembler::LoadConstant(LiftoffRegister reg, WasmValue value, + RelocInfo::Mode rmode) { + switch (value.type().kind()) { + case ValueType::kI32: + TurboAssembler::li(reg.gp(), Operand(value.to_i32(), rmode)); + break; + case ValueType::kI64: + TurboAssembler::li(reg.gp(), Operand(value.to_i64(), rmode)); + break; + case ValueType::kF32: + TurboAssembler::LoadFPRImmediate(reg.fp(), + value.to_f32_boxed().get_bits()); + break; + case ValueType::kF64: + TurboAssembler::LoadFPRImmediate(reg.fp(), + value.to_f64_boxed().get_bits()); + break; + default: + UNREACHABLE(); + } +} + +void LiftoffAssembler::LoadFromInstance(Register dst, uint32_t offset, + int size) { + DCHECK_LE(offset, kMaxInt); + Ld(dst, liftoff::GetInstanceOperand()); + DCHECK(size == 4 || size == 8); + if (size == 4) { + Lw(dst, MemOperand(dst, offset)); + } else { + Ld(dst, MemOperand(dst, offset)); + } +} + +void LiftoffAssembler::LoadTaggedPointerFromInstance(Register dst, + uint32_t offset) { + LoadFromInstance(dst, offset, kTaggedSize); +} + +void LiftoffAssembler::SpillInstance(Register instance) { + Sd(instance, liftoff::GetInstanceOperand()); +} + +void LiftoffAssembler::FillInstanceInto(Register dst) { + Ld(dst, liftoff::GetInstanceOperand()); +} + +void LiftoffAssembler::LoadTaggedPointer(Register dst, Register src_addr, + Register offset_reg, + int32_t offset_imm, + LiftoffRegList pinned) { + STATIC_ASSERT(kTaggedSize == kInt64Size); + Load(LiftoffRegister(dst), src_addr, offset_reg, + static_cast(offset_imm), LoadType::kI64Load, pinned); +} + +void LiftoffAssembler::StoreTaggedPointer(Register dst_addr, + int32_t offset_imm, + LiftoffRegister src, + LiftoffRegList pinned) { + bailout(kRefTypes, "GlobalSet"); +} + +void LiftoffAssembler::Load(LiftoffRegister dst, Register src_addr, + Register offset_reg, uint32_t offset_imm, + LoadType type, LiftoffRegList pinned, + uint32_t* protected_load_pc, bool is_load_mem) { + Register src = no_reg; + if (offset_reg != no_reg) { + src = GetUnusedRegister(kGpReg, pinned).gp(); + emit_ptrsize_add(src, src_addr, offset_reg); + } + MemOperand src_op = (offset_reg != no_reg) ? MemOperand(src, offset_imm) + : MemOperand(src_addr, offset_imm); + + if (protected_load_pc) *protected_load_pc = pc_offset(); + switch (type.value()) { + case LoadType::kI32Load8U: + case LoadType::kI64Load8U: + Lbu(dst.gp(), src_op); + break; + case LoadType::kI32Load8S: + case LoadType::kI64Load8S: + Lb(dst.gp(), src_op); + break; + case LoadType::kI32Load16U: + case LoadType::kI64Load16U: + TurboAssembler::Ulhu(dst.gp(), src_op); + break; + case LoadType::kI32Load16S: + case LoadType::kI64Load16S: + TurboAssembler::Ulh(dst.gp(), src_op); + break; + case LoadType::kI64Load32U: + TurboAssembler::Ulwu(dst.gp(), src_op); + break; + case LoadType::kI32Load: + case LoadType::kI64Load32S: + TurboAssembler::Ulw(dst.gp(), src_op); + break; + case LoadType::kI64Load: + TurboAssembler::Uld(dst.gp(), src_op); + break; + case LoadType::kF32Load: + TurboAssembler::ULoadFloat(dst.fp(), src_op, t5); + break; + case LoadType::kF64Load: + TurboAssembler::ULoadDouble(dst.fp(), src_op, t5); + break; + default: + UNREACHABLE(); + } + +#if defined(V8_TARGET_BIG_ENDIAN) + if (is_load_mem) { + pinned.set(src_op.rm()); + liftoff::ChangeEndiannessLoad(this, dst, type, pinned); + } +#endif +} + +void LiftoffAssembler::Store(Register dst_addr, Register offset_reg, + uint32_t offset_imm, LiftoffRegister src, + StoreType type, LiftoffRegList pinned, + uint32_t* protected_store_pc, bool is_store_mem) { + Register dst = no_reg; + MemOperand dst_op = MemOperand(dst_addr, offset_imm); + if (offset_reg != no_reg) { + if (is_store_mem) { + pinned.set(src); + } + dst = GetUnusedRegister(kGpReg, pinned).gp(); + emit_ptrsize_add(dst, dst_addr, offset_reg); + dst_op = MemOperand(dst, offset_imm); + } + +#if defined(V8_TARGET_BIG_ENDIAN) + if (is_store_mem) { + pinned.set(dst_op.rm()); + LiftoffRegister tmp = GetUnusedRegister(src.reg_class(), pinned); + // Save original value. + Move(tmp, src, type.value_type()); + + src = tmp; + pinned.set(tmp); + liftoff::ChangeEndiannessStore(this, src, type, pinned); + } +#endif + + if (protected_store_pc) *protected_store_pc = pc_offset(); + + // FIXME (RISCV): current implementation treats all stores as unaligned + switch (type.value()) { + case StoreType::kI32Store8: + case StoreType::kI64Store8: + Sb(src.gp(), dst_op); + break; + case StoreType::kI32Store16: + case StoreType::kI64Store16: + TurboAssembler::Ush(src.gp(), dst_op); + break; + case StoreType::kI32Store: + case StoreType::kI64Store32: + TurboAssembler::Usw(src.gp(), dst_op); + break; + case StoreType::kI64Store: + TurboAssembler::Usd(src.gp(), dst_op); + break; + case StoreType::kF32Store: + TurboAssembler::UStoreFloat(src.fp(), dst_op, t5); + break; + case StoreType::kF64Store: + TurboAssembler::UStoreDouble(src.fp(), dst_op, t5); + break; + default: + UNREACHABLE(); + } +} + +void LiftoffAssembler::AtomicLoad(LiftoffRegister dst, Register src_addr, + Register offset_reg, uint32_t offset_imm, + LoadType type, LiftoffRegList pinned) { + bailout(kAtomics, "AtomicLoad"); +} + +void LiftoffAssembler::AtomicStore(Register dst_addr, Register offset_reg, + uint32_t offset_imm, LiftoffRegister src, + StoreType type, LiftoffRegList pinned) { + bailout(kAtomics, "AtomicStore"); +} + +void LiftoffAssembler::AtomicAdd(Register dst_addr, Register offset_reg, + uint32_t offset_imm, LiftoffRegister value, + LiftoffRegister result, StoreType type) { + bailout(kAtomics, "AtomicAdd"); +} + +void LiftoffAssembler::AtomicSub(Register dst_addr, Register offset_reg, + uint32_t offset_imm, LiftoffRegister value, + LiftoffRegister result, StoreType type) { + bailout(kAtomics, "AtomicSub"); +} + +void LiftoffAssembler::AtomicAnd(Register dst_addr, Register offset_reg, + uint32_t offset_imm, LiftoffRegister value, + LiftoffRegister result, StoreType type) { + bailout(kAtomics, "AtomicAnd"); +} + +void LiftoffAssembler::AtomicOr(Register dst_addr, Register offset_reg, + uint32_t offset_imm, LiftoffRegister value, + LiftoffRegister result, StoreType type) { + bailout(kAtomics, "AtomicOr"); +} + +void LiftoffAssembler::AtomicXor(Register dst_addr, Register offset_reg, + uint32_t offset_imm, LiftoffRegister value, + LiftoffRegister result, StoreType type) { + bailout(kAtomics, "AtomicXor"); +} + +void LiftoffAssembler::AtomicExchange(Register dst_addr, Register offset_reg, + uint32_t offset_imm, + LiftoffRegister value, + LiftoffRegister result, StoreType type) { + bailout(kAtomics, "AtomicExchange"); +} + +void LiftoffAssembler::AtomicCompareExchange( + Register dst_addr, Register offset_reg, uint32_t offset_imm, + LiftoffRegister expected, LiftoffRegister new_value, LiftoffRegister result, + StoreType type) { + bailout(kAtomics, "AtomicCompareExchange"); +} + +void LiftoffAssembler::AtomicFence() { sync(); } + +void LiftoffAssembler::LoadCallerFrameSlot(LiftoffRegister dst, + uint32_t caller_slot_idx, + ValueType type) { + MemOperand src(fp, kSystemPointerSize * (caller_slot_idx + 1)); + liftoff::Load(this, dst, src, type); +} + +void LiftoffAssembler::StoreCallerFrameSlot(LiftoffRegister src, + uint32_t caller_slot_idx, + ValueType type) { + int32_t offset = kSystemPointerSize * (caller_slot_idx + 1); + liftoff::Store(this, fp, offset, src, type); +} + +void LiftoffAssembler::LoadReturnStackSlot(LiftoffRegister dst, int offset, + ValueType type) { + liftoff::Load(this, dst, MemOperand(sp, offset), type); +} + +void LiftoffAssembler::MoveStackValue(uint32_t dst_offset, uint32_t src_offset, + ValueType type) { + DCHECK_NE(dst_offset, src_offset); + LiftoffRegister reg = GetUnusedRegister(reg_class_for(type), {}); + Fill(reg, src_offset, type); + Spill(dst_offset, reg, type); +} + +void LiftoffAssembler::Move(Register dst, Register src, ValueType type) { + DCHECK_NE(dst, src); + // TODO(ksreten): Handle different sizes here. + TurboAssembler::Move(dst, src); +} + +void LiftoffAssembler::Move(DoubleRegister dst, DoubleRegister src, + ValueType type) { + DCHECK_NE(dst, src); + TurboAssembler::Move(dst, src); +} + +void LiftoffAssembler::Spill(int offset, LiftoffRegister reg, ValueType type) { + RecordUsedSpillOffset(offset); + MemOperand dst = liftoff::GetStackSlot(offset); + switch (type.kind()) { + case ValueType::kI32: + Sw(reg.gp(), dst); + break; + case ValueType::kI64: + Sd(reg.gp(), dst); + break; + case ValueType::kF32: + StoreFloat(reg.fp(), dst); + break; + case ValueType::kF64: + TurboAssembler::StoreDouble(reg.fp(), dst); + break; + default: + UNREACHABLE(); + } +} + +void LiftoffAssembler::Spill(int offset, WasmValue value) { + RecordUsedSpillOffset(offset); + MemOperand dst = liftoff::GetStackSlot(offset); + switch (value.type().kind()) { + case ValueType::kI32: { + LiftoffRegister tmp = GetUnusedRegister(kGpReg, {}); + TurboAssembler::li(tmp.gp(), Operand(value.to_i32())); + Sw(tmp.gp(), dst); + break; + } + case ValueType::kI64: { + LiftoffRegister tmp = GetUnusedRegister(kGpReg, {}); + TurboAssembler::li(tmp.gp(), value.to_i64()); + Sd(tmp.gp(), dst); + break; + } + default: + // kWasmF32 and kWasmF64 are unreachable, since those + // constants are not tracked. + UNREACHABLE(); + } +} + +void LiftoffAssembler::Fill(LiftoffRegister reg, int offset, ValueType type) { + MemOperand src = liftoff::GetStackSlot(offset); + switch (type.kind()) { + case ValueType::kI32: + Lw(reg.gp(), src); + break; + case ValueType::kI64: + Ld(reg.gp(), src); + break; + case ValueType::kF32: + LoadFloat(reg.fp(), src); + break; + case ValueType::kF64: + TurboAssembler::LoadDouble(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) { + Sd(zero_reg, liftoff::GetStackSlot(start + remainder)); + } + DCHECK(remainder == 4 || remainder == 0); + if (remainder) { + Sw(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); + Add64(a0, fp, Operand(-start - size)); + Add64(a1, fp, Operand(-start)); + + Label loop; + bind(&loop); + Sd(zero_reg, MemOperand(a0)); + addi(a0, a0, kSystemPointerSize); + BranchShort(&loop, ne, a0, Operand(a1)); + + Pop(a1, a0); + } +} + +void LiftoffAssembler::emit_i64_clz(LiftoffRegister dst, LiftoffRegister src) { + TurboAssembler::Clz64(dst.gp(), src.gp()); +} + +void LiftoffAssembler::emit_i64_ctz(LiftoffRegister dst, LiftoffRegister src) { + TurboAssembler::Ctz64(dst.gp(), src.gp()); +} + +bool LiftoffAssembler::emit_i64_popcnt(LiftoffRegister dst, + LiftoffRegister src) { + TurboAssembler::Popcnt64(dst.gp(), src.gp()); + return true; +} + +void LiftoffAssembler::emit_i32_mul(Register dst, Register lhs, Register rhs) { + TurboAssembler::Mul32(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::CompareI(kScratchReg, lhs, Operand(kMinInt), ne); + TurboAssembler::CompareI(kScratchReg2, rhs, Operand(-1), ne); + add(kScratchReg, kScratchReg, kScratchReg2); + TurboAssembler::Branch(trap_div_unrepresentable, eq, kScratchReg, + Operand(zero_reg)); + + TurboAssembler::Div32(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)); + TurboAssembler::Divu32(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)); + TurboAssembler::Mod32(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)); + TurboAssembler::Modu32(dst, lhs, rhs); +} + +#define I32_BINOP(name, instruction) \ + void LiftoffAssembler::emit_i32_##name(Register dst, Register lhs, \ + Register rhs) { \ + instruction(dst, lhs, rhs); \ + } + +// 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, Add32) +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::Clz32(dst, src); +} + +void LiftoffAssembler::emit_i32_ctz(Register dst, Register src) { + TurboAssembler::Ctz32(dst, src); +} + +bool LiftoffAssembler::emit_i32_popcnt(Register dst, Register src) { + TurboAssembler::Popcnt32(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) \ + void LiftoffAssembler::emit_i32_##name##i(Register dst, Register src, \ + int amount) { \ + instruction(dst, src, amount); \ + } + +I32_SHIFTOP(shl, sllw) +I32_SHIFTOP(sar, sraw) +I32_SHIFTOP(shr, srlw) + +I32_SHIFTOP_I(shl, slliw) +I32_SHIFTOP_I(sar, sraiw) +I32_SHIFTOP_I(shr, srliw) + +#undef I32_SHIFTOP +#undef I32_SHIFTOP_I + +void LiftoffAssembler::emit_i64_mul(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + TurboAssembler::Mul64(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) { + TurboAssembler::Branch(trap_div_by_zero, eq, rhs.gp(), Operand(zero_reg)); + + // Check if lhs == MinInt64 and rhs == -1, since this case is unrepresentable. + TurboAssembler::CompareI(kScratchReg, lhs.gp(), + Operand(std::numeric_limits::min()), ne); + TurboAssembler::CompareI(kScratchReg2, rhs.gp(), Operand(-1), ne); + add(kScratchReg, kScratchReg, kScratchReg2); + TurboAssembler::Branch(trap_div_unrepresentable, eq, kScratchReg, + Operand(zero_reg)); + + TurboAssembler::Div64(dst.gp(), lhs.gp(), rhs.gp()); + return true; +} + +bool LiftoffAssembler::emit_i64_divu(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs, + Label* trap_div_by_zero) { + TurboAssembler::Branch(trap_div_by_zero, eq, rhs.gp(), Operand(zero_reg)); + TurboAssembler::Divu64(dst.gp(), lhs.gp(), rhs.gp()); + return true; +} + +bool LiftoffAssembler::emit_i64_rems(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs, + Label* trap_div_by_zero) { + TurboAssembler::Branch(trap_div_by_zero, eq, rhs.gp(), Operand(zero_reg)); + TurboAssembler::Mod64(dst.gp(), lhs.gp(), rhs.gp()); + return true; +} + +bool LiftoffAssembler::emit_i64_remu(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs, + Label* trap_div_by_zero) { + TurboAssembler::Branch(trap_div_by_zero, eq, rhs.gp(), Operand(zero_reg)); + TurboAssembler::Modu64(dst.gp(), lhs.gp(), rhs.gp()); + return true; +} + +#define I64_BINOP(name, instruction) \ + void LiftoffAssembler::emit_i64_##name( \ + LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs) { \ + instruction(dst.gp(), lhs.gp(), rhs.gp()); \ + } + +// clang-format off +I64_BINOP(add, add) +I64_BINOP(sub, sub) +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(add, Add64) +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(dst.gp(), src.gp(), amount); \ + } +#define I64_SHIFTOP_I(name, instruction) \ + void LiftoffAssembler::emit_i64_##name##i(LiftoffRegister dst, \ + LiftoffRegister src, int amount) { \ + DCHECK(is_uint6(amount)); \ + instruction(dst.gp(), src.gp(), amount); \ + } + +I64_SHIFTOP(shl, sll) +I64_SHIFTOP(sar, sra) +I64_SHIFTOP(shr, srl) + +I64_SHIFTOP_I(shl, slli) +I64_SHIFTOP_I(sar, srai) +I64_SHIFTOP_I(shr, srli) + +#undef I64_SHIFTOP +#undef I64_SHIFTOP_I + +void LiftoffAssembler::emit_u32_to_intptr(Register dst, Register src) { + addw(dst, src, zero_reg); +} + +void LiftoffAssembler::emit_f32_neg(DoubleRegister dst, DoubleRegister src) { + TurboAssembler::Neg_s(dst, src); +} + +void LiftoffAssembler::emit_f64_neg(DoubleRegister dst, DoubleRegister src) { + TurboAssembler::Neg_d(dst, src); +} + +void LiftoffAssembler::emit_f32_min(DoubleRegister dst, DoubleRegister lhs, + DoubleRegister rhs) { + TurboAssembler::Float32Min(dst, lhs, rhs); +} + +void LiftoffAssembler::emit_f32_max(DoubleRegister dst, DoubleRegister lhs, + DoubleRegister rhs) { + TurboAssembler::Float32Max(dst, lhs, rhs); +} + +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) { + TurboAssembler::Float64Min(dst, lhs, rhs); +} + +void LiftoffAssembler::emit_f64_max(DoubleRegister dst, DoubleRegister lhs, + DoubleRegister rhs) { + TurboAssembler::Float64Max(dst, lhs, rhs); +} + +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(dst, lhs, rhs); \ + } +#define FP_UNOP(name, instruction) \ + void LiftoffAssembler::emit_##name(DoubleRegister dst, DoubleRegister src) { \ + instruction(dst, src); \ + } +#define FP_UNOP_RETURN_TRUE(name, instruction) \ + bool LiftoffAssembler::emit_##name(DoubleRegister dst, DoubleRegister src) { \ + instruction(dst, src, kScratchDoubleReg); \ + return true; \ + } + +FP_BINOP(f32_add, fadd_s) +FP_BINOP(f32_sub, fsub_s) +FP_BINOP(f32_mul, fmul_s) +FP_BINOP(f32_div, fdiv_s) +FP_UNOP(f32_abs, fabs_s) +FP_UNOP_RETURN_TRUE(f32_ceil, Ceil_s_s) +FP_UNOP_RETURN_TRUE(f32_floor, Floor_s_s) +FP_UNOP_RETURN_TRUE(f32_trunc, Trunc_s_s) +FP_UNOP_RETURN_TRUE(f32_nearest_int, Round_s_s) +FP_UNOP(f32_sqrt, fsqrt_s) +FP_BINOP(f64_add, fadd_d) +FP_BINOP(f64_sub, fsub_d) +FP_BINOP(f64_mul, fmul_d) +FP_BINOP(f64_div, fdiv_d) +FP_UNOP(f64_abs, fabs_d) +FP_UNOP_RETURN_TRUE(f64_ceil, Ceil_d_d) +FP_UNOP_RETURN_TRUE(f64_floor, Floor_d_d) +FP_UNOP_RETURN_TRUE(f64_trunc, Trunc_d_d) +FP_UNOP_RETURN_TRUE(f64_nearest_int, Round_d_d) +FP_UNOP(f64_sqrt, fsqrt_d) + +#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: + // According to WebAssembly spec, if I64 value does not fit the range of + // I32, the value is undefined. Therefore, We use sign extension to + // implement I64 to I32 truncation + TurboAssembler::SignExtendWord(dst.gp(), src.gp()); + return true; + case kExprI32SConvertF32: + case kExprI32UConvertF32: + case kExprI32SConvertF64: + case kExprI32UConvertF64: + case kExprI64SConvertF32: + case kExprI64UConvertF32: + case kExprI64SConvertF64: + case kExprI64UConvertF64: + case kExprF32ConvertF64: { + // real conversion, if src is out-of-bound of target integer types, + // kScratchReg is set to 0 + switch (opcode) { + case kExprI32SConvertF32: + Trunc_w_s(dst.gp(), src.fp(), kScratchReg); + break; + case kExprI32UConvertF32: + Trunc_uw_s(dst.gp(), src.fp(), kScratchReg); + break; + case kExprI32SConvertF64: + Trunc_w_d(dst.gp(), src.fp(), kScratchReg); + break; + case kExprI32UConvertF64: + Trunc_uw_d(dst.gp(), src.fp(), kScratchReg); + break; + case kExprI64SConvertF32: + Trunc_l_s(dst.gp(), src.fp(), kScratchReg); + break; + case kExprI64UConvertF32: + Trunc_ul_s(dst.gp(), src.fp(), kScratchReg); + break; + case kExprI64SConvertF64: + Trunc_l_d(dst.gp(), src.fp(), kScratchReg); + break; + case kExprI64UConvertF64: + Trunc_ul_d(dst.gp(), src.fp(), kScratchReg); + break; + case kExprF32ConvertF64: + fcvt_s_d(dst.fp(), src.fp()); + // FIXME (?): what if double cannot be represented by float? + // Trunc_s_d(dst.gp(), src.fp(), kScratchReg); + break; + default: + UNREACHABLE(); + } + + // Checking if trap. + TurboAssembler::Branch(trap, eq, kScratchReg, Operand(zero_reg)); + + return true; + } + case kExprI32ReinterpretF32: + TurboAssembler::ExtractLowWordFromF64(dst.gp(), src.fp()); + return true; + case kExprI64SConvertI32: + TurboAssembler::SignExtendWord(dst.gp(), src.gp()); + return true; + case kExprI64UConvertI32: + TurboAssembler::ZeroExtendWord(dst.gp(), src.gp()); + return true; + case kExprI64ReinterpretF64: + fmv_x_d(dst.gp(), src.fp()); + return true; + case kExprF32SConvertI32: { + TurboAssembler::Cvt_s_w(dst.fp(), src.gp()); + return true; + } + case kExprF32UConvertI32: + TurboAssembler::Cvt_s_uw(dst.fp(), src.gp()); + return true; + case kExprF32ReinterpretI32: + fmv_w_x(dst.fp(), src.gp()); + return true; + case kExprF64SConvertI32: { + TurboAssembler::Cvt_d_w(dst.fp(), src.gp()); + return true; + } + case kExprF64UConvertI32: + TurboAssembler::Cvt_d_uw(dst.fp(), src.gp()); + return true; + case kExprF64ConvertF32: + fcvt_d_s(dst.fp(), src.fp()); + return true; + case kExprF64ReinterpretI64: + fmv_d_x(dst.fp(), src.gp()); + 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) { + slliw(dst, src, 32 - 8); + sraiw(dst, dst, 32 - 8); +} + +void LiftoffAssembler::emit_i32_signextend_i16(Register dst, Register src) { + slliw(dst, src, 32 - 16); + sraiw(dst, dst, 32 - 16); +} + +void LiftoffAssembler::emit_i64_signextend_i8(LiftoffRegister dst, + LiftoffRegister src) { + slli(dst.gp(), src.gp(), 64 - 8); + srai(dst.gp(), dst.gp(), 64 - 8); +} + +void LiftoffAssembler::emit_i64_signextend_i16(LiftoffRegister dst, + LiftoffRegister src) { + slli(dst.gp(), src.gp(), 64 - 16); + srai(dst.gp(), dst.gp(), 64 - 16); +} + +void LiftoffAssembler::emit_i64_signextend_i32(LiftoffRegister dst, + LiftoffRegister src) { + slli(dst.gp(), src.gp(), 64 - 32); + srai(dst.gp(), dst.gp(), 64 - 32); +} + +void LiftoffAssembler::emit_jump(Label* label) { + TurboAssembler::Branch(label); +} + +void LiftoffAssembler::emit_jump(Register target) { + TurboAssembler::Jump(target); +} + +void LiftoffAssembler::emit_cond_jump(Condition cond, Label* label, + ValueType type, Register lhs, + Register rhs) { + if (rhs != no_reg) { + TurboAssembler::Branch(label, cond, lhs, Operand(rhs)); + } else { + TurboAssembler::Branch(label, cond, lhs, Operand(zero_reg)); + } +} + +void LiftoffAssembler::emit_i32_eqz(Register dst, Register src) { + TurboAssembler::Sltu(dst, src, 1); +} + +void LiftoffAssembler::emit_i32_set_cond(Condition cond, Register dst, + Register lhs, Register rhs) { + TurboAssembler::CompareI(dst, lhs, Operand(rhs), cond); +} + +void LiftoffAssembler::emit_i64_eqz(Register dst, LiftoffRegister src) { + TurboAssembler::Sltu(dst, src.gp(), 1); +} + +void LiftoffAssembler::emit_i64_set_cond(Condition cond, Register dst, + LiftoffRegister lhs, + LiftoffRegister rhs) { + TurboAssembler::CompareI(dst, lhs.gp(), Operand(rhs.gp()), cond); +} + +static FPUCondition ConditionToConditionCmpFPU(Condition condition) { + switch (condition) { + case kEqual: + return EQ; + case kUnequal: + return NE; + case kUnsignedLessThan: + return LT; + case kUnsignedGreaterEqual: + return GE; + case kUnsignedLessEqual: + return LE; + case kUnsignedGreaterThan: + return GT; + default: + break; + } + UNREACHABLE(); +} + +void LiftoffAssembler::emit_f32_set_cond(Condition cond, Register dst, + DoubleRegister lhs, + DoubleRegister rhs) { + FPUCondition fcond = ConditionToConditionCmpFPU(cond); + TurboAssembler::CompareF32(dst, fcond, lhs, rhs); +} + +void LiftoffAssembler::emit_f64_set_cond(Condition cond, Register dst, + DoubleRegister lhs, + DoubleRegister rhs) { + FPUCondition fcond = ConditionToConditionCmpFPU(cond); + TurboAssembler::CompareF64(dst, fcond, lhs, rhs); +} + +bool LiftoffAssembler::emit_select(LiftoffRegister dst, Register condition, + LiftoffRegister true_value, + LiftoffRegister false_value, + ValueType type) { + return false; +} + +void LiftoffAssembler::LoadTransform(LiftoffRegister dst, Register src_addr, + Register offset_reg, uint32_t offset_imm, + LoadType type, + LoadTransformationKind transform, + uint32_t* protected_load_pc) { + bailout(kSimd, "load extend and load splat unimplemented"); +} + +void LiftoffAssembler::emit_i8x16_shuffle(LiftoffRegister dst, + LiftoffRegister lhs, + LiftoffRegister rhs, + const uint8_t shuffle[16], + bool is_swizzle) { + bailout(kSimd, "emit_i8x16_shuffle"); +} + +void LiftoffAssembler::emit_i8x16_swizzle(LiftoffRegister dst, + LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i8x16_swizzle"); +} + +void LiftoffAssembler::emit_i8x16_splat(LiftoffRegister dst, + LiftoffRegister src) { + bailout(kSimd, "emit_i8x16_splat"); +} + +void LiftoffAssembler::emit_i16x8_splat(LiftoffRegister dst, + LiftoffRegister src) { + bailout(kSimd, "emit_i16x8_splat"); +} + +void LiftoffAssembler::emit_i32x4_splat(LiftoffRegister dst, + LiftoffRegister src) { + bailout(kSimd, "emit_i32x4_splat"); +} + +void LiftoffAssembler::emit_i64x2_splat(LiftoffRegister dst, + LiftoffRegister src) { + bailout(kSimd, "emit_i64x2_splat"); +} + +void LiftoffAssembler::emit_f32x4_splat(LiftoffRegister dst, + LiftoffRegister src) { + bailout(kSimd, "emit_f32x4_splat"); +} + +void LiftoffAssembler::emit_f64x2_splat(LiftoffRegister dst, + LiftoffRegister src) { + bailout(kSimd, "emit_f64x2_splat"); +} + +void LiftoffAssembler::emit_i8x16_eq(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i8x16_eq"); +} + +void LiftoffAssembler::emit_i8x16_ne(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i8x16_ne"); +} + +void LiftoffAssembler::emit_i8x16_gt_s(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i8x16_gt_s"); +} + +void LiftoffAssembler::emit_i8x16_gt_u(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i8x16_gt_u"); +} + +void LiftoffAssembler::emit_i8x16_ge_s(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i8x16_ge_s"); +} + +void LiftoffAssembler::emit_i8x16_ge_u(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i8x16_ge_u"); +} + +void LiftoffAssembler::emit_i16x8_eq(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i16x8_eq"); +} + +void LiftoffAssembler::emit_i16x8_ne(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i16x8_ne"); +} + +void LiftoffAssembler::emit_i16x8_gt_s(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i16x8_gt_s"); +} + +void LiftoffAssembler::emit_i16x8_gt_u(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i16x8_gt_u"); +} + +void LiftoffAssembler::emit_i16x8_ge_s(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i16x8_ge_s"); +} + +void LiftoffAssembler::emit_i16x8_ge_u(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i16x8_ge_u"); +} + +void LiftoffAssembler::emit_i32x4_eq(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i32x4_eq"); +} + +void LiftoffAssembler::emit_i32x4_ne(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i32x4_ne"); +} + +void LiftoffAssembler::emit_i32x4_gt_s(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i32x4_gt_s"); +} + +void LiftoffAssembler::emit_i32x4_gt_u(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i32x4_gt_u"); +} + +void LiftoffAssembler::emit_i32x4_ge_s(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i32x4_ge_s"); +} + +void LiftoffAssembler::emit_i32x4_ge_u(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i32x4_ge_u"); +} + +void LiftoffAssembler::emit_f32x4_eq(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_f32x4_eq"); +} + +void LiftoffAssembler::emit_f32x4_ne(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_f32x4_ne"); +} + +void LiftoffAssembler::emit_f32x4_lt(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_f32x4_lt"); +} + +void LiftoffAssembler::emit_f32x4_le(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_f32x4_le"); +} + +void LiftoffAssembler::emit_f64x2_eq(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_f64x2_eq"); +} + +void LiftoffAssembler::emit_f64x2_ne(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_f64x2_ne"); +} + +void LiftoffAssembler::emit_f64x2_lt(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_f64x2_lt"); +} + +void LiftoffAssembler::emit_f64x2_le(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_f64x2_le"); +} + +void LiftoffAssembler::emit_s128_const(LiftoffRegister dst, + const uint8_t imms[16]) { + bailout(kSimd, "emit_s128_const"); +} + +void LiftoffAssembler::emit_s128_not(LiftoffRegister dst, LiftoffRegister src) { + bailout(kSimd, "emit_s128_not"); +} + +void LiftoffAssembler::emit_s128_and(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_s128_and"); +} + +void LiftoffAssembler::emit_s128_or(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_s128_or"); +} + +void LiftoffAssembler::emit_s128_xor(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_s128_xor"); +} + +void LiftoffAssembler::emit_s128_and_not(LiftoffRegister dst, + LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_s128_and_not"); +} + +void LiftoffAssembler::emit_s128_select(LiftoffRegister dst, + LiftoffRegister src1, + LiftoffRegister src2, + LiftoffRegister mask) { + bailout(kSimd, "emit_s128_select"); +} + +void LiftoffAssembler::emit_i8x16_neg(LiftoffRegister dst, + LiftoffRegister src) { + bailout(kSimd, "emit_i8x16_neg"); +} + +void LiftoffAssembler::emit_v8x16_anytrue(LiftoffRegister dst, + LiftoffRegister src) { + bailout(kSimd, "emit_i8x16_anytrue"); +} + +void LiftoffAssembler::emit_v8x16_alltrue(LiftoffRegister dst, + LiftoffRegister src) { + bailout(kSimd, "emit_i8x16_alltrue"); +} + +void LiftoffAssembler::emit_i8x16_bitmask(LiftoffRegister dst, + LiftoffRegister src) { + bailout(kSimd, "emit_i8x16_bitmask"); +} + +void LiftoffAssembler::emit_i8x16_shl(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i8x16_shl"); +} + +void LiftoffAssembler::emit_i8x16_shli(LiftoffRegister dst, LiftoffRegister lhs, + int32_t rhs) { + bailout(kSimd, "emit_i8x16_shli"); +} + +void LiftoffAssembler::emit_i8x16_shr_s(LiftoffRegister dst, + LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i8x16_shr_s"); +} + +void LiftoffAssembler::emit_i8x16_shri_s(LiftoffRegister dst, + LiftoffRegister lhs, int32_t rhs) { + bailout(kSimd, "emit_i8x16_shri_s"); +} + +void LiftoffAssembler::emit_i8x16_shr_u(LiftoffRegister dst, + LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i8x16_shr_u"); +} + +void LiftoffAssembler::emit_i8x16_shri_u(LiftoffRegister dst, + LiftoffRegister lhs, int32_t rhs) { + bailout(kSimd, "emit_i8x16_shri_u"); +} + +void LiftoffAssembler::emit_i8x16_add(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i8x16_add"); +} + +void LiftoffAssembler::emit_i8x16_add_saturate_s(LiftoffRegister dst, + LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i8x16_add_saturate_s"); +} + +void LiftoffAssembler::emit_i8x16_add_saturate_u(LiftoffRegister dst, + LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i8x16_add_saturate_u"); +} + +void LiftoffAssembler::emit_i8x16_sub(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i8x16_sub"); +} + +void LiftoffAssembler::emit_i8x16_sub_saturate_s(LiftoffRegister dst, + LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i8x16_sub_saturate_s"); +} + +void LiftoffAssembler::emit_i8x16_sub_saturate_u(LiftoffRegister dst, + LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i8x16_sub_saturate_u"); +} + +void LiftoffAssembler::emit_i8x16_mul(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i8x16_mul"); +} + +void LiftoffAssembler::emit_i8x16_min_s(LiftoffRegister dst, + LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i8x16_min_s"); +} + +void LiftoffAssembler::emit_i8x16_min_u(LiftoffRegister dst, + LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i8x16_min_u"); +} + +void LiftoffAssembler::emit_i8x16_max_s(LiftoffRegister dst, + LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i8x16_max_s"); +} + +void LiftoffAssembler::emit_i8x16_max_u(LiftoffRegister dst, + LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i8x16_max_u"); +} + +void LiftoffAssembler::emit_i16x8_neg(LiftoffRegister dst, + LiftoffRegister src) { + bailout(kSimd, "emit_i16x8_neg"); +} + +void LiftoffAssembler::emit_v16x8_anytrue(LiftoffRegister dst, + LiftoffRegister src) { + bailout(kSimd, "emit_i16x8_anytrue"); +} + +void LiftoffAssembler::emit_v16x8_alltrue(LiftoffRegister dst, + LiftoffRegister src) { + bailout(kSimd, "emit_i16x8_alltrue"); +} + +void LiftoffAssembler::emit_i16x8_bitmask(LiftoffRegister dst, + LiftoffRegister src) { + bailout(kSimd, "emit_i16x8_bitmask"); +} + +void LiftoffAssembler::emit_i16x8_shl(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i16x8_shl"); +} + +void LiftoffAssembler::emit_i16x8_shli(LiftoffRegister dst, LiftoffRegister lhs, + int32_t rhs) { + bailout(kSimd, "emit_i16x8_shli"); +} + +void LiftoffAssembler::emit_i16x8_shr_s(LiftoffRegister dst, + LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i16x8_shr_s"); +} + +void LiftoffAssembler::emit_i16x8_shri_s(LiftoffRegister dst, + LiftoffRegister lhs, int32_t rhs) { + bailout(kSimd, "emit_i16x8_shri_s"); +} + +void LiftoffAssembler::emit_i16x8_shr_u(LiftoffRegister dst, + LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i16x8_shr_u"); +} + +void LiftoffAssembler::emit_i16x8_shri_u(LiftoffRegister dst, + LiftoffRegister lhs, int32_t rhs) { + bailout(kSimd, "emit_i16x8_shri_u"); +} + +void LiftoffAssembler::emit_i16x8_add(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i16x8_add"); +} + +void LiftoffAssembler::emit_i16x8_add_saturate_s(LiftoffRegister dst, + LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i16x8_add_saturate_s"); +} + +void LiftoffAssembler::emit_i16x8_add_saturate_u(LiftoffRegister dst, + LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i16x8_add_saturate_u"); +} + +void LiftoffAssembler::emit_i16x8_sub(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i16x8_sub"); +} + +void LiftoffAssembler::emit_i16x8_sub_saturate_s(LiftoffRegister dst, + LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i16x8_sub_saturate_s"); +} + +void LiftoffAssembler::emit_i16x8_sub_saturate_u(LiftoffRegister dst, + LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i16x8_sub_saturate_u"); +} + +void LiftoffAssembler::emit_i16x8_mul(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i16x8_mul"); +} + +void LiftoffAssembler::emit_i16x8_min_s(LiftoffRegister dst, + LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i16x8_min_s"); +} + +void LiftoffAssembler::emit_i16x8_min_u(LiftoffRegister dst, + LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i16x8_min_u"); +} + +void LiftoffAssembler::emit_i16x8_max_s(LiftoffRegister dst, + LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i16x8_max_s"); +} + +void LiftoffAssembler::emit_i16x8_max_u(LiftoffRegister dst, + LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i16x8_max_u"); +} + +void LiftoffAssembler::emit_i32x4_neg(LiftoffRegister dst, + LiftoffRegister src) { + bailout(kSimd, "emit_i32x4_neg"); +} + +void LiftoffAssembler::emit_v32x4_anytrue(LiftoffRegister dst, + LiftoffRegister src) { + bailout(kSimd, "emit_v32x4_anytrue"); +} + +void LiftoffAssembler::emit_v32x4_alltrue(LiftoffRegister dst, + LiftoffRegister src) { + bailout(kSimd, "emit_v32x4_alltrue"); +} + +void LiftoffAssembler::emit_i32x4_bitmask(LiftoffRegister dst, + LiftoffRegister src) { + bailout(kSimd, "emit_i32x4_bitmask"); +} + +void LiftoffAssembler::emit_i32x4_shl(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i32x4_shl"); +} + +void LiftoffAssembler::emit_i32x4_shli(LiftoffRegister dst, LiftoffRegister lhs, + int32_t rhs) { + bailout(kSimd, "emit_i32x4_shli"); +} + +void LiftoffAssembler::emit_i32x4_shr_s(LiftoffRegister dst, + LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i32x4_shr_s"); +} + +void LiftoffAssembler::emit_i32x4_shri_s(LiftoffRegister dst, + LiftoffRegister lhs, int32_t rhs) { + bailout(kSimd, "emit_i32x4_shri_s"); +} + +void LiftoffAssembler::emit_i32x4_shr_u(LiftoffRegister dst, + LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i32x4_shr_u"); +} + +void LiftoffAssembler::emit_i32x4_shri_u(LiftoffRegister dst, + LiftoffRegister lhs, int32_t rhs) { + bailout(kSimd, "emit_i32x4_shri_u"); +} + +void LiftoffAssembler::emit_i32x4_add(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i32x4_add"); +} + +void LiftoffAssembler::emit_i32x4_sub(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i32x4_sub"); +} + +void LiftoffAssembler::emit_i32x4_mul(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i32x4_mul"); +} + +void LiftoffAssembler::emit_i32x4_min_s(LiftoffRegister dst, + LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i32x4_min_s"); +} + +void LiftoffAssembler::emit_i32x4_min_u(LiftoffRegister dst, + LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i32x4_min_u"); +} + +void LiftoffAssembler::emit_i32x4_max_s(LiftoffRegister dst, + LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i32x4_max_s"); +} + +void LiftoffAssembler::emit_i32x4_max_u(LiftoffRegister dst, + LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i32x4_max_u"); +} + +void LiftoffAssembler::emit_i64x2_neg(LiftoffRegister dst, + LiftoffRegister src) { + bailout(kSimd, "emit_i64x2_neg"); +} + +void LiftoffAssembler::emit_i64x2_shl(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i64x2_shl"); +} + +void LiftoffAssembler::emit_i64x2_shli(LiftoffRegister dst, LiftoffRegister lhs, + int32_t rhs) { + bailout(kSimd, "emit_i64x2_shli"); +} + +void LiftoffAssembler::emit_i64x2_shr_s(LiftoffRegister dst, + LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i64x2_shr_s"); +} + +void LiftoffAssembler::emit_i64x2_shri_s(LiftoffRegister dst, + LiftoffRegister lhs, int32_t rhs) { + bailout(kSimd, "emit_i64x2_shri_s"); +} + +void LiftoffAssembler::emit_i64x2_shr_u(LiftoffRegister dst, + LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i64x2_shr_u"); +} + +void LiftoffAssembler::emit_i64x2_shri_u(LiftoffRegister dst, + LiftoffRegister lhs, int32_t rhs) { + bailout(kSimd, "emit_i64x2_shri_u"); +} + +void LiftoffAssembler::emit_i64x2_add(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i64x2_add"); +} + +void LiftoffAssembler::emit_i64x2_sub(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i64x2_sub"); +} + +void LiftoffAssembler::emit_i64x2_mul(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i64x2_mul"); +} + +void LiftoffAssembler::emit_f32x4_abs(LiftoffRegister dst, + LiftoffRegister src) { + bailout(kSimd, "emit_f32x4_abs"); +} + +void LiftoffAssembler::emit_f32x4_neg(LiftoffRegister dst, + LiftoffRegister src) { + bailout(kSimd, "emit_f32x4_neg"); +} + +void LiftoffAssembler::emit_f32x4_sqrt(LiftoffRegister dst, + LiftoffRegister src) { + bailout(kSimd, "emit_f32x4_sqrt"); +} + +bool LiftoffAssembler::emit_f32x4_ceil(LiftoffRegister dst, + LiftoffRegister src) { + bailout(kSimd, "emit_f32x4_ceil"); + return true; +} + +bool LiftoffAssembler::emit_f32x4_floor(LiftoffRegister dst, + LiftoffRegister src) { + bailout(kSimd, "emit_f32x4_floor"); + return true; +} + +bool LiftoffAssembler::emit_f32x4_trunc(LiftoffRegister dst, + LiftoffRegister src) { + bailout(kSimd, "emit_f32x4_trunc"); + return true; +} + +bool LiftoffAssembler::emit_f32x4_nearest_int(LiftoffRegister dst, + LiftoffRegister src) { + bailout(kSimd, "emit_f32x4_nearest_int"); + return true; +} + +void LiftoffAssembler::emit_f32x4_add(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_f32x4_add"); +} + +void LiftoffAssembler::emit_f32x4_sub(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_f32x4_sub"); +} + +void LiftoffAssembler::emit_f32x4_mul(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_f32x4_mul"); +} + +void LiftoffAssembler::emit_f32x4_div(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_f32x4_div"); +} + +void LiftoffAssembler::emit_f32x4_min(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_f32x4_min"); +} + +void LiftoffAssembler::emit_f32x4_max(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_f32x4_max"); +} + +void LiftoffAssembler::emit_f32x4_pmin(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_f32x4_pmin"); +} + +void LiftoffAssembler::emit_f32x4_pmax(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_f32x4_pmax"); +} + +void LiftoffAssembler::emit_f64x2_abs(LiftoffRegister dst, + LiftoffRegister src) { + bailout(kSimd, "emit_f64x2_abs"); +} + +void LiftoffAssembler::emit_f64x2_neg(LiftoffRegister dst, + LiftoffRegister src) { + bailout(kSimd, "emit_f64x2_neg"); +} + +void LiftoffAssembler::emit_f64x2_sqrt(LiftoffRegister dst, + LiftoffRegister src) { + bailout(kSimd, "emit_f64x2_sqrt"); +} + +bool LiftoffAssembler::emit_f64x2_ceil(LiftoffRegister dst, + LiftoffRegister src) { + bailout(kSimd, "emit_f64x2_ceil"); + return true; +} + +bool LiftoffAssembler::emit_f64x2_floor(LiftoffRegister dst, + LiftoffRegister src) { + bailout(kSimd, "emit_f64x2_floor"); + return true; +} + +bool LiftoffAssembler::emit_f64x2_trunc(LiftoffRegister dst, + LiftoffRegister src) { + bailout(kSimd, "emit_f64x2_trunc"); + return true; +} + +bool LiftoffAssembler::emit_f64x2_nearest_int(LiftoffRegister dst, + LiftoffRegister src) { + bailout(kSimd, "emit_f64x2_nearest_int"); + return true; +} + +void LiftoffAssembler::emit_f64x2_add(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_f64x2_add"); +} + +void LiftoffAssembler::emit_f64x2_sub(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_f64x2_sub"); +} + +void LiftoffAssembler::emit_f64x2_mul(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_f64x2_mul"); +} + +void LiftoffAssembler::emit_f64x2_div(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_f64x2_div"); +} + +void LiftoffAssembler::emit_f64x2_min(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_f64x2_min"); +} + +void LiftoffAssembler::emit_f64x2_max(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_f64x2_max"); +} + +void LiftoffAssembler::emit_f64x2_pmin(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_f64x2_pmin"); +} + +void LiftoffAssembler::emit_f64x2_pmax(LiftoffRegister dst, LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_f64x2_pmax"); +} + +void LiftoffAssembler::emit_i32x4_sconvert_f32x4(LiftoffRegister dst, + LiftoffRegister src) { + bailout(kSimd, "emit_i32x4_sconvert_f32x4"); +} + +void LiftoffAssembler::emit_i32x4_uconvert_f32x4(LiftoffRegister dst, + LiftoffRegister src) { + bailout(kSimd, "emit_i32x4_uconvert_f32x4"); +} + +void LiftoffAssembler::emit_f32x4_sconvert_i32x4(LiftoffRegister dst, + LiftoffRegister src) { + bailout(kSimd, "emit_f32x4_sconvert_i32x4"); +} + +void LiftoffAssembler::emit_f32x4_uconvert_i32x4(LiftoffRegister dst, + LiftoffRegister src) { + bailout(kSimd, "emit_f32x4_uconvert_i32x4"); +} + +void LiftoffAssembler::emit_i8x16_sconvert_i16x8(LiftoffRegister dst, + LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i8x16_sconvert_i16x8"); +} + +void LiftoffAssembler::emit_i8x16_uconvert_i16x8(LiftoffRegister dst, + LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i8x16_uconvert_i16x8"); +} + +void LiftoffAssembler::emit_i16x8_sconvert_i32x4(LiftoffRegister dst, + LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i16x8_sconvert_i32x4"); +} + +void LiftoffAssembler::emit_i16x8_uconvert_i32x4(LiftoffRegister dst, + LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i16x8_uconvert_i32x4"); +} + +void LiftoffAssembler::emit_i16x8_sconvert_i8x16_low(LiftoffRegister dst, + LiftoffRegister src) { + bailout(kSimd, "emit_i16x8_sconvert_i8x16_low"); +} + +void LiftoffAssembler::emit_i16x8_sconvert_i8x16_high(LiftoffRegister dst, + LiftoffRegister src) { + bailout(kSimd, "emit_i16x8_sconvert_i8x16_high"); +} + +void LiftoffAssembler::emit_i16x8_uconvert_i8x16_low(LiftoffRegister dst, + LiftoffRegister src) { + bailout(kSimd, "emit_i16x8_uconvert_i8x16_low"); +} + +void LiftoffAssembler::emit_i16x8_uconvert_i8x16_high(LiftoffRegister dst, + LiftoffRegister src) { + bailout(kSimd, "emit_i16x8_uconvert_i8x16_high"); +} + +void LiftoffAssembler::emit_i32x4_sconvert_i16x8_low(LiftoffRegister dst, + LiftoffRegister src) { + bailout(kSimd, "emit_i32x4_sconvert_i16x8_low"); +} + +void LiftoffAssembler::emit_i32x4_sconvert_i16x8_high(LiftoffRegister dst, + LiftoffRegister src) { + bailout(kSimd, "emit_i32x4_sconvert_i16x8_high"); +} + +void LiftoffAssembler::emit_i32x4_uconvert_i16x8_low(LiftoffRegister dst, + LiftoffRegister src) { + bailout(kSimd, "emit_i32x4_uconvert_i16x8_low"); +} + +void LiftoffAssembler::emit_i32x4_uconvert_i16x8_high(LiftoffRegister dst, + LiftoffRegister src) { + bailout(kSimd, "emit_i32x4_uconvert_i16x8_high"); +} + +void LiftoffAssembler::emit_i8x16_rounding_average_u(LiftoffRegister dst, + LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i8x16_rounding_average_u"); +} + +void LiftoffAssembler::emit_i16x8_rounding_average_u(LiftoffRegister dst, + LiftoffRegister lhs, + LiftoffRegister rhs) { + bailout(kSimd, "emit_i16x8_rounding_average_u"); +} + +void LiftoffAssembler::emit_i8x16_abs(LiftoffRegister dst, + LiftoffRegister src) { + bailout(kSimd, "emit_i8x16_abs"); +} + +void LiftoffAssembler::emit_i16x8_abs(LiftoffRegister dst, + LiftoffRegister src) { + bailout(kSimd, "emit_i16x8_abs"); +} + +void LiftoffAssembler::emit_i32x4_abs(LiftoffRegister dst, + LiftoffRegister src) { + bailout(kSimd, "emit_i32x4_abs"); +} + +void LiftoffAssembler::emit_i8x16_extract_lane_s(LiftoffRegister dst, + LiftoffRegister lhs, + uint8_t imm_lane_idx) { + bailout(kSimd, "emit_i8x16_extract_lane_s"); +} + +void LiftoffAssembler::emit_i8x16_extract_lane_u(LiftoffRegister dst, + LiftoffRegister lhs, + uint8_t imm_lane_idx) { + bailout(kSimd, "emit_i8x16_extract_lane_u"); +} + +void LiftoffAssembler::emit_i16x8_extract_lane_s(LiftoffRegister dst, + LiftoffRegister lhs, + uint8_t imm_lane_idx) { + bailout(kSimd, "emit_i16x8_extract_lane_s"); +} + +void LiftoffAssembler::emit_i16x8_extract_lane_u(LiftoffRegister dst, + LiftoffRegister lhs, + uint8_t imm_lane_idx) { + bailout(kSimd, "emit_i16x8_extract_lane_u"); +} + +void LiftoffAssembler::emit_i32x4_extract_lane(LiftoffRegister dst, + LiftoffRegister lhs, + uint8_t imm_lane_idx) { + bailout(kSimd, "emit_i32x4_extract_lane"); +} + +void LiftoffAssembler::emit_i64x2_extract_lane(LiftoffRegister dst, + LiftoffRegister lhs, + uint8_t imm_lane_idx) { + bailout(kSimd, "emit_i64x2_extract_lane"); +} + +void LiftoffAssembler::emit_f32x4_extract_lane(LiftoffRegister dst, + LiftoffRegister lhs, + uint8_t imm_lane_idx) { + bailout(kSimd, "emit_f32x4_extract_lane"); +} + +void LiftoffAssembler::emit_f64x2_extract_lane(LiftoffRegister dst, + LiftoffRegister lhs, + uint8_t imm_lane_idx) { + bailout(kSimd, "emit_f64x2_extract_lane"); +} + +void LiftoffAssembler::emit_i8x16_replace_lane(LiftoffRegister dst, + LiftoffRegister src1, + LiftoffRegister src2, + uint8_t imm_lane_idx) { + bailout(kSimd, "emit_i8x16_replace_lane"); +} + +void LiftoffAssembler::emit_i16x8_replace_lane(LiftoffRegister dst, + LiftoffRegister src1, + LiftoffRegister src2, + uint8_t imm_lane_idx) { + bailout(kSimd, "emit_i16x8_replace_lane"); +} + +void LiftoffAssembler::emit_i32x4_replace_lane(LiftoffRegister dst, + LiftoffRegister src1, + LiftoffRegister src2, + uint8_t imm_lane_idx) { + bailout(kSimd, "emit_i32x4_replace_lane"); +} + +void LiftoffAssembler::emit_i64x2_replace_lane(LiftoffRegister dst, + LiftoffRegister src1, + LiftoffRegister src2, + uint8_t imm_lane_idx) { + bailout(kSimd, "emit_i64x2_replace_lane"); +} + +void LiftoffAssembler::emit_f32x4_replace_lane(LiftoffRegister dst, + LiftoffRegister src1, + LiftoffRegister src2, + uint8_t imm_lane_idx) { + bailout(kSimd, "emit_f32x4_replace_lane"); +} + +void LiftoffAssembler::emit_f64x2_replace_lane(LiftoffRegister dst, + LiftoffRegister src1, + LiftoffRegister src2, + uint8_t imm_lane_idx) { + bailout(kSimd, "emit_f64x2_replace_lane"); +} + +void LiftoffAssembler::StackCheck(Label* ool_code, Register limit_address) { + TurboAssembler::Uld(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 (emit_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; + Add64(sp, sp, Operand(-offset)); + while (!gp_regs.is_empty()) { + LiftoffRegister reg = gp_regs.GetFirstRegSet(); + offset -= kSystemPointerSize; + Sd(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) { + Add64(sp, sp, Operand(-(num_fp_regs * kStackSlotSize))); + unsigned offset = 0; + while (!fp_regs.is_empty()) { + LiftoffRegister reg = fp_regs.GetFirstRegSet(); + TurboAssembler::StoreDouble(reg.fp(), MemOperand(sp, offset)); + fp_regs.clear(reg); + offset += sizeof(double); + } + DCHECK_EQ(offset, num_fp_regs * sizeof(double)); + } +} + +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::LoadDouble(reg.fp(), MemOperand(sp, fp_offset)); + fp_regs.clear(reg); + fp_offset += sizeof(double); + } + if (fp_offset) Add64(sp, sp, Operand(fp_offset)); + LiftoffRegList gp_regs = regs & kGpCacheRegList; + unsigned gp_offset = 0; + while (!gp_regs.is_empty()) { + LiftoffRegister reg = gp_regs.GetLastRegSet(); + Ld(reg.gp(), MemOperand(sp, gp_offset)); + gp_regs.clear(reg); + gp_offset += kSystemPointerSize; + } + Add64(sp, sp, Operand(gp_offset)); +} + +void LiftoffAssembler::DropStackSlotsAndRet(uint32_t num_stack_slots) { + TurboAssembler::DropAndRet(static_cast(num_stack_slots)); +} + +void LiftoffAssembler::CallC(const wasm::FunctionSig* sig, + const LiftoffRegister* args, + const LiftoffRegister* rets, + ValueType out_argument_type, int stack_bytes, + ExternalReference ext_ref) { + Add64(sp, sp, Operand(-stack_bytes)); + + int arg_bytes = 0; + for (ValueType param_type : sig->parameters()) { + liftoff::Store(this, sp, arg_bytes, *args++, param_type); + arg_bytes += param_type.element_size_bytes(); + } + DCHECK_LE(arg_bytes, stack_bytes); + + // Pass a pointer to the buffer with the arguments to the C function. + // On RISC-V, the first argument is passed in {a0}. + constexpr Register kFirstArgReg = a0; + mv(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 = a0; + 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_type != kWasmStmt) { + liftoff::Load(this, *next_result_reg, MemOperand(sp, 0), out_argument_type); + } + + Add64(sp, sp, Operand(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 wasm::FunctionSig* 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) { + Add64(sp, sp, Operand(-size)); + TurboAssembler::Move(addr, sp); +} + +void LiftoffAssembler::DeallocateStackSlot(uint32_t size) { + Add64(sp, sp, Operand(size)); +} + +void LiftoffStackSlots::Construct() { + for (auto& slot : slots_) { + const LiftoffAssembler::VarState& src = slot.src_; + switch (src.loc()) { + case LiftoffAssembler::VarState::kStack: + asm_->Ld(kScratchReg, liftoff::GetStackSlot(slot.src_offset_)); + asm_->push(kScratchReg); + break; + case LiftoffAssembler::VarState::kRegister: + liftoff::push(asm_, src.reg(), src.type()); + break; + case LiftoffAssembler::VarState::kIntConst: { + asm_->li(kScratchReg, Operand(src.i32_const())); + asm_->push(kScratchReg); + break; + } + } + } +} + +} // namespace wasm +} // namespace internal +} // namespace v8 + +#endif // V8_WASM_BASELINE_RISCV_LIFTOFF_ASSEMBLER_RISCV_H_ Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/wasm/jump-table-assembler.cc =================================================================== --- qtwebengine-everywhere-src-5.15.3.orig/src/3rdparty/chromium/v8/src/wasm/jump-table-assembler.cc +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/wasm/jump-table-assembler.cc @@ -307,6 +307,40 @@ void JumpTableAssembler::NopBytes(int by } } +#elif V8_TARGET_ARCH_RISCV64 +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. 9 instructions (8 for li + 1 for jr) + Jump(lazy_compile_target, RelocInfo::NONE); + 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) { + JumpToInstructionStream(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(); + } +} + #else #error Unknown architecture. #endif Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/wasm/jump-table-assembler.h =================================================================== --- qtwebengine-everywhere-src-5.15.3.orig/src/3rdparty/chromium/v8/src/wasm/jump-table-assembler.h +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/wasm/jump-table-assembler.h @@ -215,6 +215,11 @@ class V8_EXPORT_PRIVATE JumpTableAssembl static constexpr int kJumpTableSlotSize = 8 * kInstrSize; static constexpr int kFarJumpTableSlotSize = 6 * kInstrSize; static constexpr int kLazyCompileTableSlotSize = 8 * kInstrSize; +#elif V8_TARGET_ARCH_RISCV64 + static constexpr int kJumpTableLineSize = 6 * kInstrSize; + static constexpr int kJumpTableSlotSize = 6 * kInstrSize; + static constexpr int kFarJumpTableSlotSize = 9 * kInstrSize; + static constexpr int kLazyCompileTableSlotSize = 9 * kInstrSize; #else #error Unknown architecture. #endif Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/wasm/wasm-linkage.h =================================================================== --- qtwebengine-everywhere-src-5.15.3.orig/src/3rdparty/chromium/v8/src/wasm/wasm-linkage.h +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/wasm/wasm-linkage.h @@ -102,6 +102,22 @@ constexpr Register kGpReturnRegisters[] constexpr DoubleRegister kFpParamRegisters[] = {d0, d2}; constexpr DoubleRegister kFpReturnRegisters[] = {d0, d2}; +#elif V8_TARGET_ARCH_RISCV64 +// =========================================================================== +// == riscv64 ================================================================= +// =========================================================================== +// Note that kGpParamRegisters and kFpParamRegisters are used in +// Builtins::Generate_WasmCompileLazy (builtins-riscv64.cc) +constexpr Register kGpParamRegisters[] = {a0, a2, a3, a4, a5, a6, a7}; +constexpr Register kGpReturnRegisters[] = {a0, a1}; +constexpr DoubleRegister kFpParamRegisters[] = {fa0, fa1, fa2, fa3, + fa4, fa5, fa6}; +constexpr DoubleRegister kFpReturnRegisters[] = {fa0, fa1}; + +#elif V8_TARGET_ARCH_RISCV + +#error RISCV(32) architecture not supported + #else // =========================================================================== // == unknown ================================================================ Index: qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/wasm/wasm-serialization.cc =================================================================== --- qtwebengine-everywhere-src-5.15.3.orig/src/3rdparty/chromium/v8/src/wasm/wasm-serialization.cc +++ qtwebengine-everywhere-src-5.15.3/src/3rdparty/chromium/v8/src/wasm/wasm-serialization.cc @@ -368,7 +368,8 @@ bool NativeModuleSerializer::WriteCode(c writer->WriteVector(code->source_positions()); writer->WriteVector(code->protected_instructions_data()); #if V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_ARM || \ - V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_PPC64 || V8_TARGET_ARCH_S390X + V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_PPC64 || V8_TARGET_ARCH_S390X || \ + V8_TARGET_ARCH_RISCV64 || V8_TARGET_ARCH_RISCV32 // On platforms that don't support misaligned word stores, copy to an aligned // buffer if necessary so we can relocate the serialized code. std::unique_ptr aligned_buffer;