From 9c31cf369922975721e1187b8470086a9932f393 Mon Sep 17 00:00:00 2001 From: U2FsdGVkX1 Date: Wed, 20 Mar 2024 10:08:20 +0800 Subject: [PATCH] Add riscv64 support --- qt5-qtwebengine.spec | 31 +- qtwebengine-ffmpeg5.patch | 154 + riscv.patch | 2999 ++++ v8.patch | 33254 ++++++++++++++++++++++++++++++++++++ 4 files changed, 36436 insertions(+), 2 deletions(-) create mode 100644 qtwebengine-ffmpeg5.patch create mode 100644 riscv.patch create mode 100644 v8.patch diff --git a/qt5-qtwebengine.spec b/qt5-qtwebengine.spec index 7c597c7..b491066 100644 --- a/qt5-qtwebengine.spec +++ b/qt5-qtwebengine.spec @@ -53,7 +53,7 @@ Summary: Qt5 - QtWebEngine components Name: qt5-qtwebengine Version: 5.15.16 -Release: 4%{?dist} +Release: 5%{?dist} # See LICENSE.GPL LICENSE.LGPL LGPL_EXCEPTION.txt, for details # See also http://qt-project.org/doc/qt-5.0/qtdoc/licensing.html @@ -109,6 +109,13 @@ Patch35: qt5-qtwebengine-c99.patch Patch50: 0001-avcodec-x86-mathops-clip-constants-used-with-shift-i.patch Patch51: qtwebengine-icu-74.patch +# Working with ffmpeg +Patch60: qtwebengine-ffmpeg5.patch + +# riscv64 support patch from https://github.com/felixonmars/archriscv-packages/tree/master/qt5-webengine +Patch100: v8.patch +Patch101: riscv.patch + ## Upstream patches: # handled by qt5-srpm-macros, which defines %%qt5_qtwebengine_arches @@ -214,6 +221,11 @@ BuildRequires: libtirpc BuildRequires: libnsl2 BuildRequires: python-rpm-macros +# For ffmpeg libraries +BuildRequires: pkgconfig(libavcodec) +BuildRequires: pkgconfig(libavformat) +BuildRequires: pkgconfig(libavutil) + # extra (non-upstream) functions needed, see # src/3rdparty/chromium/third_party/sqlite/README.chromium for details #BuildRequires: pkgconfig(sqlite3) @@ -410,6 +422,13 @@ popd %patch50 -p1 -b .0001-avcodec-x86-mathops-clip-constants-used-with-shift-i %patch51 -p1 -b .icu-74 +%patch60 -p1 + +%ifarch riscv64 +%patch100 -p1 -b .riscv64-v8 +%patch101 -p1 -b .riscv64 +%endif + # delete all "toolprefix = " lines from build/toolchain/linux/BUILD.gn, as we # never cross-compile in native Fedora RPMs, fixes ARM and aarch64 FTBFS sed -i -e '/toolprefix = /d' -e 's/\${toolprefix}//g' \ @@ -446,9 +465,14 @@ export NINJA_PATH=%{__ninja} %{qmake_qt5} \ %{?debug_config:CONFIG+="%{debug_config}}" \ +%ifarch riscv64 + CONFIG+="link_pulseaudio" \ +%else CONFIG+="link_pulseaudio use_gold_linker" \ - %{?use_system_libicu:QMAKE_EXTRA_ARGS+="-system-webengine-icu"} \ +%endif + QMAKE_EXTRA_ARGS+="-system-webengine-ffmpeg -system-webengine-webp -system-webengine-opus" \ QMAKE_EXTRA_ARGS+="-webengine-kerberos" \ + %{?use_system_libicu:QMAKE_EXTRA_ARGS+="-system-webengine-icu"} \ %{?pipewire:QMAKE_EXTRA_ARGS+="-webengine-webrtc-pipewire"} \ . @@ -594,6 +618,9 @@ done %{_qt5_examplesdir}/ %changelog +* Wed Mar 20 2024 Liu Yang - 5.15.16-5 +- Add riscv64 support patch from Arch Linux + * Thu Mar 14 2024 Jan Grulich - 5.15.16-4 - Rebuild (qt5) diff --git a/qtwebengine-ffmpeg5.patch b/qtwebengine-ffmpeg5.patch new file mode 100644 index 0000000..87f259e --- /dev/null +++ b/qtwebengine-ffmpeg5.patch @@ -0,0 +1,154 @@ +Allow building qtwebengine using ffmpeg 5 +Origin: ArchLinux, https://github.com/archlinux/svntogit-packages/tree/packages/qt5-webengine/trunk + +diff --git a/src/3rdparty/chromium/media/ffmpeg/ffmpeg_common.h b/src/3rdparty/chromium/media/ffmpeg/ffmpeg_common.h +index 2734a48..70b1877 100644 +--- a/src/3rdparty/chromium/media/ffmpeg/ffmpeg_common.h ++++ b/src/3rdparty/chromium/media/ffmpeg/ffmpeg_common.h +@@ -29,6 +29,7 @@ extern "C" { + #include + #include + #include ++#include + #include + #include + #include +diff --git a/src/3rdparty/chromium/media/filters/audio_file_reader.cc b/src/3rdparty/chromium/media/filters/audio_file_reader.cc +index cb81d92..bd73908 100644 +--- a/src/3rdparty/chromium/media/filters/audio_file_reader.cc ++++ b/src/3rdparty/chromium/media/filters/audio_file_reader.cc +@@ -85,7 +85,7 @@ bool AudioFileReader::OpenDemuxer() { + } + + bool AudioFileReader::OpenDecoder() { +- AVCodec* codec = avcodec_find_decoder(codec_context_->codec_id); ++ const AVCodec* codec = avcodec_find_decoder(codec_context_->codec_id); + if (codec) { + // MP3 decodes to S16P which we don't support, tell it to use S16 instead. + if (codec_context_->sample_fmt == AV_SAMPLE_FMT_S16P) +diff --git a/src/3rdparty/chromium/media/filters/ffmpeg_audio_decoder.cc b/src/3rdparty/chromium/media/filters/ffmpeg_audio_decoder.cc +index 0d825ed..72fac61 100644 +--- a/src/3rdparty/chromium/media/filters/ffmpeg_audio_decoder.cc ++++ b/src/3rdparty/chromium/media/filters/ffmpeg_audio_decoder.cc +@@ -329,7 +329,7 @@ bool FFmpegAudioDecoder::ConfigureDecoder(const AudioDecoderConfig& config) { + } + } + +- AVCodec* codec = avcodec_find_decoder(codec_context_->codec_id); ++ const AVCodec* codec = avcodec_find_decoder(codec_context_->codec_id); + if (!codec || + avcodec_open2(codec_context_.get(), codec, &codec_options) < 0) { + DLOG(ERROR) << "Could not initialize audio decoder: " +diff --git a/src/3rdparty/chromium/media/filters/ffmpeg_demuxer.cc b/src/3rdparty/chromium/media/filters/ffmpeg_demuxer.cc +index d34db63..427565b 100644 +--- a/src/3rdparty/chromium/media/filters/ffmpeg_demuxer.cc ++++ b/src/3rdparty/chromium/media/filters/ffmpeg_demuxer.cc +@@ -98,12 +98,12 @@ static base::TimeDelta ExtractStartTime(AVStream* stream) { + + // Next try to use the first DTS value, for codecs where we know PTS == DTS + // (excludes all H26x codecs). The start time must be returned in PTS. +- if (stream->first_dts != kNoFFmpegTimestamp && ++ if (av_stream_get_first_dts(stream) != kNoFFmpegTimestamp && + stream->codecpar->codec_id != AV_CODEC_ID_HEVC && + stream->codecpar->codec_id != AV_CODEC_ID_H264 && + stream->codecpar->codec_id != AV_CODEC_ID_MPEG4) { + const base::TimeDelta first_pts = +- ConvertFromTimeBase(stream->time_base, stream->first_dts); ++ ConvertFromTimeBase(stream->time_base, av_stream_get_first_dts(stream)); + if (first_pts < start_time) + start_time = first_pts; + } +@@ -408,11 +408,11 @@ void FFmpegDemuxerStream::EnqueuePacket(ScopedAVPacket packet) { + scoped_refptr buffer; + + if (type() == DemuxerStream::TEXT) { +- int id_size = 0; ++ size_t id_size = 0; + uint8_t* id_data = av_packet_get_side_data( + packet.get(), AV_PKT_DATA_WEBVTT_IDENTIFIER, &id_size); + +- int settings_size = 0; ++ size_t settings_size = 0; + uint8_t* settings_data = av_packet_get_side_data( + packet.get(), AV_PKT_DATA_WEBVTT_SETTINGS, &settings_size); + +@@ -424,7 +424,7 @@ void FFmpegDemuxerStream::EnqueuePacket(ScopedAVPacket packet) { + buffer = DecoderBuffer::CopyFrom(packet->data, packet->size, + side_data.data(), side_data.size()); + } else { +- int side_data_size = 0; ++ size_t side_data_size = 0; + uint8_t* side_data = av_packet_get_side_data( + packet.get(), AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL, &side_data_size); + +@@ -485,7 +485,7 @@ void FFmpegDemuxerStream::EnqueuePacket(ScopedAVPacket packet) { + packet->size - data_offset); + } + +- int skip_samples_size = 0; ++ size_t skip_samples_size = 0; + const uint32_t* skip_samples_ptr = + reinterpret_cast(av_packet_get_side_data( + packet.get(), AV_PKT_DATA_SKIP_SAMPLES, &skip_samples_size)); +diff --git a/src/3rdparty/chromium/media/filters/ffmpeg_glue.cc b/src/3rdparty/chromium/media/filters/ffmpeg_glue.cc +index 0ef3521..8483ecc 100644 +--- a/src/3rdparty/chromium/media/filters/ffmpeg_glue.cc ++++ b/src/3rdparty/chromium/media/filters/ffmpeg_glue.cc +@@ -59,7 +59,6 @@ static int64_t AVIOSeekOperation(void* opaque, int64_t offset, int whence) { + } + + void FFmpegGlue::InitializeFFmpeg() { +- av_register_all(); + } + + static void LogContainer(bool is_local_file, +@@ -95,9 +94,6 @@ FFmpegGlue::FFmpegGlue(FFmpegURLProtocol* protocol) { + // Enable fast, but inaccurate seeks for MP3. + format_context_->flags |= AVFMT_FLAG_FAST_SEEK; + +- // Ensures we can read out various metadata bits like vp8 alpha. +- format_context_->flags |= AVFMT_FLAG_KEEP_SIDE_DATA; +- + // Ensures format parsing errors will bail out. From an audit on 11/2017, all + // instances were real failures. Solves bugs like http://crbug.com/710791. + format_context_->error_recognition |= AV_EF_EXPLODE; +diff --git a/src/3rdparty/chromium/media/filters/ffmpeg_video_decoder.cc b/src/3rdparty/chromium/media/filters/ffmpeg_video_decoder.cc +index ef12477..7996606 100644 +--- a/src/3rdparty/chromium/media/filters/ffmpeg_video_decoder.cc ++++ b/src/3rdparty/chromium/media/filters/ffmpeg_video_decoder.cc +@@ -391,7 +391,7 @@ bool FFmpegVideoDecoder::ConfigureDecoder(const VideoDecoderConfig& config, + if (decode_nalus_) + codec_context_->flags2 |= AV_CODEC_FLAG2_CHUNKS; + +- AVCodec* codec = avcodec_find_decoder(codec_context_->codec_id); ++ const AVCodec* codec = avcodec_find_decoder(codec_context_->codec_id); + if (!codec || avcodec_open2(codec_context_.get(), codec, NULL) < 0) { + ReleaseFFmpegResources(); + return false; +diff --git a/src/3rdparty/chromium/media/filters/media_file_checker.cc b/src/3rdparty/chromium/media/filters/media_file_checker.cc +index 59c2a2f..1a9872c 100644 +--- a/src/3rdparty/chromium/media/filters/media_file_checker.cc ++++ b/src/3rdparty/chromium/media/filters/media_file_checker.cc +@@ -68,7 +68,7 @@ bool MediaFileChecker::Start(base::TimeDelta check_time) { + auto context = AVStreamToAVCodecContext(format_context->streams[i]); + if (!context) + continue; +- AVCodec* codec = avcodec_find_decoder(cp->codec_id); ++ const AVCodec* codec = avcodec_find_decoder(cp->codec_id); + if (codec && avcodec_open2(context.get(), codec, nullptr) >= 0) { + auto loop = std::make_unique(context.get()); + stream_contexts[i] = {std::move(context), std::move(loop)}; +diff --git a/src/3rdparty/chromium/third_party/webrtc/modules/video_coding/codecs/h264/h264_decoder_impl.cc b/src/3rdparty/chromium/third_party/webrtc/modules/video_coding/codecs/h264/h264_decoder_impl.cc +index 9002b87..d12fade 100644 +--- a/src/3rdparty/chromium/third_party/webrtc/modules/video_coding/codecs/h264/h264_decoder_impl.cc ++++ b/src/3rdparty/chromium/third_party/webrtc/modules/video_coding/codecs/h264/h264_decoder_impl.cc +@@ -203,7 +203,7 @@ int32_t H264DecoderImpl::InitDecode(const VideoCodec* codec_settings, + // a pointer |this|. + av_context_->opaque = this; + +- AVCodec* codec = avcodec_find_decoder(av_context_->codec_id); ++ const AVCodec* codec = avcodec_find_decoder(av_context_->codec_id); + if (!codec) { + // This is an indication that FFmpeg has not been initialized or it has not + // been compiled/initialized with the correct set of codecs. + diff --git a/riscv.patch b/riscv.patch new file mode 100644 index 0000000..58b852a --- /dev/null +++ b/riscv.patch @@ -0,0 +1,2999 @@ +Index: qtwebengine-everywhere-src-5.15.7/configure.pri +=================================================================== +--- qtwebengine-everywhere-src-5.15.7.orig/configure.pri ++++ qtwebengine-everywhere-src-5.15.7/configure.pri +@@ -144,6 +144,7 @@ defineTest(qtConfTest_detectArch) { + contains(QT_ARCH, "arm")|contains(QT_ARCH, "arm64"): return(true) + contains(QT_ARCH, "mips"): return(true) + contains(QT_ARCH, "mips64"): return(true) ++ contains(QT_ARCH, "riscv64"): return(true) + qtLog("Architecture not supported.") + return(false) + } +Index: qtwebengine-everywhere-src-5.15.7/mkspecs/features/functions.prf +=================================================================== +--- qtwebengine-everywhere-src-5.15.7.orig/mkspecs/features/functions.prf ++++ qtwebengine-everywhere-src-5.15.7/mkspecs/features/functions.prf +@@ -107,6 +107,7 @@ defineReplace(gnArch) { + contains(qtArch, "mips"): return(mipsel) + contains(qtArch, "mips64"): return(mips64el) + contains(qtArch, "mips64el"): return(mips64el) ++ contains(qtArch, "riscv64"): return(riscv64) + return(unknown) + } + +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/base/process/launch_posix.cc +=================================================================== +--- qtwebengine-everywhere-src-5.15.7.orig/src/3rdparty/chromium/base/process/launch_posix.cc ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/base/process/launch_posix.cc +@@ -704,7 +704,7 @@ NOINLINE pid_t CloneAndLongjmpInChild(un + alignas(16) char stack_buf[PTHREAD_STACK_MIN]; + #if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM_FAMILY) || \ + defined(ARCH_CPU_MIPS_FAMILY) || defined(ARCH_CPU_S390_FAMILY) || \ +- defined(ARCH_CPU_PPC64_FAMILY) ++ defined(ARCH_CPU_PPC64_FAMILY) || defined(ARCH_CPU_RISCV_FAMILY) + // The stack grows downward. + void* stack = stack_buf + sizeof(stack_buf); + #else +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/build/build_config.h +=================================================================== +--- qtwebengine-everywhere-src-5.15.7.orig/src/3rdparty/chromium/build/build_config.h ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/build/build_config.h +@@ -193,6 +193,11 @@ + #define ARCH_CPU_32_BITS 1 + #define ARCH_CPU_BIG_ENDIAN 1 + #endif ++#elif defined(__riscv) && __riscv_xlen == 64 ++#define ARCH_CPU_RISCV_FAMILY 1 ++#define ARCH_CPU_RISCV64 1 ++#define ARCH_CPU_64_BITS 1 ++#define ARCH_CPU_LITTLE_ENDIAN 1 + #else + #error Please add support for your architecture in build/build_config.h + #endif +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/build/config/BUILD.gn +=================================================================== +--- qtwebengine-everywhere-src-5.15.7.orig/src/3rdparty/chromium/build/config/BUILD.gn ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/build/config/BUILD.gn +@@ -238,6 +238,7 @@ config("default_libs") { + "dl", + "pthread", + "rt", ++ "atomic", + ] + } + } +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/build/toolchain/linux/BUILD.gn +=================================================================== +--- qtwebengine-everywhere-src-5.15.7.orig/src/3rdparty/chromium/build/toolchain/linux/BUILD.gn ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/build/toolchain/linux/BUILD.gn +@@ -298,3 +298,20 @@ gcc_toolchain("mips64") { + is_clang = false + } + } ++ ++gcc_toolchain("riscv64") { ++ cc = "gcc" ++ cxx = "g++" ++ ++ readelf = "readelf" ++ nm = "nm" ++ ar = "ar" ++ ld = cxx ++ ++ toolchain_args = { ++ current_cpu = "riscv64" ++ current_os = "linux" ++ is_clang = false ++ } ++} ++ +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/sandbox/features.gni +=================================================================== +--- qtwebengine-everywhere-src-5.15.7.orig/src/3rdparty/chromium/sandbox/features.gni ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/sandbox/features.gni +@@ -11,7 +11,8 @@ import("//build/config/nacl/config.gni") + use_seccomp_bpf = (is_linux || is_chromeos || is_android) && + (current_cpu == "x86" || current_cpu == "x64" || + current_cpu == "arm" || current_cpu == "arm64" || +- current_cpu == "mipsel" || current_cpu == "mips64el") ++ current_cpu == "mipsel" || current_cpu == "mips64el" || ++ current_cpu == "riscv64") + + use_seccomp_bpf = use_seccomp_bpf || is_nacl_nonsfi + +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/sandbox/linux/bpf_dsl/linux_syscall_ranges.h +=================================================================== +--- qtwebengine-everywhere-src-5.15.7.orig/src/3rdparty/chromium/sandbox/linux/bpf_dsl/linux_syscall_ranges.h ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/sandbox/linux/bpf_dsl/linux_syscall_ranges.h +@@ -56,6 +56,12 @@ + #define MAX_PUBLIC_SYSCALL __NR_syscalls + #define MAX_SYSCALL MAX_PUBLIC_SYSCALL + ++#elif defined(__riscv) ++ ++#define MIN_SYSCALL 0u ++#define MAX_PUBLIC_SYSCALL 1024u ++#define MAX_SYSCALL MAX_PUBLIC_SYSCALL ++ + #else + #error "Unsupported architecture" + #endif +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/sandbox/linux/bpf_dsl/seccomp_macros.h +=================================================================== +--- qtwebengine-everywhere-src-5.15.7.orig/src/3rdparty/chromium/sandbox/linux/bpf_dsl/seccomp_macros.h ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/sandbox/linux/bpf_dsl/seccomp_macros.h +@@ -346,6 +346,46 @@ struct regs_struct { + #define SECCOMP_PT_PARM4(_regs) (_regs).regs[3] + #define SECCOMP_PT_PARM5(_regs) (_regs).regs[4] + #define SECCOMP_PT_PARM6(_regs) (_regs).regs[5] ++ ++#elif defined(__riscv) ++struct regs_struct { ++ unsigned long regs[32]; ++}; ++ ++#define SECCOMP_ARCH AUDIT_ARCH_RISCV64 ++ ++#define SECCOMP_REG(_ctx, _reg) ((_ctx)->uc_mcontext.__gregs[_reg]) ++ ++#define SECCOMP_RESULT(_ctx) SECCOMP_REG(_ctx, REG_A0) ++#define SECCOMP_SYSCALL(_ctx) SECCOMP_REG(_ctx, REG_A0+7) ++#define SECCOMP_IP(_ctx) (_ctx)->uc_mcontext.__gregs[REG_PC] ++#define SECCOMP_PARM1(_ctx) SECCOMP_REG(_ctx, REG_A0) ++#define SECCOMP_PARM2(_ctx) SECCOMP_REG(_ctx, REG_A0+1) ++#define SECCOMP_PARM3(_ctx) SECCOMP_REG(_ctx, REG_A0+2) ++#define SECCOMP_PARM4(_ctx) SECCOMP_REG(_ctx, REG_A0+3) ++#define SECCOMP_PARM5(_ctx) SECCOMP_REG(_ctx, REG_A0+4) ++#define SECCOMP_PARM6(_ctx) SECCOMP_REG(_ctx, REG_A0+5) ++ ++#define SECCOMP_NR_IDX (offsetof(struct arch_seccomp_data, nr)) ++#define SECCOMP_ARCH_IDX (offsetof(struct arch_seccomp_data, arch)) ++#define SECCOMP_IP_MSB_IDX \ ++ (offsetof(struct arch_seccomp_data, instruction_pointer) + 4) ++#define SECCOMP_IP_LSB_IDX \ ++ (offsetof(struct arch_seccomp_data, instruction_pointer) + 0) ++#define SECCOMP_ARG_MSB_IDX(nr) \ ++ (offsetof(struct arch_seccomp_data, args) + 8 * (nr) + 4) ++#define SECCOMP_ARG_LSB_IDX(nr) \ ++ (offsetof(struct arch_seccomp_data, args) + 8 * (nr) + 0) ++ ++#define SECCOMP_PT_RESULT(_regs) (_regs).regs[REG_A0] ++#define SECCOMP_PT_SYSCALL(_regs) (_regs).regs[REG_A0+7] ++#define SECCOMP_PT_IP(_regs) (_regs).regs[REG_PC] ++#define SECCOMP_PT_PARM1(_regs) (_regs).regs[REG_A0] ++#define SECCOMP_PT_PARM2(_regs) (_regs).regs[REG_A0+1] ++#define SECCOMP_PT_PARM3(_regs) (_regs).regs[REG_A0+2] ++#define SECCOMP_PT_PARM4(_regs) (_regs).regs[REG_A0+3] ++#define SECCOMP_PT_PARM5(_regs) (_regs).regs[REG_A0+4] ++#define SECCOMP_PT_PARM6(_regs) (_regs).regs[REG_A0+5] + #else + #error Unsupported target platform + +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc +=================================================================== +--- qtwebengine-everywhere-src-5.15.7.orig/src/3rdparty/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc +@@ -181,7 +181,7 @@ ResultExpr EvaluateSyscallImpl(int fs_de + return RestrictFcntlCommands(); + #endif + +-#if !defined(__aarch64__) ++#if !defined(__aarch64__) && !defined(__riscv) + // fork() is never used as a system call (clone() is used instead), but we + // have seen it in fallback code on Android. + if (sysno == __NR_fork) { +@@ -231,7 +231,7 @@ ResultExpr EvaluateSyscallImpl(int fs_de + } + + #if defined(__i386__) || defined(__x86_64__) || defined(__mips__) || \ +- defined(__aarch64__) ++ defined(__aarch64__) || defined(__riscv) + if (sysno == __NR_mmap) + return RestrictMmapFlags(); + #endif +@@ -249,7 +249,7 @@ ResultExpr EvaluateSyscallImpl(int fs_de + return RestrictPrctl(); + + #if defined(__x86_64__) || defined(__arm__) || defined(__mips__) || \ +- defined(__aarch64__) ++ defined(__aarch64__) || defined(__riscv) + if (sysno == __NR_socketpair) { + // Only allow AF_UNIX, PF_UNIX. Crash if anything else is seen. + static_assert(AF_UNIX == PF_UNIX, +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc +=================================================================== +--- qtwebengine-everywhere-src-5.15.7.orig/src/3rdparty/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc +@@ -37,7 +37,7 @@ + #include + #include + #if defined(OS_LINUX) && !defined(OS_CHROMEOS) && !defined(__arm__) && \ +- !defined(__aarch64__) && !defined(PTRACE_GET_THREAD_AREA) ++ !defined(__aarch64__) && !defined(__riscv) && !defined(PTRACE_GET_THREAD_AREA) + // Also include asm/ptrace-abi.h since ptrace.h in older libc (for instance + // the one in Ubuntu 16.04 LTS) is missing PTRACE_GET_THREAD_AREA. + // asm/ptrace-abi.h doesn't exist on arm32 and PTRACE_GET_THREAD_AREA isn't +@@ -406,7 +406,7 @@ ResultExpr RestrictPrlimitToGetrlimit(pi + ResultExpr RestrictPtrace() { + const Arg request(0); + return Switch(request).CASES(( +-#if !defined(__aarch64__) ++#if !defined(__aarch64__) && !defined(__riscv) + PTRACE_GETREGS, + PTRACE_GETFPREGS, + #if defined(TRACE_GET_THREAD_AREA) +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_sets.cc +=================================================================== +--- qtwebengine-everywhere-src-5.15.7.orig/src/3rdparty/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_sets.cc ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_sets.cc +@@ -86,7 +86,7 @@ bool SyscallSets::IsUmask(int sysno) { + // Both EPERM and ENOENT are valid errno unless otherwise noted in comment. + bool SyscallSets::IsFileSystem(int sysno) { + switch (sysno) { +-#if !defined(__aarch64__) ++#if !defined(__aarch64__) && !defined(__riscv) + case __NR_access: // EPERM not a valid errno. + case __NR_chmod: + case __NR_chown: +@@ -118,7 +118,7 @@ bool SyscallSets::IsFileSystem(int sysno + case __NR_faccessat: // EPERM not a valid errno. + case __NR_fchmodat: + case __NR_fchownat: // Should be called chownat ? +-#if defined(__x86_64__) || defined(__aarch64__) ++#if defined(__x86_64__) || defined(__aarch64__) || defined(__riscv) + case __NR_newfstatat: // fstatat(). EPERM not a valid errno. + #elif defined(__i386__) || defined(__arm__) || \ + (defined(ARCH_CPU_MIPS_FAMILY) && defined(ARCH_CPU_32_BITS)) +@@ -201,7 +201,7 @@ bool SyscallSets::IsAllowedFileSystemAcc + case __NR_oldfstat: + #endif + #if defined(__i386__) || defined(__x86_64__) || defined(__mips__) || \ +- defined(__aarch64__) ++ defined(__aarch64__) || defined(__riscv) + case __NR_sync_file_range: // EPERM not a valid errno. + #elif defined(__arm__) + case __NR_arm_sync_file_range: // EPERM not a valid errno. +@@ -225,7 +225,7 @@ bool SyscallSets::IsDeniedFileSystemAcce + (defined(ARCH_CPU_MIPS_FAMILY) && defined(ARCH_CPU_32_BITS)) + case __NR_ftruncate64: + #endif +-#if !defined(__aarch64__) ++#if !defined(__aarch64__) && !defined(__riscv) + case __NR_getdents: // EPERM not a valid errno. + #endif + case __NR_getdents64: // EPERM not a valid errno. +@@ -304,7 +304,7 @@ bool SyscallSets::IsProcessPrivilegeChan + bool SyscallSets::IsProcessGroupOrSession(int sysno) { + switch (sysno) { + case __NR_setpgid: +-#if !defined(__aarch64__) ++#if !defined(__aarch64__) && !defined(__riscv) + case __NR_getpgrp: + #endif + case __NR_setsid: +@@ -333,7 +333,7 @@ bool SyscallSets::IsAllowedSignalHandlin + case __NR_rt_sigsuspend: + case __NR_rt_tgsigqueueinfo: + case __NR_sigaltstack: +-#if !defined(__aarch64__) ++#if !defined(__aarch64__) && !defined(__riscv) + case __NR_signalfd: + #endif + case __NR_signalfd4: +@@ -357,12 +357,12 @@ bool SyscallSets::IsAllowedOperationOnFd + switch (sysno) { + case __NR_close: + case __NR_dup: +-#if !defined(__aarch64__) ++#if !defined(__aarch64__) && !defined(__riscv) + case __NR_dup2: + #endif + case __NR_dup3: + #if defined(__x86_64__) || defined(__arm__) || defined(__mips__) || \ +- defined(__aarch64__) ++ defined(__aarch64__) || defined(__riscv) + case __NR_shutdown: + #endif + return true; +@@ -401,7 +401,7 @@ bool SyscallSets::IsAllowedProcessStartO + return true; + case __NR_clone: // Should be parameter-restricted. + case __NR_setns: // Privileged. +-#if !defined(__aarch64__) ++#if !defined(__aarch64__) && !defined(__riscv) + case __NR_fork: + #endif + #if defined(__i386__) || defined(__x86_64__) +@@ -412,7 +412,7 @@ bool SyscallSets::IsAllowedProcessStartO + #endif + case __NR_set_tid_address: + case __NR_unshare: +-#if !defined(__mips__) && !defined(__aarch64__) ++#if !defined(__mips__) && !defined(__aarch64__) && !defined(__riscv) + case __NR_vfork: + #endif + default: +@@ -433,7 +433,7 @@ bool SyscallSets::IsAllowedFutex(int sys + + bool SyscallSets::IsAllowedEpoll(int sysno) { + switch (sysno) { +-#if !defined(__aarch64__) ++#if !defined(__aarch64__) && !defined(__riscv) + case __NR_epoll_create: + case __NR_epoll_wait: + #endif +@@ -454,14 +454,14 @@ bool SyscallSets::IsAllowedEpoll(int sys + + bool SyscallSets::IsAllowedGetOrModifySocket(int sysno) { + switch (sysno) { +-#if !defined(__aarch64__) ++#if !defined(__aarch64__) && !defined(__riscv) + case __NR_pipe: + #endif + case __NR_pipe2: + return true; + default: + #if defined(__x86_64__) || defined(__arm__) || defined(__mips__) || \ +- defined(__aarch64__) ++ defined(__aarch64__) || defined(__riscv) + case __NR_socketpair: // We will want to inspect its argument. + #endif + return false; +@@ -471,7 +471,7 @@ bool SyscallSets::IsAllowedGetOrModifySo + bool SyscallSets::IsDeniedGetOrModifySocket(int sysno) { + switch (sysno) { + #if defined(__x86_64__) || defined(__arm__) || defined(__mips__) || \ +- defined(__aarch64__) ++ defined(__aarch64__) || defined(__riscv) + case __NR_accept: + case __NR_accept4: + case __NR_bind: +@@ -525,7 +525,7 @@ bool SyscallSets::IsAllowedAddressSpaceA + case __NR_mincore: + case __NR_mlockall: + #if defined(__i386__) || defined(__x86_64__) || defined(__mips__) || \ +- defined(__aarch64__) ++ defined(__aarch64__) || defined(__riscv) + case __NR_mmap: + #endif + #if defined(__i386__) || defined(__arm__) || \ +@@ -558,7 +558,7 @@ bool SyscallSets::IsAllowedGeneralIo(int + (defined(ARCH_CPU_MIPS_FAMILY) && defined(ARCH_CPU_32_BITS)) + case __NR__llseek: + #endif +-#if !defined(__aarch64__) ++#if !defined(__aarch64__) && !defined(__riscv) + case __NR_poll: + #endif + case __NR_ppoll: +@@ -571,7 +571,7 @@ bool SyscallSets::IsAllowedGeneralIo(int + case __NR_recv: + #endif + #if defined(__x86_64__) || defined(__arm__) || defined(__mips__) || \ +- defined(__aarch64__) ++ defined(__aarch64__) || defined(__riscv) + case __NR_recvfrom: // Could specify source. + case __NR_recvmsg: // Could specify source. + #endif +@@ -586,7 +586,7 @@ bool SyscallSets::IsAllowedGeneralIo(int + case __NR_send: + #endif + #if defined(__x86_64__) || defined(__arm__) || defined(__mips__) || \ +- defined(__aarch64__) ++ defined(__aarch64__) || defined(__riscv) + case __NR_sendmsg: // Could specify destination. + case __NR_sendto: // Could specify destination. + #endif +@@ -636,7 +636,7 @@ bool SyscallSets::IsSeccomp(int sysno) { + bool SyscallSets::IsAllowedBasicScheduler(int sysno) { + switch (sysno) { + case __NR_sched_yield: +-#if !defined(__aarch64__) ++#if !defined(__aarch64__) && !defined(__riscv) + case __NR_pause: + #endif + case __NR_nanosleep: +@@ -720,7 +720,7 @@ bool SyscallSets::IsNuma(int sysno) { + case __NR_getcpu: + case __NR_mbind: + #if defined(__i386__) || defined(__x86_64__) || defined(__mips__) || \ +- defined(__aarch64__) ++ defined(__aarch64__) || defined(__riscv) + case __NR_migrate_pages: + #endif + case __NR_move_pages: +@@ -749,7 +749,7 @@ bool SyscallSets::IsGlobalProcessEnviron + switch (sysno) { + case __NR_acct: // Privileged. + #if defined(__i386__) || defined(__x86_64__) || defined(__mips__) || \ +- defined(__aarch64__) ++ defined(__aarch64__) || defined(__riscv) + case __NR_getrlimit: + #endif + #if defined(__i386__) || defined(__arm__) +@@ -784,7 +784,7 @@ bool SyscallSets::IsDebug(int sysno) { + + bool SyscallSets::IsGlobalSystemStatus(int sysno) { + switch (sysno) { +-#if !defined(__aarch64__) ++#if !defined(__aarch64__) && !defined(__riscv) + case __NR__sysctl: + case __NR_sysfs: + #endif +@@ -802,7 +802,7 @@ bool SyscallSets::IsGlobalSystemStatus(i + + bool SyscallSets::IsEventFd(int sysno) { + switch (sysno) { +-#if !defined(__aarch64__) ++#if !defined(__aarch64__) && !defined(__riscv) + case __NR_eventfd: + #endif + case __NR_eventfd2: +@@ -838,7 +838,8 @@ bool SyscallSets::IsKeyManagement(int sy + } + + #if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__) || \ +- (defined(ARCH_CPU_MIPS_FAMILY) && defined(ARCH_CPU_64_BITS)) ++ (defined(ARCH_CPU_MIPS_FAMILY) && defined(ARCH_CPU_64_BITS)) || \ ++ defined(__riscv) + bool SyscallSets::IsSystemVSemaphores(int sysno) { + switch (sysno) { + case __NR_semctl: +@@ -854,7 +855,8 @@ bool SyscallSets::IsSystemVSemaphores(in + + #if defined(__i386__) || defined(__x86_64__) || defined(__arm__) || \ + defined(__aarch64__) || \ +- (defined(ARCH_CPU_MIPS_FAMILY) && defined(ARCH_CPU_64_BITS)) ++ (defined(ARCH_CPU_MIPS_FAMILY) && defined(ARCH_CPU_64_BITS)) || \ ++ defined(__riscv) + // These give a lot of ambient authority and bypass the setuid sandbox. + bool SyscallSets::IsSystemVSharedMemory(int sysno) { + switch (sysno) { +@@ -870,7 +872,8 @@ bool SyscallSets::IsSystemVSharedMemory( + #endif + + #if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__) || \ +- (defined(ARCH_CPU_MIPS_FAMILY) && defined(ARCH_CPU_64_BITS)) ++ (defined(ARCH_CPU_MIPS_FAMILY) && defined(ARCH_CPU_64_BITS)) || \ ++ defined(__riscv) + bool SyscallSets::IsSystemVMessageQueue(int sysno) { + switch (sysno) { + case __NR_msgctl: +@@ -901,7 +904,8 @@ bool SyscallSets::IsSystemVIpc(int sysno + + bool SyscallSets::IsAnySystemV(int sysno) { + #if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__) || \ +- (defined(ARCH_CPU_MIPS_FAMILY) && defined(ARCH_CPU_64_BITS)) ++ (defined(ARCH_CPU_MIPS_FAMILY) && defined(ARCH_CPU_64_BITS)) || \ ++ defined(__riscv) + return IsSystemVMessageQueue(sysno) || IsSystemVSemaphores(sysno) || + IsSystemVSharedMemory(sysno); + #elif defined(__i386__) || \ +@@ -934,7 +938,7 @@ bool SyscallSets::IsAdvancedScheduler(in + bool SyscallSets::IsInotify(int sysno) { + switch (sysno) { + case __NR_inotify_add_watch: +-#if !defined(__aarch64__) ++#if !defined(__aarch64__) && !defined(__riscv) + case __NR_inotify_init: + #endif + case __NR_inotify_init1: +@@ -1065,7 +1069,7 @@ bool SyscallSets::IsMisc(int sysno) { + #if defined(__x86_64__) + case __NR_tuxcall: + #endif +-#if !defined(__aarch64__) ++#if !defined(__aarch64__) && !defined(__riscv) + case __NR_vserver: + #endif + return true; +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_sets.h +=================================================================== +--- qtwebengine-everywhere-src-5.15.7.orig/src/3rdparty/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_sets.h ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_sets.h +@@ -49,7 +49,7 @@ class SANDBOX_EXPORT SyscallSets { + #endif + + #if defined(__x86_64__) || defined(__arm__) || defined(__mips__) || \ +- defined(__aarch64__) ++ defined(__aarch64__) || defined(__riscv) + static bool IsNetworkSocketInformation(int sysno); + #endif + +@@ -72,18 +72,21 @@ class SANDBOX_EXPORT SyscallSets { + static bool IsAsyncIo(int sysno); + static bool IsKeyManagement(int sysno); + #if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__) || \ +- (defined(ARCH_CPU_MIPS_FAMILY) && defined(ARCH_CPU_64_BITS)) ++ (defined(ARCH_CPU_MIPS_FAMILY) && defined(ARCH_CPU_64_BITS)) || \ ++ defined(__riscv) + static bool IsSystemVSemaphores(int sysno); + #endif + #if defined(__i386__) || defined(__x86_64__) || defined(__arm__) || \ + defined(__aarch64__) || \ +- (defined(ARCH_CPU_MIPS_FAMILY) && defined(ARCH_CPU_64_BITS)) ++ (defined(ARCH_CPU_MIPS_FAMILY) && defined(ARCH_CPU_64_BITS)) || \ ++ defined(__riscv) + // These give a lot of ambient authority and bypass the setuid sandbox. + static bool IsSystemVSharedMemory(int sysno); + #endif + + #if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__) || \ +- (defined(ARCH_CPU_MIPS_FAMILY) && defined(ARCH_CPU_64_BITS)) ++ (defined(ARCH_CPU_MIPS_FAMILY) && defined(ARCH_CPU_64_BITS)) || \ ++ defined(__riscv) + static bool IsSystemVMessageQueue(int sysno); + #endif + +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/sandbox/linux/seccomp-bpf/syscall.cc +=================================================================== +--- qtwebengine-everywhere-src-5.15.7.orig/src/3rdparty/chromium/sandbox/linux/seccomp-bpf/syscall.cc ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/sandbox/linux/seccomp-bpf/syscall.cc +@@ -18,7 +18,7 @@ namespace sandbox { + namespace { + + #if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM_FAMILY) || \ +- defined(ARCH_CPU_MIPS_FAMILY) ++ defined(ARCH_CPU_MIPS_FAMILY) || defined(ARCH_CPU_RISCV_FAMILY) + // Number that's not currently used by any Linux kernel ABIs. + const int kInvalidSyscallNumber = 0x351d3; + #else +@@ -312,6 +312,28 @@ asm(// We need to be able to tell the ke + "2:ret\n" + ".cfi_endproc\n" + ".size SyscallAsm, .-SyscallAsm\n" ++#elif defined(__riscv) ++ ".text\n" ++ ".align 2\n" ++ ".type SyscallAsm, %function\n" ++ "SyscallAsm:\n" ++ ".cfi_startproc\n" ++ "bgez a0,1f\n" ++ "la a0,2f\n" ++ "j 2f\n" ++ "1:mv a7, a0\n" ++ "ld a0, (t0)\n" ++ "ld a1, 8(t0)\n" ++ "ld a2, 16(t0)\n" ++ "ld a3, 24(t0)\n" ++ "ld a4, 32(t0)\n" ++ "ld a5, 40(t0)\n" ++ "ld a6, 48(t0)\n" ++ // Enter the kernel ++ "scall\n" ++ "2:ret\n" ++ ".cfi_endproc\n" ++ ".size SyscallAsm, .-SyscallAsm\n" + #endif + ); // asm + +@@ -429,6 +451,18 @@ intptr_t Syscall::Call(int nr, + ret = inout; + } + ++#elif defined(__riscv) ++ intptr_t ret; ++ { ++ register intptr_t inout __asm__("a0") = nr; ++ register const intptr_t* data __asm__("t0") = args; ++ asm volatile("jal SyscallAsm\n" ++ : "+r"(inout) ++ : "r"(data) ++ : "memory", "a1", "a2", "a3", "a4", "a5", "a6"); ++ ret = inout; ++ } ++ + #else + #error "Unimplemented architecture" + #endif +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/sandbox/linux/services/credentials.cc +=================================================================== +--- qtwebengine-everywhere-src-5.15.7.orig/src/3rdparty/chromium/sandbox/linux/services/credentials.cc ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/sandbox/linux/services/credentials.cc +@@ -81,7 +81,7 @@ bool ChrootToSafeEmptyDir() { + pid_t pid = -1; + alignas(16) char stack_buf[PTHREAD_STACK_MIN]; + #if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM_FAMILY) || \ +- defined(ARCH_CPU_MIPS_FAMILY) ++ defined(ARCH_CPU_MIPS_FAMILY) || defined(ARCH_CPU_RISCV_FAMILY) + // The stack grows downward. + void* stack = stack_buf + sizeof(stack_buf); + #else +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/sandbox/linux/syscall_broker/broker_process.cc +=================================================================== +--- qtwebengine-everywhere-src-5.15.7.orig/src/3rdparty/chromium/sandbox/linux/syscall_broker/broker_process.cc ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/sandbox/linux/syscall_broker/broker_process.cc +@@ -128,43 +128,43 @@ bool BrokerProcess::IsSyscallBrokerable( + // and are default disabled in Android. So, we should refuse to broker them + // to be consistent with the platform's restrictions. + switch (sysno) { +-#if !defined(__aarch64__) && !defined(OS_ANDROID) ++#if !defined(__aarch64__) && !defined(OS_ANDROID) && !defined(__riscv) + case __NR_access: + #endif + case __NR_faccessat: + return !fast_check || allowed_command_set_.test(COMMAND_ACCESS); + +-#if !defined(__aarch64__) && !defined(OS_ANDROID) ++#if !defined(__aarch64__) && !defined(OS_ANDROID) && !defined(__riscv) + case __NR_mkdir: + #endif + case __NR_mkdirat: + return !fast_check || allowed_command_set_.test(COMMAND_MKDIR); + +-#if !defined(__aarch64__) && !defined(OS_ANDROID) ++#if !defined(__aarch64__) && !defined(OS_ANDROID) && !defined(__riscv) + case __NR_open: + #endif + case __NR_openat: + return !fast_check || allowed_command_set_.test(COMMAND_OPEN); + +-#if !defined(__aarch64__) && !defined(OS_ANDROID) ++#if !defined(__aarch64__) && !defined(OS_ANDROID) && !defined(__riscv) + case __NR_readlink: + #endif + case __NR_readlinkat: + return !fast_check || allowed_command_set_.test(COMMAND_READLINK); + +-#if !defined(__aarch64__) && !defined(OS_ANDROID) ++#if !defined(__aarch64__) && !defined(OS_ANDROID) && !defined(__riscv) + case __NR_rename: + #endif + case __NR_renameat: + case __NR_renameat2: + return !fast_check || allowed_command_set_.test(COMMAND_RENAME); + +-#if !defined(__aarch64__) && !defined(OS_ANDROID) ++#if !defined(__aarch64__) && !defined(OS_ANDROID) && !defined(__riscv) + case __NR_rmdir: + return !fast_check || allowed_command_set_.test(COMMAND_RMDIR); + #endif + +-#if !defined(__aarch64__) && !defined(OS_ANDROID) ++#if !defined(__aarch64__) && !defined(OS_ANDROID) && !defined(__riscv) + case __NR_stat: + case __NR_lstat: + #endif +@@ -174,7 +174,7 @@ bool BrokerProcess::IsSyscallBrokerable( + #if defined(__NR_fstatat64) + case __NR_fstatat64: + #endif +-#if defined(__x86_64__) || defined(__aarch64__) ++#if defined(__x86_64__) || defined(__aarch64__) || defined(__riscv) + case __NR_newfstatat: + #endif + return !fast_check || allowed_command_set_.test(COMMAND_STAT); +@@ -189,7 +189,7 @@ bool BrokerProcess::IsSyscallBrokerable( + return !fast_check || allowed_command_set_.test(COMMAND_STAT); + #endif + +-#if !defined(__aarch64__) && !defined(OS_ANDROID) ++#if !defined(__aarch64__) && !defined(OS_ANDROID) && !defined(__riscv) + case __NR_unlink: + return !fast_check || allowed_command_set_.test(COMMAND_UNLINK); + #endif +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/sandbox/linux/system_headers/linux_seccomp.h +=================================================================== +--- qtwebengine-everywhere-src-5.15.7.orig/src/3rdparty/chromium/sandbox/linux/system_headers/linux_seccomp.h ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/sandbox/linux/system_headers/linux_seccomp.h +@@ -41,6 +41,9 @@ + #ifndef EM_AARCH64 + #define EM_AARCH64 183 + #endif ++#ifndef EM_RISCV ++#define EM_RISCV 243 ++#endif + + #ifndef __AUDIT_ARCH_64BIT + #define __AUDIT_ARCH_64BIT 0x80000000 +@@ -73,6 +76,9 @@ + #ifndef AUDIT_ARCH_AARCH64 + #define AUDIT_ARCH_AARCH64 (EM_AARCH64 | __AUDIT_ARCH_64BIT | __AUDIT_ARCH_LE) + #endif ++#ifndef AUDIT_ARCH_RISCV64 ++#define AUDIT_ARCH_RISCV64 (EM_RISCV|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE) ++#endif + + // For prctl.h + #ifndef PR_SET_SECCOMP +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/sandbox/linux/system_headers/linux_signal.h +=================================================================== +--- qtwebengine-everywhere-src-5.15.7.orig/src/3rdparty/chromium/sandbox/linux/system_headers/linux_signal.h ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/sandbox/linux/system_headers/linux_signal.h +@@ -13,7 +13,7 @@ + // (not undefined, but defined different values and in different memory + // layouts). So, fill the gap here. + #if defined(__i386__) || defined(__x86_64__) || defined(__arm__) || \ +- defined(__aarch64__) ++ defined(__aarch64__) || defined(__riscv) + + #define LINUX_SIGHUP 1 + #define LINUX_SIGINT 2 +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/sandbox/linux/system_headers/linux_stat.h +=================================================================== +--- qtwebengine-everywhere-src-5.15.7.orig/src/3rdparty/chromium/sandbox/linux/system_headers/linux_stat.h ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/sandbox/linux/system_headers/linux_stat.h +@@ -132,7 +132,7 @@ struct kernel_stat { + int st_blocks; + int st_pad4[14]; + }; +-#elif defined(__aarch64__) ++#elif defined(__aarch64__) || defined(__riscv) + struct kernel_stat { + unsigned long st_dev; + unsigned long st_ino; +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/sandbox/linux/system_headers/linux_syscalls.h +=================================================================== +--- qtwebengine-everywhere-src-5.15.7.orig/src/3rdparty/chromium/sandbox/linux/system_headers/linux_syscalls.h ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/sandbox/linux/system_headers/linux_syscalls.h +@@ -35,5 +35,9 @@ + #include "sandbox/linux/system_headers/arm64_linux_syscalls.h" + #endif + ++#if defined(__riscv) && __riscv_xlen == 64 ++#include "sandbox/linux/system_headers/riscv64_linux_syscalls.h" ++#endif ++ + #endif // SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_SYSCALLS_H_ + +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/sandbox/linux/system_headers/riscv64_linux_syscalls.h +=================================================================== +--- /dev/null ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/sandbox/linux/system_headers/riscv64_linux_syscalls.h +@@ -0,0 +1,1066 @@ ++// Copyright 2014 The Chromium Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef SANDBOX_LINUX_SYSTEM_HEADERS_RISCV64_LINUX_SYSCALLS_H_ ++#define SANDBOX_LINUX_SYSTEM_HEADERS_RISCV64_LINUX_SYSCALLS_H_ ++ ++#include ++ ++#if !defined(__NR_io_setup) ++#define __NR_io_setup 0 ++#endif ++ ++#if !defined(__NR_io_destroy) ++#define __NR_io_destroy 1 ++#endif ++ ++#if !defined(__NR_io_submit) ++#define __NR_io_submit 2 ++#endif ++ ++#if !defined(__NR_io_cancel) ++#define __NR_io_cancel 3 ++#endif ++ ++#if !defined(__NR_io_getevents) ++#define __NR_io_getevents 4 ++#endif ++ ++#if !defined(__NR_setxattr) ++#define __NR_setxattr 5 ++#endif ++ ++#if !defined(__NR_lsetxattr) ++#define __NR_lsetxattr 6 ++#endif ++ ++#if !defined(__NR_fsetxattr) ++#define __NR_fsetxattr 7 ++#endif ++ ++#if !defined(__NR_getxattr) ++#define __NR_getxattr 8 ++#endif ++ ++#if !defined(__NR_lgetxattr) ++#define __NR_lgetxattr 9 ++#endif ++ ++#if !defined(__NR_fgetxattr) ++#define __NR_fgetxattr 10 ++#endif ++ ++#if !defined(__NR_listxattr) ++#define __NR_listxattr 11 ++#endif ++ ++#if !defined(__NR_llistxattr) ++#define __NR_llistxattr 12 ++#endif ++ ++#if !defined(__NR_flistxattr) ++#define __NR_flistxattr 13 ++#endif ++ ++#if !defined(__NR_removexattr) ++#define __NR_removexattr 14 ++#endif ++ ++#if !defined(__NR_lremovexattr) ++#define __NR_lremovexattr 15 ++#endif ++ ++#if !defined(__NR_fremovexattr) ++#define __NR_fremovexattr 16 ++#endif ++ ++#if !defined(__NR_getcwd) ++#define __NR_getcwd 17 ++#endif ++ ++#if !defined(__NR_lookup_dcookie) ++#define __NR_lookup_dcookie 18 ++#endif ++ ++#if !defined(__NR_eventfd2) ++#define __NR_eventfd2 19 ++#endif ++ ++#if !defined(__NR_epoll_create1) ++#define __NR_epoll_create1 20 ++#endif ++ ++#if !defined(__NR_epoll_ctl) ++#define __NR_epoll_ctl 21 ++#endif ++ ++#if !defined(__NR_epoll_pwait) ++#define __NR_epoll_pwait 22 ++#endif ++ ++#if !defined(__NR_dup) ++#define __NR_dup 23 ++#endif ++ ++#if !defined(__NR_dup3) ++#define __NR_dup3 24 ++#endif ++ ++#if !defined(__NR_fcntl) ++#define __NR_fcntl 25 ++#endif ++ ++#if !defined(__NR_inotify_init1) ++#define __NR_inotify_init1 26 ++#endif ++ ++#if !defined(__NR_inotify_add_watch) ++#define __NR_inotify_add_watch 27 ++#endif ++ ++#if !defined(__NR_inotify_rm_watch) ++#define __NR_inotify_rm_watch 28 ++#endif ++ ++#if !defined(__NR_ioctl) ++#define __NR_ioctl 29 ++#endif ++ ++#if !defined(__NR_ioprio_set) ++#define __NR_ioprio_set 30 ++#endif ++ ++#if !defined(__NR_ioprio_get) ++#define __NR_ioprio_get 31 ++#endif ++ ++#if !defined(__NR_flock) ++#define __NR_flock 32 ++#endif ++ ++#if !defined(__NR_mknodat) ++#define __NR_mknodat 33 ++#endif ++ ++#if !defined(__NR_mkdirat) ++#define __NR_mkdirat 34 ++#endif ++ ++#if !defined(__NR_unlinkat) ++#define __NR_unlinkat 35 ++#endif ++ ++#if !defined(__NR_symlinkat) ++#define __NR_symlinkat 36 ++#endif ++ ++#if !defined(__NR_linkat) ++#define __NR_linkat 37 ++#endif ++ ++#if !defined(__NR_renameat) ++#define __NR_renameat 38 ++#endif ++ ++#if !defined(__NR_umount2) ++#define __NR_umount2 39 ++#endif ++ ++#if !defined(__NR_mount) ++#define __NR_mount 40 ++#endif ++ ++#if !defined(__NR_pivot_root) ++#define __NR_pivot_root 41 ++#endif ++ ++#if !defined(__NR_nfsservctl) ++#define __NR_nfsservctl 42 ++#endif ++ ++#if !defined(__NR_statfs) ++#define __NR_statfs 43 ++#endif ++ ++#if !defined(__NR_fstatfs) ++#define __NR_fstatfs 44 ++#endif ++ ++#if !defined(__NR_truncate) ++#define __NR_truncate 45 ++#endif ++ ++#if !defined(__NR_ftruncate) ++#define __NR_ftruncate 46 ++#endif ++ ++#if !defined(__NR_fallocate) ++#define __NR_fallocate 47 ++#endif ++ ++#if !defined(__NR_faccessat) ++#define __NR_faccessat 48 ++#endif ++ ++#if !defined(__NR_chdir) ++#define __NR_chdir 49 ++#endif ++ ++#if !defined(__NR_fchdir) ++#define __NR_fchdir 50 ++#endif ++ ++#if !defined(__NR_chroot) ++#define __NR_chroot 51 ++#endif ++ ++#if !defined(__NR_fchmod) ++#define __NR_fchmod 52 ++#endif ++ ++#if !defined(__NR_fchmodat) ++#define __NR_fchmodat 53 ++#endif ++ ++#if !defined(__NR_fchownat) ++#define __NR_fchownat 54 ++#endif ++ ++#if !defined(__NR_fchown) ++#define __NR_fchown 55 ++#endif ++ ++#if !defined(__NR_openat) ++#define __NR_openat 56 ++#endif ++ ++#if !defined(__NR_close) ++#define __NR_close 57 ++#endif ++ ++#if !defined(__NR_vhangup) ++#define __NR_vhangup 58 ++#endif ++ ++#if !defined(__NR_pipe2) ++#define __NR_pipe2 59 ++#endif ++ ++#if !defined(__NR_quotactl) ++#define __NR_quotactl 60 ++#endif ++ ++#if !defined(__NR_getdents64) ++#define __NR_getdents64 61 ++#endif ++ ++#if !defined(__NR_lseek) ++#define __NR_lseek 62 ++#endif ++ ++#if !defined(__NR_read) ++#define __NR_read 63 ++#endif ++ ++#if !defined(__NR_write) ++#define __NR_write 64 ++#endif ++ ++#if !defined(__NR_readv) ++#define __NR_readv 65 ++#endif ++ ++#if !defined(__NR_writev) ++#define __NR_writev 66 ++#endif ++ ++#if !defined(__NR_pread64) ++#define __NR_pread64 67 ++#endif ++ ++#if !defined(__NR_pwrite64) ++#define __NR_pwrite64 68 ++#endif ++ ++#if !defined(__NR_preadv) ++#define __NR_preadv 69 ++#endif ++ ++#if !defined(__NR_pwritev) ++#define __NR_pwritev 70 ++#endif ++ ++#if !defined(__NR_sendfile) ++#define __NR_sendfile 71 ++#endif ++ ++#if !defined(__NR_pselect6) ++#define __NR_pselect6 72 ++#endif ++ ++#if !defined(__NR_ppoll) ++#define __NR_ppoll 73 ++#endif ++ ++#if !defined(__NR_signalfd4) ++#define __NR_signalfd4 74 ++#endif ++ ++#if !defined(__NR_vmsplice) ++#define __NR_vmsplice 75 ++#endif ++ ++#if !defined(__NR_splice) ++#define __NR_splice 76 ++#endif ++ ++#if !defined(__NR_tee) ++#define __NR_tee 77 ++#endif ++ ++#if !defined(__NR_readlinkat) ++#define __NR_readlinkat 78 ++#endif ++ ++#if !defined(__NR_newfstatat) ++#define __NR_newfstatat 79 ++#endif ++ ++#if !defined(__NR_fstat) ++#define __NR_fstat 80 ++#endif ++ ++#if !defined(__NR_sync) ++#define __NR_sync 81 ++#endif ++ ++#if !defined(__NR_fsync) ++#define __NR_fsync 82 ++#endif ++ ++#if !defined(__NR_fdatasync) ++#define __NR_fdatasync 83 ++#endif ++ ++#if !defined(__NR_sync_file_range) ++#define __NR_sync_file_range 84 ++#endif ++ ++#if !defined(__NR_timerfd_create) ++#define __NR_timerfd_create 85 ++#endif ++ ++#if !defined(__NR_timerfd_settime) ++#define __NR_timerfd_settime 86 ++#endif ++ ++#if !defined(__NR_timerfd_gettime) ++#define __NR_timerfd_gettime 87 ++#endif ++ ++#if !defined(__NR_utimensat) ++#define __NR_utimensat 88 ++#endif ++ ++#if !defined(__NR_acct) ++#define __NR_acct 89 ++#endif ++ ++#if !defined(__NR_capget) ++#define __NR_capget 90 ++#endif ++ ++#if !defined(__NR_capset) ++#define __NR_capset 91 ++#endif ++ ++#if !defined(__NR_personality) ++#define __NR_personality 92 ++#endif ++ ++#if !defined(__NR_exit) ++#define __NR_exit 93 ++#endif ++ ++#if !defined(__NR_exit_group) ++#define __NR_exit_group 94 ++#endif ++ ++#if !defined(__NR_waitid) ++#define __NR_waitid 95 ++#endif ++ ++#if !defined(__NR_set_tid_address) ++#define __NR_set_tid_address 96 ++#endif ++ ++#if !defined(__NR_unshare) ++#define __NR_unshare 97 ++#endif ++ ++#if !defined(__NR_futex) ++#define __NR_futex 98 ++#endif ++ ++#if !defined(__NR_set_robust_list) ++#define __NR_set_robust_list 99 ++#endif ++ ++#if !defined(__NR_get_robust_list) ++#define __NR_get_robust_list 100 ++#endif ++ ++#if !defined(__NR_nanosleep) ++#define __NR_nanosleep 101 ++#endif ++ ++#if !defined(__NR_getitimer) ++#define __NR_getitimer 102 ++#endif ++ ++#if !defined(__NR_setitimer) ++#define __NR_setitimer 103 ++#endif ++ ++#if !defined(__NR_kexec_load) ++#define __NR_kexec_load 104 ++#endif ++ ++#if !defined(__NR_init_module) ++#define __NR_init_module 105 ++#endif ++ ++#if !defined(__NR_delete_module) ++#define __NR_delete_module 106 ++#endif ++ ++#if !defined(__NR_timer_create) ++#define __NR_timer_create 107 ++#endif ++ ++#if !defined(__NR_timer_gettime) ++#define __NR_timer_gettime 108 ++#endif ++ ++#if !defined(__NR_timer_getoverrun) ++#define __NR_timer_getoverrun 109 ++#endif ++ ++#if !defined(__NR_timer_settime) ++#define __NR_timer_settime 110 ++#endif ++ ++#if !defined(__NR_timer_delete) ++#define __NR_timer_delete 111 ++#endif ++ ++#if !defined(__NR_clock_settime) ++#define __NR_clock_settime 112 ++#endif ++ ++#if !defined(__NR_clock_gettime) ++#define __NR_clock_gettime 113 ++#endif ++ ++#if !defined(__NR_clock_getres) ++#define __NR_clock_getres 114 ++#endif ++ ++#if !defined(__NR_clock_nanosleep) ++#define __NR_clock_nanosleep 115 ++#endif ++ ++#if !defined(__NR_syslog) ++#define __NR_syslog 116 ++#endif ++ ++#if !defined(__NR_ptrace) ++#define __NR_ptrace 117 ++#endif ++ ++#if !defined(__NR_sched_setparam) ++#define __NR_sched_setparam 118 ++#endif ++ ++#if !defined(__NR_sched_setscheduler) ++#define __NR_sched_setscheduler 119 ++#endif ++ ++#if !defined(__NR_sched_getscheduler) ++#define __NR_sched_getscheduler 120 ++#endif ++ ++#if !defined(__NR_sched_getparam) ++#define __NR_sched_getparam 121 ++#endif ++ ++#if !defined(__NR_sched_setaffinity) ++#define __NR_sched_setaffinity 122 ++#endif ++ ++#if !defined(__NR_sched_getaffinity) ++#define __NR_sched_getaffinity 123 ++#endif ++ ++#if !defined(__NR_sched_yield) ++#define __NR_sched_yield 124 ++#endif ++ ++#if !defined(__NR_sched_get_priority_max) ++#define __NR_sched_get_priority_max 125 ++#endif ++ ++#if !defined(__NR_sched_get_priority_min) ++#define __NR_sched_get_priority_min 126 ++#endif ++ ++#if !defined(__NR_sched_rr_get_interval) ++#define __NR_sched_rr_get_interval 127 ++#endif ++ ++#if !defined(__NR_restart_syscall) ++#define __NR_restart_syscall 128 ++#endif ++ ++#if !defined(__NR_kill) ++#define __NR_kill 129 ++#endif ++ ++#if !defined(__NR_tkill) ++#define __NR_tkill 130 ++#endif ++ ++#if !defined(__NR_tgkill) ++#define __NR_tgkill 131 ++#endif ++ ++#if !defined(__NR_sigaltstack) ++#define __NR_sigaltstack 132 ++#endif ++ ++#if !defined(__NR_rt_sigsuspend) ++#define __NR_rt_sigsuspend 133 ++#endif ++ ++#if !defined(__NR_rt_sigaction) ++#define __NR_rt_sigaction 134 ++#endif ++ ++#if !defined(__NR_rt_sigprocmask) ++#define __NR_rt_sigprocmask 135 ++#endif ++ ++#if !defined(__NR_rt_sigpending) ++#define __NR_rt_sigpending 136 ++#endif ++ ++#if !defined(__NR_rt_sigtimedwait) ++#define __NR_rt_sigtimedwait 137 ++#endif ++ ++#if !defined(__NR_rt_sigqueueinfo) ++#define __NR_rt_sigqueueinfo 138 ++#endif ++ ++#if !defined(__NR_rt_sigreturn) ++#define __NR_rt_sigreturn 139 ++#endif ++ ++#if !defined(__NR_setpriority) ++#define __NR_setpriority 140 ++#endif ++ ++#if !defined(__NR_getpriority) ++#define __NR_getpriority 141 ++#endif ++ ++#if !defined(__NR_reboot) ++#define __NR_reboot 142 ++#endif ++ ++#if !defined(__NR_setregid) ++#define __NR_setregid 143 ++#endif ++ ++#if !defined(__NR_setgid) ++#define __NR_setgid 144 ++#endif ++ ++#if !defined(__NR_setreuid) ++#define __NR_setreuid 145 ++#endif ++ ++#if !defined(__NR_setuid) ++#define __NR_setuid 146 ++#endif ++ ++#if !defined(__NR_setresuid) ++#define __NR_setresuid 147 ++#endif ++ ++#if !defined(__NR_getresuid) ++#define __NR_getresuid 148 ++#endif ++ ++#if !defined(__NR_setresgid) ++#define __NR_setresgid 149 ++#endif ++ ++#if !defined(__NR_getresgid) ++#define __NR_getresgid 150 ++#endif ++ ++#if !defined(__NR_setfsuid) ++#define __NR_setfsuid 151 ++#endif ++ ++#if !defined(__NR_setfsgid) ++#define __NR_setfsgid 152 ++#endif ++ ++#if !defined(__NR_times) ++#define __NR_times 153 ++#endif ++ ++#if !defined(__NR_setpgid) ++#define __NR_setpgid 154 ++#endif ++ ++#if !defined(__NR_getpgid) ++#define __NR_getpgid 155 ++#endif ++ ++#if !defined(__NR_getsid) ++#define __NR_getsid 156 ++#endif ++ ++#if !defined(__NR_setsid) ++#define __NR_setsid 157 ++#endif ++ ++#if !defined(__NR_getgroups) ++#define __NR_getgroups 158 ++#endif ++ ++#if !defined(__NR_setgroups) ++#define __NR_setgroups 159 ++#endif ++ ++#if !defined(__NR_uname) ++#define __NR_uname 160 ++#endif ++ ++#if !defined(__NR_sethostname) ++#define __NR_sethostname 161 ++#endif ++ ++#if !defined(__NR_setdomainname) ++#define __NR_setdomainname 162 ++#endif ++ ++#if !defined(__NR_getrlimit) ++#define __NR_getrlimit 163 ++#endif ++ ++#if !defined(__NR_setrlimit) ++#define __NR_setrlimit 164 ++#endif ++ ++#if !defined(__NR_getrusage) ++#define __NR_getrusage 165 ++#endif ++ ++#if !defined(__NR_umask) ++#define __NR_umask 166 ++#endif ++ ++#if !defined(__NR_prctl) ++#define __NR_prctl 167 ++#endif ++ ++#if !defined(__NR_getcpu) ++#define __NR_getcpu 168 ++#endif ++ ++#if !defined(__NR_gettimeofday) ++#define __NR_gettimeofday 169 ++#endif ++ ++#if !defined(__NR_settimeofday) ++#define __NR_settimeofday 170 ++#endif ++ ++#if !defined(__NR_adjtimex) ++#define __NR_adjtimex 171 ++#endif ++ ++#if !defined(__NR_getpid) ++#define __NR_getpid 172 ++#endif ++ ++#if !defined(__NR_getppid) ++#define __NR_getppid 173 ++#endif ++ ++#if !defined(__NR_getuid) ++#define __NR_getuid 174 ++#endif ++ ++#if !defined(__NR_geteuid) ++#define __NR_geteuid 175 ++#endif ++ ++#if !defined(__NR_getgid) ++#define __NR_getgid 176 ++#endif ++ ++#if !defined(__NR_getegid) ++#define __NR_getegid 177 ++#endif ++ ++#if !defined(__NR_gettid) ++#define __NR_gettid 178 ++#endif ++ ++#if !defined(__NR_sysinfo) ++#define __NR_sysinfo 179 ++#endif ++ ++#if !defined(__NR_mq_open) ++#define __NR_mq_open 180 ++#endif ++ ++#if !defined(__NR_mq_unlink) ++#define __NR_mq_unlink 181 ++#endif ++ ++#if !defined(__NR_mq_timedsend) ++#define __NR_mq_timedsend 182 ++#endif ++ ++#if !defined(__NR_mq_timedreceive) ++#define __NR_mq_timedreceive 183 ++#endif ++ ++#if !defined(__NR_mq_notify) ++#define __NR_mq_notify 184 ++#endif ++ ++#if !defined(__NR_mq_getsetattr) ++#define __NR_mq_getsetattr 185 ++#endif ++ ++#if !defined(__NR_msgget) ++#define __NR_msgget 186 ++#endif ++ ++#if !defined(__NR_msgctl) ++#define __NR_msgctl 187 ++#endif ++ ++#if !defined(__NR_msgrcv) ++#define __NR_msgrcv 188 ++#endif ++ ++#if !defined(__NR_msgsnd) ++#define __NR_msgsnd 189 ++#endif ++ ++#if !defined(__NR_semget) ++#define __NR_semget 190 ++#endif ++ ++#if !defined(__NR_semctl) ++#define __NR_semctl 191 ++#endif ++ ++#if !defined(__NR_semtimedop) ++#define __NR_semtimedop 192 ++#endif ++ ++#if !defined(__NR_semop) ++#define __NR_semop 193 ++#endif ++ ++#if !defined(__NR_shmget) ++#define __NR_shmget 194 ++#endif ++ ++#if !defined(__NR_shmctl) ++#define __NR_shmctl 195 ++#endif ++ ++#if !defined(__NR_shmat) ++#define __NR_shmat 196 ++#endif ++ ++#if !defined(__NR_shmdt) ++#define __NR_shmdt 197 ++#endif ++ ++#if !defined(__NR_socket) ++#define __NR_socket 198 ++#endif ++ ++#if !defined(__NR_socketpair) ++#define __NR_socketpair 199 ++#endif ++ ++#if !defined(__NR_bind) ++#define __NR_bind 200 ++#endif ++ ++#if !defined(__NR_listen) ++#define __NR_listen 201 ++#endif ++ ++#if !defined(__NR_accept) ++#define __NR_accept 202 ++#endif ++ ++#if !defined(__NR_connect) ++#define __NR_connect 203 ++#endif ++ ++#if !defined(__NR_getsockname) ++#define __NR_getsockname 204 ++#endif ++ ++#if !defined(__NR_getpeername) ++#define __NR_getpeername 205 ++#endif ++ ++#if !defined(__NR_sendto) ++#define __NR_sendto 206 ++#endif ++ ++#if !defined(__NR_recvfrom) ++#define __NR_recvfrom 207 ++#endif ++ ++#if !defined(__NR_setsockopt) ++#define __NR_setsockopt 208 ++#endif ++ ++#if !defined(__NR_getsockopt) ++#define __NR_getsockopt 209 ++#endif ++ ++#if !defined(__NR_shutdown) ++#define __NR_shutdown 210 ++#endif ++ ++#if !defined(__NR_sendmsg) ++#define __NR_sendmsg 211 ++#endif ++ ++#if !defined(__NR_recvmsg) ++#define __NR_recvmsg 212 ++#endif ++ ++#if !defined(__NR_readahead) ++#define __NR_readahead 213 ++#endif ++ ++#if !defined(__NR_brk) ++#define __NR_brk 214 ++#endif ++ ++#if !defined(__NR_munmap) ++#define __NR_munmap 215 ++#endif ++ ++#if !defined(__NR_mremap) ++#define __NR_mremap 216 ++#endif ++ ++#if !defined(__NR_add_key) ++#define __NR_add_key 217 ++#endif ++ ++#if !defined(__NR_request_key) ++#define __NR_request_key 218 ++#endif ++ ++#if !defined(__NR_keyctl) ++#define __NR_keyctl 219 ++#endif ++ ++#if !defined(__NR_clone) ++#define __NR_clone 220 ++#endif ++ ++#if !defined(__NR_execve) ++#define __NR_execve 221 ++#endif ++ ++#if !defined(__NR_mmap) ++#define __NR_mmap 222 ++#endif ++ ++#if !defined(__NR_fadvise64) ++#define __NR_fadvise64 223 ++#endif ++ ++#if !defined(__NR_swapon) ++#define __NR_swapon 224 ++#endif ++ ++#if !defined(__NR_swapoff) ++#define __NR_swapoff 225 ++#endif ++ ++#if !defined(__NR_mprotect) ++#define __NR_mprotect 226 ++#endif ++ ++#if !defined(__NR_msync) ++#define __NR_msync 227 ++#endif ++ ++#if !defined(__NR_mlock) ++#define __NR_mlock 228 ++#endif ++ ++#if !defined(__NR_munlock) ++#define __NR_munlock 229 ++#endif ++ ++#if !defined(__NR_mlockall) ++#define __NR_mlockall 230 ++#endif ++ ++#if !defined(__NR_munlockall) ++#define __NR_munlockall 231 ++#endif ++ ++#if !defined(__NR_mincore) ++#define __NR_mincore 232 ++#endif ++ ++#if !defined(__NR_madvise) ++#define __NR_madvise 233 ++#endif ++ ++#if !defined(__NR_remap_file_pages) ++#define __NR_remap_file_pages 234 ++#endif ++ ++#if !defined(__NR_mbind) ++#define __NR_mbind 235 ++#endif ++ ++#if !defined(__NR_get_mempolicy) ++#define __NR_get_mempolicy 236 ++#endif ++ ++#if !defined(__NR_set_mempolicy) ++#define __NR_set_mempolicy 237 ++#endif ++ ++#if !defined(__NR_migrate_pages) ++#define __NR_migrate_pages 238 ++#endif ++ ++#if !defined(__NR_move_pages) ++#define __NR_move_pages 239 ++#endif ++ ++#if !defined(__NR_rt_tgsigqueueinfo) ++#define __NR_rt_tgsigqueueinfo 240 ++#endif ++ ++#if !defined(__NR_perf_event_open) ++#define __NR_perf_event_open 241 ++#endif ++ ++#if !defined(__NR_accept4) ++#define __NR_accept4 242 ++#endif ++ ++#if !defined(__NR_recvmmsg) ++#define __NR_recvmmsg 243 ++#endif ++ ++#if !defined(__NR_wait4) ++#define __NR_wait4 260 ++#endif ++ ++#if !defined(__NR_prlimit64) ++#define __NR_prlimit64 261 ++#endif ++ ++#if !defined(__NR_fanotify_init) ++#define __NR_fanotify_init 262 ++#endif ++ ++#if !defined(__NR_fanotify_mark) ++#define __NR_fanotify_mark 263 ++#endif ++ ++#if !defined(__NR_name_to_handle_at) ++#define __NR_name_to_handle_at 264 ++#endif ++ ++#if !defined(__NR_open_by_handle_at) ++#define __NR_open_by_handle_at 265 ++#endif ++ ++#if !defined(__NR_clock_adjtime) ++#define __NR_clock_adjtime 266 ++#endif ++ ++#if !defined(__NR_syncfs) ++#define __NR_syncfs 267 ++#endif ++ ++#if !defined(__NR_setns) ++#define __NR_setns 268 ++#endif ++ ++#if !defined(__NR_sendmmsg) ++#define __NR_sendmmsg 269 ++#endif ++ ++#if !defined(__NR_process_vm_readv) ++#define __NR_process_vm_readv 270 ++#endif ++ ++#if !defined(__NR_process_vm_writev) ++#define __NR_process_vm_writev 271 ++#endif ++ ++#if !defined(__NR_kcmp) ++#define __NR_kcmp 272 ++#endif ++ ++#if !defined(__NR_finit_module) ++#define __NR_finit_module 273 ++#endif ++ ++#if !defined(__NR_sched_setattr) ++#define __NR_sched_setattr 274 ++#endif ++ ++#if !defined(__NR_sched_getattr) ++#define __NR_sched_getattr 275 ++#endif ++ ++#if !defined(__NR_renameat2) ++#define __NR_renameat2 276 ++#endif ++ ++#if !defined(__NR_seccomp) ++#define __NR_seccomp 277 ++#endif ++ ++#if !defined(__NR_getrandom) ++#define __NR_getrandom 278 ++#endif ++ ++#if !defined(__NR_memfd_create) ++#define __NR_memfd_create 279 ++#endif ++ ++#endif // SANDBOX_LINUX_SYSTEM_HEADERS_RISCV64_LINUX_SYSCALLS_H_ +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/sandbox/policy/linux/bpf_cros_amd_gpu_policy_linux.cc +=================================================================== +--- qtwebengine-everywhere-src-5.15.7.orig/src/3rdparty/chromium/sandbox/policy/linux/bpf_cros_amd_gpu_policy_linux.cc ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/sandbox/policy/linux/bpf_cros_amd_gpu_policy_linux.cc +@@ -38,7 +38,7 @@ ResultExpr CrosAmdGpuProcessPolicy::Eval + case __NR_sched_setscheduler: + case __NR_sysinfo: + case __NR_uname: +-#if !defined(__aarch64__) ++#if !defined(__aarch64__) && !defined(__riscv) + case __NR_readlink: + case __NR_stat: + #endif +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/sandbox/policy/linux/bpf_gpu_policy_linux.cc +=================================================================== +--- qtwebengine-everywhere-src-5.15.7.orig/src/3rdparty/chromium/sandbox/policy/linux/bpf_gpu_policy_linux.cc ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/sandbox/policy/linux/bpf_gpu_policy_linux.cc +@@ -70,7 +70,7 @@ ResultExpr GpuProcessPolicy::EvaluateSys + (defined(ARCH_CPU_MIPS_FAMILY) && defined(ARCH_CPU_32_BITS)) + case __NR_ftruncate64: + #endif +-#if !defined(__aarch64__) ++#if !defined(__aarch64__) && !defined(__riscv) + case __NR_getdents: + #endif + case __NR_getdents64: +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/sandbox/policy/linux/sandbox_seccomp_bpf_linux.cc +=================================================================== +--- qtwebengine-everywhere-src-5.15.7.orig/src/3rdparty/chromium/sandbox/policy/linux/sandbox_seccomp_bpf_linux.cc ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/sandbox/policy/linux/sandbox_seccomp_bpf_linux.cc +@@ -64,7 +64,7 @@ using sandbox::bpf_dsl::ResultExpr; + + // Make sure that seccomp-bpf does not get disabled by mistake. Also make sure + // that we think twice about this when adding a new architecture. +-#if !defined(ARCH_CPU_ARM64) && !defined(ARCH_CPU_MIPS64EL) ++#if !defined(ARCH_CPU_ARM64) && !defined(ARCH_CPU_MIPS64EL) && !defined(ARCH_CPU_RISCV64) + #error "Seccomp-bpf disabled on supported architecture!" + #endif // !defined(ARCH_CPU_ARM64) && !defined(ARCH_CPU_MIPS64EL) + +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/skia/BUILD.gn +=================================================================== +--- qtwebengine-everywhere-src-5.15.7.orig/src/3rdparty/chromium/skia/BUILD.gn ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/skia/BUILD.gn +@@ -821,6 +821,8 @@ skia_source_set("skia_opts") { + sources = skia_opts.none_sources + } else if (current_cpu == "s390x") { + sources = skia_opts.none_sources ++ } else if (current_cpu == "riscv64") { ++ sources = skia_opts.none_sources + } else { + assert(false, "Need to port cpu specific stuff from skia_library_opts.gyp") + } +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/angle/gni/angle.gni +=================================================================== +--- qtwebengine-everywhere-src-5.15.7.orig/src/3rdparty/chromium/third_party/angle/gni/angle.gni ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/angle/gni/angle.gni +@@ -54,7 +54,7 @@ angle_data_dir = "angledata" + declare_args() { + if (current_cpu == "arm64" || current_cpu == "x64" || + current_cpu == "mips64el" || current_cpu == "s390x" || +- current_cpu == "ppc64") { ++ current_cpu == "ppc64" || current_cpu == "riscv64") { + angle_64bit_current_cpu = true + } else if (current_cpu == "arm" || current_cpu == "x86" || + current_cpu == "mipsel" || current_cpu == "s390" || +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/angle/include/platform/PlatformMethods.h +=================================================================== +--- qtwebengine-everywhere-src-5.15.7.orig/src/3rdparty/chromium/third_party/angle/include/platform/PlatformMethods.h ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/angle/include/platform/PlatformMethods.h +@@ -9,6 +9,7 @@ + #ifndef ANGLE_PLATFORMMETHODS_H + #define ANGLE_PLATFORMMETHODS_H + ++#include + #include + #include + #include +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/blink/renderer/platform/heap/asm/BUILD.gn +=================================================================== +--- qtwebengine-everywhere-src-5.15.7.orig/src/3rdparty/chromium/third_party/blink/renderer/platform/heap/asm/BUILD.gn ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/blink/renderer/platform/heap/asm/BUILD.gn +@@ -38,6 +38,8 @@ if (current_cpu == "x86" || current_cpu + sources = [ "SaveRegisters_mips64.S" ] + } else if (current_cpu == "ppc64") { + sources = [ "SaveRegisters_ppc64.S" ] ++ } else if (current_cpu == "riscv64") { ++ sources = [ "SaveRegisters_riscv64.S" ] + } + + if (current_cpu == "arm") { +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/blink/renderer/platform/heap/asm/SaveRegisters_riscv64.S +=================================================================== +--- /dev/null ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/blink/renderer/platform/heap/asm/SaveRegisters_riscv64.S +@@ -0,0 +1,45 @@ ++/* ++ * typedef void (*PushAllRegistersCallback)(ThreadState*, intptr_t*); ++ * extern "C" void PushAllRegisters(ThreadState*, PushAllRegistersCallback) ++ */ ++ ++.type PushAllRegisters, %function ++.global PushAllRegisters ++.hidden PushAllRegisters ++PushAllRegisters: ++ /* Push all callee-saves registers to get them ++ * on the stack for conservative stack scanning. ++ * Reserve space for callee-saved registers and return address. ++ */ ++ addi sp,sp,-112 ++ /* Save the callee-saved registers and the return address. */ ++ sd ra,0(sp) ++ sd s0,8(sp) ++ sd s1,16(sp) ++ sd s2,24(sp) ++ sd s3,32(sp) ++ sd s4,40(sp) ++ sd s5,48(sp) ++ sd s6,56(sp) ++ sd s7,64(sp) ++ sd s8,72(sp) ++ sd s9,80(sp) ++ sd s10,88(sp) ++ sd s11,96(sp) ++ /* Note: the callee-saved floating point registers do not need to be ++ * copied to the stack, because fp registers never hold heap pointers ++ * and so do not need to be kept visible to the garbage collector. ++ * Pass the first argument untouched in a0 and the ++ * stack pointer to the callback. ++ */ ++ mv ra,a1 ++ mv a1,sp ++ jalr ra ++ /* Restore return address, adjust stack and return. ++ * Note: the copied registers do not need to be reloaded here, ++ * because they were preserved by the called routine. ++ */ ++ ld ra,0(sp) ++ addi sp,sp,112 ++ ret ++.size PushAllRegisters, . - PushAllRegisters +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/boringssl/src/include/openssl/base.h +=================================================================== +--- qtwebengine-everywhere-src-5.15.7.orig/src/3rdparty/chromium/third_party/boringssl/src/include/openssl/base.h ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/boringssl/src/include/openssl/base.h +@@ -114,6 +114,8 @@ extern "C" { + #define OPENSSL_32_BIT + #elif defined(__myriad2__) + #define OPENSSL_32_BIT ++#elif defined(__riscv) && __riscv_xlen == 64 ++#define OPENSSL_64_BIT + #else + // Note BoringSSL only supports standard 32-bit and 64-bit two's-complement, + // little-endian architectures. Functions will not produce the correct answer +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/breakpad/breakpad/src/client/linux/dump_writer_common/raw_context_cpu.h +=================================================================== +--- qtwebengine-everywhere-src-5.15.7.orig/src/3rdparty/chromium/third_party/breakpad/breakpad/src/client/linux/dump_writer_common/raw_context_cpu.h ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/breakpad/breakpad/src/client/linux/dump_writer_common/raw_context_cpu.h +@@ -44,6 +44,8 @@ typedef MDRawContextARM RawContextCPU; + typedef MDRawContextARM64_Old RawContextCPU; + #elif defined(__mips__) + typedef MDRawContextMIPS RawContextCPU; ++#elif defined(__riscv) ++typedef MDRawContextRISCV64 RawContextCPU; + #else + #error "This code has not been ported to your platform yet." + #endif +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/breakpad/breakpad/src/client/linux/dump_writer_common/thread_info.cc +=================================================================== +--- qtwebengine-everywhere-src-5.15.7.orig/src/3rdparty/chromium/third_party/breakpad/breakpad/src/client/linux/dump_writer_common/thread_info.cc ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/breakpad/breakpad/src/client/linux/dump_writer_common/thread_info.cc +@@ -270,7 +270,23 @@ void ThreadInfo::FillCPUContext(RawConte + out->float_save.fir = mcontext.fpc_eir; + #endif + } +-#endif // __mips__ ++#elif defined(__riscv) ++ ++uintptr_t ThreadInfo::GetInstructionPointer() const { ++ return mcontext.__gregs[REG_PC]; ++} ++ ++void ThreadInfo::FillCPUContext(RawContextCPU* out) const { ++ out->context_flags = MD_CONTEXT_RISCV64_FULL; ++ ++ my_memcpy (out->iregs, mcontext.__gregs, MD_CONTEXT_RISCV64_GPR_COUNT * 8); ++ ++ out->float_save.fcsr = mcontext.__fpregs.__d.__fcsr; ++ my_memcpy(&out->float_save.regs, &mcontext.__fpregs.__d.__f, ++ MD_FLOATINGSAVEAREA_RISCV64_FPR_COUNT * 8); ++} ++ ++#endif // __riscv + + void ThreadInfo::GetGeneralPurposeRegisters(void** gp_regs, size_t* size) { + assert(gp_regs || size); +@@ -279,6 +295,11 @@ void ThreadInfo::GetGeneralPurposeRegist + *gp_regs = mcontext.gregs; + if (size) + *size = sizeof(mcontext.gregs); ++#elif defined(__riscv) ++ if (gp_regs) ++ *gp_regs = mcontext.__gregs; ++ if (size) ++ *size = sizeof(mcontext.__gregs); + #else + if (gp_regs) + *gp_regs = ®s; +@@ -294,6 +315,11 @@ void ThreadInfo::GetFloatingPointRegiste + *fp_regs = &mcontext.fpregs; + if (size) + *size = sizeof(mcontext.fpregs); ++#elif defined(__riscv) ++ if (fp_regs) ++ *fp_regs = &mcontext.__fpregs; ++ if (size) ++ *size = sizeof(mcontext.__fpregs); + #else + if (fp_regs) + *fp_regs = &fpregs; +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/breakpad/breakpad/src/client/linux/dump_writer_common/thread_info.h +=================================================================== +--- qtwebengine-everywhere-src-5.15.7.orig/src/3rdparty/chromium/third_party/breakpad/breakpad/src/client/linux/dump_writer_common/thread_info.h ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/breakpad/breakpad/src/client/linux/dump_writer_common/thread_info.h +@@ -68,7 +68,7 @@ struct ThreadInfo { + // Use the structures defined in + struct user_regs_struct regs; + struct user_fpsimd_struct fpregs; +-#elif defined(__mips__) ++#elif defined(__mips__) || defined(__riscv) + // Use the structure defined in . + mcontext_t mcontext; + #endif +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/breakpad/breakpad/src/client/linux/handler/exception_handler.cc +=================================================================== +--- qtwebengine-everywhere-src-5.15.7.orig/src/3rdparty/chromium/third_party/breakpad/breakpad/src/client/linux/handler/exception_handler.cc ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/breakpad/breakpad/src/client/linux/handler/exception_handler.cc +@@ -461,7 +461,7 @@ bool ExceptionHandler::HandleSignal(int + memcpy(&g_crash_context_.float_state, fp_ptr, + sizeof(g_crash_context_.float_state)); + } +-#elif !defined(__ARM_EABI__) && !defined(__mips__) ++#elif !defined(__ARM_EABI__) && !defined(__mips__) && !defined(__riscv) + // FP state is not part of user ABI on ARM Linux. + // In case of MIPS Linux FP state is already part of ucontext_t + // and 'float_state' is not a member of CrashContext. +@@ -701,7 +701,7 @@ bool ExceptionHandler::WriteMinidump() { + } + #endif + +-#if !defined(__ARM_EABI__) && !defined(__aarch64__) && !defined(__mips__) ++#if !defined(__ARM_EABI__) && !defined(__aarch64__) && !defined(__mips__) && !defined(__riscv) + // FPU state is not part of ARM EABI ucontext_t. + memcpy(&context.float_state, context.context.uc_mcontext.fpregs, + sizeof(context.float_state)); +@@ -726,6 +726,9 @@ bool ExceptionHandler::WriteMinidump() { + #elif defined(__mips__) + context.siginfo.si_addr = + reinterpret_cast(context.context.uc_mcontext.pc); ++#elif defined(__riscv) ++ context.siginfo.si_addr = ++ reinterpret_cast(context.context.uc_mcontext.__gregs[REG_PC]); + #else + #error "This code has not been ported to your platform yet." + #endif +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/breakpad/breakpad/src/client/linux/handler/exception_handler.h +=================================================================== +--- qtwebengine-everywhere-src-5.15.7.orig/src/3rdparty/chromium/third_party/breakpad/breakpad/src/client/linux/handler/exception_handler.h ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/breakpad/breakpad/src/client/linux/handler/exception_handler.h +@@ -192,7 +192,7 @@ class ExceptionHandler { + siginfo_t siginfo; + pid_t tid; // the crashing thread. + ucontext_t context; +-#if !defined(__ARM_EABI__) && !defined(__mips__) ++#if !defined(__ARM_EABI__) && !defined(__mips__) && !defined(__riscv) + // #ifdef this out because FP state is not part of user ABI for Linux ARM. + // In case of MIPS Linux FP state is already part of ucontext_t so + // 'float_state' is not required. +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/breakpad/breakpad/src/client/linux/microdump_writer/microdump_writer.cc +=================================================================== +--- qtwebengine-everywhere-src-5.15.7.orig/src/3rdparty/chromium/third_party/breakpad/breakpad/src/client/linux/microdump_writer/microdump_writer.cc ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/breakpad/breakpad/src/client/linux/microdump_writer/microdump_writer.cc +@@ -138,7 +138,7 @@ class MicrodumpWriter { + const MicrodumpExtraInfo& microdump_extra_info, + LinuxDumper* dumper) + : ucontext_(context ? &context->context : NULL), +-#if !defined(__ARM_EABI__) && !defined(__mips__) ++#if !defined(__ARM_EABI__) && !defined(__mips__) && !defined(__riscv) + float_state_(context ? &context->float_state : NULL), + #endif + dumper_(dumper), +@@ -337,6 +337,12 @@ class MicrodumpWriter { + # else + # error "This mips ABI is currently not supported (n32)" + #endif ++#elif defined(__riscv) ++# if __riscv_xlen == 64 ++ const char kArch[] = "riscv64"; ++# else ++# error "This RISC-V ABI is currently not supported" ++#endif + #else + #error "This code has not been ported to your platform yet" + #endif +@@ -409,7 +415,7 @@ class MicrodumpWriter { + void DumpCPUState() { + RawContextCPU cpu; + my_memset(&cpu, 0, sizeof(RawContextCPU)); +-#if !defined(__ARM_EABI__) && !defined(__mips__) ++#if !defined(__ARM_EABI__) && !defined(__mips__) && !defined(__riscv) + UContextReader::FillCPUContext(&cpu, ucontext_, float_state_); + #else + UContextReader::FillCPUContext(&cpu, ucontext_); +@@ -605,7 +611,7 @@ class MicrodumpWriter { + void* Alloc(unsigned bytes) { return dumper_->allocator()->Alloc(bytes); } + + const ucontext_t* const ucontext_; +-#if !defined(__ARM_EABI__) && !defined(__mips__) ++#if !defined(__ARM_EABI__) && !defined(__mips__) && !defined(__riscv) + const google_breakpad::fpstate_t* const float_state_; + #endif + LinuxDumper* dumper_; +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/breakpad/breakpad/src/client/linux/minidump_writer/linux_core_dumper.cc +=================================================================== +--- qtwebengine-everywhere-src-5.15.7.orig/src/3rdparty/chromium/third_party/breakpad/breakpad/src/client/linux/minidump_writer/linux_core_dumper.cc ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/breakpad/breakpad/src/client/linux/minidump_writer/linux_core_dumper.cc +@@ -112,6 +112,9 @@ bool LinuxCoreDumper::GetThreadInfoByInd + #elif defined(__mips__) + stack_pointer = + reinterpret_cast(info->mcontext.gregs[MD_CONTEXT_MIPS_REG_SP]); ++#elif defined(__riscv) ++ stack_pointer = ++ reinterpret_cast(info->mcontext.__gregs[MD_CONTEXT_RISCV64_REG_SP]); + #else + #error "This code hasn't been ported to your platform yet." + #endif +@@ -208,6 +211,8 @@ bool LinuxCoreDumper::EnumerateThreads() + info.mcontext.mdlo = status->pr_reg[EF_LO]; + info.mcontext.mdhi = status->pr_reg[EF_HI]; + info.mcontext.pc = status->pr_reg[EF_CP0_EPC]; ++#elif defined(__riscv) ++ memcpy(info.mcontext.__gregs, status->pr_reg, sizeof(info.mcontext.__gregs)); + #else // __mips__ + memcpy(&info.regs, status->pr_reg, sizeof(info.regs)); + #endif // __mips__ +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/breakpad/breakpad/src/client/linux/minidump_writer/linux_dumper.h +=================================================================== +--- qtwebengine-everywhere-src-5.15.7.orig/src/3rdparty/chromium/third_party/breakpad/breakpad/src/client/linux/minidump_writer/linux_dumper.h ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/breakpad/breakpad/src/client/linux/minidump_writer/linux_dumper.h +@@ -63,7 +63,8 @@ namespace google_breakpad { + (defined(__mips__) && _MIPS_SIM == _ABIO32) + typedef Elf32_auxv_t elf_aux_entry; + #elif defined(__x86_64) || defined(__aarch64__) || \ +- (defined(__mips__) && _MIPS_SIM != _ABIO32) ++ (defined(__mips__) && _MIPS_SIM != _ABIO32) || \ ++ (defined(__riscv) && __riscv_xlen == 64) + typedef Elf64_auxv_t elf_aux_entry; + #endif + +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/breakpad/breakpad/src/client/linux/minidump_writer/linux_ptrace_dumper.cc +=================================================================== +--- qtwebengine-everywhere-src-5.15.7.orig/src/3rdparty/chromium/third_party/breakpad/breakpad/src/client/linux/minidump_writer/linux_ptrace_dumper.cc ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/breakpad/breakpad/src/client/linux/minidump_writer/linux_ptrace_dumper.cc +@@ -298,6 +298,9 @@ bool LinuxPtraceDumper::GetThreadInfoByI + #elif defined(__mips__) + stack_pointer = + reinterpret_cast(info->mcontext.gregs[MD_CONTEXT_MIPS_REG_SP]); ++#elif defined(__riscv) ++ stack_pointer = ++ reinterpret_cast(info->mcontext.__gregs[MD_CONTEXT_RISCV64_REG_SP]); + #else + #error "This code hasn't been ported to your platform yet." + #endif +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/breakpad/breakpad/src/client/linux/minidump_writer/minidump_writer.cc +=================================================================== +--- qtwebengine-everywhere-src-5.15.7.orig/src/3rdparty/chromium/third_party/breakpad/breakpad/src/client/linux/minidump_writer/minidump_writer.cc ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/breakpad/breakpad/src/client/linux/minidump_writer/minidump_writer.cc +@@ -136,7 +136,7 @@ class MinidumpWriter { + : fd_(minidump_fd), + path_(minidump_path), + ucontext_(context ? &context->context : NULL), +-#if !defined(__ARM_EABI__) && !defined(__mips__) ++#if !defined(__ARM_EABI__) && !defined(__mips__) && !defined(__riscv) + float_state_(context ? &context->float_state : NULL), + #endif + dumper_(dumper), +@@ -468,7 +468,7 @@ class MinidumpWriter { + if (!cpu.Allocate()) + return false; + my_memset(cpu.get(), 0, sizeof(RawContextCPU)); +-#if !defined(__ARM_EABI__) && !defined(__mips__) ++#if !defined(__ARM_EABI__) && !defined(__mips__) && !defined(__riscv) + UContextReader::FillCPUContext(cpu.get(), ucontext_, float_state_); + #else + UContextReader::FillCPUContext(cpu.get(), ucontext_); +@@ -897,7 +897,7 @@ class MinidumpWriter { + dirent->location.rva = 0; + } + +-#if defined(__i386__) || defined(__x86_64__) || defined(__mips__) ++#if defined(__i386__) || defined(__x86_64__) || defined(__mips__) || defined(__riscv) + bool WriteCPUInformation(MDRawSystemInfo* sys_info) { + char vendor_id[sizeof(sys_info->cpu.x86_cpu_info.vendor_id) + 1] = {0}; + static const char vendor_id_name[] = "vendor_id"; +@@ -925,6 +925,12 @@ class MinidumpWriter { + # else + # error "This mips ABI is currently not supported (n32)" + #endif ++#elif defined(__riscv) ++# if __riscv_xlen == 64 ++ MD_CPU_ARCHITECTURE_RISCV64; ++# else ++# error "This RISC-V ABI is currently not supported" ++# endif + #elif defined(__i386__) + MD_CPU_ARCHITECTURE_X86; + #else +@@ -1333,7 +1339,7 @@ class MinidumpWriter { + const char* path_; // Path to the file where the minidum should be written. + + const ucontext_t* const ucontext_; // also from the signal handler +-#if !defined(__ARM_EABI__) && !defined(__mips__) ++#if !defined(__ARM_EABI__) && !defined(__mips__) && !defined(__riscv) + const google_breakpad::fpstate_t* const float_state_; // ditto + #endif + LinuxDumper* dumper_; +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/breakpad/breakpad/src/client/linux/minidump_writer/minidump_writer.h +=================================================================== +--- qtwebengine-everywhere-src-5.15.7.orig/src/3rdparty/chromium/third_party/breakpad/breakpad/src/client/linux/minidump_writer/minidump_writer.h ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/breakpad/breakpad/src/client/linux/minidump_writer/minidump_writer.h +@@ -48,7 +48,7 @@ class ExceptionHandler; + + #if defined(__aarch64__) + typedef struct fpsimd_context fpstate_t; +-#elif !defined(__ARM_EABI__) && !defined(__mips__) ++#elif !defined(__ARM_EABI__) && !defined(__mips__) && !defined(__riscv) + typedef std::remove_pointer::type fpstate_t; + #endif + +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/breakpad/breakpad/src/common/linux/breakpad_getcontext.S +=================================================================== +--- qtwebengine-everywhere-src-5.15.7.orig/src/3rdparty/chromium/third_party/breakpad/breakpad/src/common/linux/breakpad_getcontext.S ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/breakpad/breakpad/src/common/linux/breakpad_getcontext.S +@@ -481,6 +481,68 @@ breakpad_getcontext: + .cfi_endproc + .size breakpad_getcontext, . - breakpad_getcontext + ++#elif defined(__riscv) && __riscv_xlen == 64 ++ ++#define __NR_rt_sigprocmask 135 ++#define _NSIG8 64 / 8 ++#define SIG_BLOCK 0 ++ ++ .text ++ .global breakpad_getcontext ++ .hidden breakpad_getcontext ++ .type breakpad_getcontext, @function ++ .align 2 ++breakpad_getcontext: ++ sd ra, MCONTEXT_GREGS_OFFSET + 0*8(a0) ++ sd ra, MCONTEXT_GREGS_OFFSET + 1*8(a0) ++ sd sp, MCONTEXT_GREGS_OFFSET + 2*8(a0) ++ sd s0, MCONTEXT_GREGS_OFFSET + 8*8(a0) ++ sd s1, MCONTEXT_GREGS_OFFSET + 9*8(a0) ++ sd x0, MCONTEXT_GREGS_OFFSET + 10*8(a0) /* return 0 by overwriting a0. */ ++ sd s2, MCONTEXT_GREGS_OFFSET + 18*8(a0) ++ sd s3, MCONTEXT_GREGS_OFFSET + 19*8(a0) ++ sd s4, MCONTEXT_GREGS_OFFSET + 20*8(a0) ++ sd s5, MCONTEXT_GREGS_OFFSET + 21*8(a0) ++ sd s6, MCONTEXT_GREGS_OFFSET + 22*8(a0) ++ sd s7, MCONTEXT_GREGS_OFFSET + 23*8(a0) ++ sd s8, MCONTEXT_GREGS_OFFSET + 24*8(a0) ++ sd s9, MCONTEXT_GREGS_OFFSET + 25*8(a0) ++ sd s10, MCONTEXT_GREGS_OFFSET + 26*8(a0) ++ sd s11, MCONTEXT_GREGS_OFFSET + 27*8(a0) ++ ++#ifndef __riscv_float_abi_soft ++ frsr a1 ++ ++ fsd fs0, MCONTEXT_FPREGS_OFFSET + 8*8(a0) ++ fsd fs1, MCONTEXT_FPREGS_OFFSET + 9*8(a0) ++ fsd fs2, MCONTEXT_FPREGS_OFFSET + 18*8(a0) ++ fsd fs3, MCONTEXT_FPREGS_OFFSET + 19*8(a0) ++ fsd fs4, MCONTEXT_FPREGS_OFFSET + 20*8(a0) ++ fsd fs5, MCONTEXT_FPREGS_OFFSET + 21*8(a0) ++ fsd fs6, MCONTEXT_FPREGS_OFFSET + 22*8(a0) ++ fsd fs7, MCONTEXT_FPREGS_OFFSET + 23*8(a0) ++ fsd fs8, MCONTEXT_FPREGS_OFFSET + 24*8(a0) ++ fsd fs9, MCONTEXT_FPREGS_OFFSET + 25*8(a0) ++ fsd fs10, MCONTEXT_FPREGS_OFFSET + 26*8(a0) ++ fsd fs11, MCONTEXT_FPREGS_OFFSET + 27*8(a0) ++ ++ sw a1, MCONTEXT_FSR_OFFSET(a0) ++#endif /* __riscv_float_abi_soft */ ++ ++/* rt_sigprocmask (SIG_BLOCK, NULL, &ucp->uc_sigmask, _NSIG / 8) */ ++ li a3, _NSIG8 ++ add a2, a0, UCONTEXT_SIGMASK_OFFSET ++ mv a1, zero ++ li a0, SIG_BLOCK ++ ++ li a7, __NR_rt_sigprocmask ++ scall ++ ++ /* Always return 0 for success, even if sigprocmask failed. */ ++ mv a0, zero ++ ret ++ .size breakpad_getcontext, . - breakpad_getcontext ++ + #else + #error "This file has not been ported for your CPU!" + #endif +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/breakpad/breakpad/src/common/linux/memory_mapped_file.cc +=================================================================== +--- qtwebengine-everywhere-src-5.15.7.orig/src/3rdparty/chromium/third_party/breakpad/breakpad/src/common/linux/memory_mapped_file.cc ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/breakpad/breakpad/src/common/linux/memory_mapped_file.cc +@@ -65,7 +65,8 @@ bool MemoryMappedFile::Map(const char* p + } + + #if defined(__x86_64__) || defined(__aarch64__) || \ +- (defined(__mips__) && _MIPS_SIM == _ABI64) ++ (defined(__mips__) && _MIPS_SIM == _ABI64) || \ ++ (defined(__riscv) && __riscv_xlen == 64) + + struct kernel_stat st; + if (sys_fstat(fd, &st) == -1 || st.st_size < 0) { +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/breakpad/breakpad/src/common/linux/ucontext_constants.h +=================================================================== +--- qtwebengine-everywhere-src-5.15.7.orig/src/3rdparty/chromium/third_party/breakpad/breakpad/src/common/linux/ucontext_constants.h ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/breakpad/breakpad/src/common/linux/ucontext_constants.h +@@ -146,6 +146,14 @@ + #endif + #define FPREGS_OFFSET_MXCSR 24 + ++#elif defined(__riscv) ++ ++#define UCONTEXT_SIGMASK_OFFSET 40 ++ ++#define MCONTEXT_GREGS_OFFSET 176 ++#define MCONTEXT_FPREGS_OFFSET 432 ++#define MCONTEXT_FSR_OFFSET (MCONTEXT_FPREGS_OFFSET + 32*8) ++ + #else + #error "This header has not been ported for your CPU" + #endif +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/breakpad/breakpad/src/google_breakpad/common/minidump_cpu_riscv64.h +=================================================================== +--- /dev/null ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/breakpad/breakpad/src/google_breakpad/common/minidump_cpu_riscv64.h +@@ -0,0 +1,121 @@ ++/* Copyright 2013 Google 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. ++ * * Redistributions in binary form must reproduce the above ++ * copyright notice, this list of conditions and the following disclaimer ++ * in the documentation and/or other materials provided with the ++ * distribution. ++ * * Neither the name of Google Inc. nor the names of its ++ * contributors may be used to endorse or promote products derived from ++ * this software without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ++ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ++ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ ++ ++/* minidump_format.h: A cross-platform reimplementation of minidump-related ++ * portions of DbgHelp.h from the Windows Platform SDK. ++ * ++ * (This is C99 source, please don't corrupt it with C++.) ++ * ++ * This file contains the necessary definitions to read minidump files ++ * produced on ARM. These files may be read on any platform provided ++ * that the alignments of these structures on the processing system are ++ * identical to the alignments of these structures on the producing system. ++ * For this reason, precise-sized types are used. The structures defined ++ * by this file have been laid out to minimize alignment problems by ++ * ensuring that all members are aligned on their natural boundaries. ++ * In some cases, tail-padding may be significant when different ABIs specify ++ * different tail-padding behaviors. To avoid problems when reading or ++ * writing affected structures, MD_*_SIZE macros are provided where needed, ++ * containing the useful size of the structures without padding. ++ * ++ * Structures that are defined by Microsoft to contain a zero-length array ++ * are instead defined here to contain an array with one element, as ++ * zero-length arrays are forbidden by standard C and C++. In these cases, ++ * *_minsize constants are provided to be used in place of sizeof. For a ++ * cleaner interface to these sizes when using C++, see minidump_size.h. ++ * ++ * These structures are also sufficient to populate minidump files. ++ * ++ * Because precise data type sizes are crucial for this implementation to ++ * function properly and portably, a set of primitive types with known sizes ++ * are used as the basis of each structure defined by this file. ++ * ++ * Author: Colin Blundell ++ */ ++ ++/* ++ * RISCV64 support ++ */ ++ ++#ifndef GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_RISCV64_H__ ++#define GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_RISCV64_H__ ++ ++#include "google_breakpad/common/breakpad_types.h" ++ ++#define MD_FLOATINGSAVEAREA_RISCV64_FPR_COUNT 32 ++#define MD_CONTEXT_RISCV64_GPR_COUNT 32 ++ ++typedef struct { ++ /* 32 64-bit floating point registers, f0 .. f31. */ ++ uint64_t regs[MD_FLOATINGSAVEAREA_RISCV64_FPR_COUNT]; ++ ++ uint32_t fcsr; /* FPU control and status register */ ++} MDFloatingSaveAreaRISCV64; ++ ++/* For (MDRawContextRISCV64).context_flags. These values indicate the type of ++ * context stored in the structure. */ ++#define MD_CONTEXT_RISCV64 0x00400000 ++#define MD_CONTEXT_RISCV64_CONTROL (MD_CONTEXT_RISCV64 | 0x00000001) ++#define MD_CONTEXT_RISCV64_INTEGER (MD_CONTEXT_RISCV64 | 0x00000002) ++#define MD_CONTEXT_RISCV64_FLOATING_POINT (MD_CONTEXT_RISCV64 | 0x00000004) ++#define MD_CONTEXT_RISCV64_DEBUG (MD_CONTEXT_RISCV64 | 0x00000008) ++#define MD_CONTEXT_RISCV64_FULL (MD_CONTEXT_RISCV64_CONTROL | \ ++ MD_CONTEXT_RISCV64_INTEGER | \ ++ MD_CONTEXT_RISCV64_FLOATING_POINT) ++#define MD_CONTEXT_RISCV64_ALL (MD_CONTEXT_RISCV64_FULL | MD_CONTEXT_RISCV64_DEBUG) ++ ++typedef struct { ++ /* Determines which fields of this struct are populated */ ++ uint32_t context_flags; ++ ++ /* 32 64-bit integer registers, x1 .. x31 + the PC ++ * Note the following fixed uses: ++ * x8 is the frame pointer ++ * x1 is the link register ++ * x2 is the stack pointer ++ * The PC is effectively x0. ++ */ ++ uint64_t iregs[MD_CONTEXT_RISCV64_GPR_COUNT]; ++ ++ /* The next field is included with MD_CONTEXT64_ARM_FLOATING_POINT */ ++ MDFloatingSaveAreaRISCV64 float_save; ++ ++} MDRawContextRISCV64; ++ ++/* Indices into iregs for registers with a dedicated or conventional ++ * purpose. ++ */ ++enum MDRISCV64RegisterNumbers { ++ MD_CONTEXT_RISCV64_REG_FP = 8, ++ MD_CONTEXT_RISCV64_REG_RA = 1, ++ MD_CONTEXT_RISCV64_REG_SP = 2, ++ MD_CONTEXT_RISCV64_REG_PC = 0 ++}; ++ ++#endif /* GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_RISCV64_H__ */ +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/breakpad/breakpad/src/google_breakpad/common/minidump_format.h +=================================================================== +--- qtwebengine-everywhere-src-5.15.7.orig/src/3rdparty/chromium/third_party/breakpad/breakpad/src/google_breakpad/common/minidump_format.h ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/breakpad/breakpad/src/google_breakpad/common/minidump_format.h +@@ -118,6 +118,7 @@ typedef struct { + #include "minidump_cpu_mips.h" + #include "minidump_cpu_ppc.h" + #include "minidump_cpu_ppc64.h" ++#include "minidump_cpu_riscv64.h" + #include "minidump_cpu_sparc.h" + #include "minidump_cpu_x86.h" + +@@ -660,6 +661,7 @@ typedef enum { + MD_CPU_ARCHITECTURE_PPC64 = 0x8002, /* Breakpad-defined value for PPC64 */ + MD_CPU_ARCHITECTURE_ARM64_OLD = 0x8003, /* Breakpad-defined value for ARM64 */ + MD_CPU_ARCHITECTURE_MIPS64 = 0x8004, /* Breakpad-defined value for MIPS64 */ ++ MD_CPU_ARCHITECTURE_RISCV64 = 0x8005, /* Breakpad-defined value for RISCV64 */ + MD_CPU_ARCHITECTURE_UNKNOWN = 0xffff /* PROCESSOR_ARCHITECTURE_UNKNOWN */ + } MDCPUArchitecture; + +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/crashpad/crashpad/util/linux/ptracer.cc +=================================================================== +--- qtwebengine-everywhere-src-5.15.7.orig/src/3rdparty/chromium/third_party/crashpad/crashpad/util/linux/ptracer.cc ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/crashpad/crashpad/util/linux/ptracer.cc +@@ -398,6 +398,51 @@ bool GetThreadArea64(pid_t tid, + return true; + } + ++#elif defined(ARCH_CPU_RISCV_FAMILY) ++ ++template ++bool GetRegisterSet(pid_t tid, int set, Destination* dest, bool can_log) { ++ iovec iov; ++ iov.iov_base = dest; ++ iov.iov_len = sizeof(*dest); ++ if (ptrace(PTRACE_GETREGSET, tid, reinterpret_cast(set), &iov) != 0) { ++ PLOG_IF(ERROR, can_log) << "ptrace"; ++ return false; ++ } ++ if (iov.iov_len != sizeof(*dest)) { ++ LOG_IF(ERROR, can_log) << "Unexpected registers size"; ++ return false; ++ } ++ return true; ++} ++ ++bool GetFloatingPointRegisters32(pid_t tid, ++ FloatContext* context, ++ bool can_log) { ++ return false; ++} ++ ++bool GetFloatingPointRegisters64(pid_t tid, ++ FloatContext* context, ++ bool can_log) { ++ return GetRegisterSet(tid, NT_PRFPREG, &context->f64.f, can_log); ++} ++ ++bool GetThreadArea32(pid_t tid, ++ const ThreadContext& context, ++ LinuxVMAddress* address, ++ bool can_log) { ++ return false; ++} ++ ++bool GetThreadArea64(pid_t tid, ++ const ThreadContext& context, ++ LinuxVMAddress* address, ++ bool can_log) { ++ *address = context.t64.tp; ++ return true; ++} ++ + #else + #error Port. + #endif // ARCH_CPU_X86_FAMILY +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/crashpad/crashpad/util/linux/thread_info.h +=================================================================== +--- qtwebengine-everywhere-src-5.15.7.orig/src/3rdparty/chromium/third_party/crashpad/crashpad/util/linux/thread_info.h ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/crashpad/crashpad/util/linux/thread_info.h +@@ -79,6 +79,40 @@ union ThreadContext { + uint32_t cp0_status; + uint32_t cp0_cause; + uint32_t padding1_; ++#elif defined(ARCH_CPU_RISCV_FAMILY) ++ // Reflects user_regs_struct in asm/ptrace.h. ++ uint32_t pc; ++ uint32_t ra; ++ uint32_t sp; ++ uint32_t gp; ++ uint32_t tp; ++ uint32_t t0; ++ uint32_t t1; ++ uint32_t t2; ++ uint32_t s0; ++ uint32_t s1; ++ uint32_t a0; ++ uint32_t a1; ++ uint32_t a2; ++ uint32_t a3; ++ uint32_t a4; ++ uint32_t a5; ++ uint32_t a6; ++ uint32_t a7; ++ uint32_t s2; ++ uint32_t s3; ++ uint32_t s4; ++ uint32_t s5; ++ uint32_t s6; ++ uint32_t s7; ++ uint32_t s8; ++ uint32_t s9; ++ uint32_t s10; ++ uint32_t s11; ++ uint32_t t3; ++ uint32_t t4; ++ uint32_t t5; ++ uint32_t t6; + #else + #error Port. + #endif // ARCH_CPU_X86_FAMILY +@@ -132,6 +166,40 @@ union ThreadContext { + uint64_t cp0_badvaddr; + uint64_t cp0_status; + uint64_t cp0_cause; ++#elif defined(ARCH_CPU_RISCV_FAMILY) ++ // Reflects user_regs_struct in asm/ptrace.h. ++ uint64_t pc; ++ uint64_t ra; ++ uint64_t sp; ++ uint64_t gp; ++ uint64_t tp; ++ uint64_t t0; ++ uint64_t t1; ++ uint64_t t2; ++ uint64_t s0; ++ uint64_t s1; ++ uint64_t a0; ++ uint64_t a1; ++ uint64_t a2; ++ uint64_t a3; ++ uint64_t a4; ++ uint64_t a5; ++ uint64_t a6; ++ uint64_t a7; ++ uint64_t s2; ++ uint64_t s3; ++ uint64_t s4; ++ uint64_t s5; ++ uint64_t s6; ++ uint64_t s7; ++ uint64_t s8; ++ uint64_t s9; ++ uint64_t s10; ++ uint64_t s11; ++ uint64_t t3; ++ uint64_t t4; ++ uint64_t t5; ++ uint64_t t6; + #else + #error Port. + #endif // ARCH_CPU_X86_FAMILY +@@ -143,11 +211,12 @@ union ThreadContext { + using NativeThreadContext = user_regs; + #elif defined(ARCH_CPU_MIPS_FAMILY) + // No appropriate NativeThreadsContext type available for MIPS ++#elif defined(ARCH_CPU_RISCV_FAMILY) + #else + #error Port. + #endif // ARCH_CPU_X86_FAMILY || ARCH_CPU_ARM64 + +-#if !defined(ARCH_CPU_MIPS_FAMILY) ++#if !defined(ARCH_CPU_MIPS_FAMILY) && !defined(ARCH_CPU_RISCV_FAMILY) + #if defined(ARCH_CPU_32_BITS) + static_assert(sizeof(t32_t) == sizeof(NativeThreadContext), "Size mismatch"); + #else // ARCH_CPU_64_BITS +@@ -218,6 +287,9 @@ union FloatContext { + } fpregs[32]; + uint32_t fpcsr; + uint32_t fpu_id; ++#elif defined(ARCH_CPU_RISCV_FAMILY) ++ uint64_t f[32]; ++ uint32_t fcsr; + #else + #error Port. + #endif // ARCH_CPU_X86_FAMILY +@@ -252,6 +324,9 @@ union FloatContext { + double fpregs[32]; + uint32_t fpcsr; + uint32_t fpu_id; ++#elif defined(ARCH_CPU_RISCV_FAMILY) ++ uint64_t f[32]; ++ uint32_t fcsr; + #else + #error Port. + #endif // ARCH_CPU_X86_FAMILY +@@ -280,6 +355,7 @@ union FloatContext { + static_assert(sizeof(f64) == sizeof(user_fpsimd_struct), "Size mismatch"); + #elif defined(ARCH_CPU_MIPS_FAMILY) + // No appropriate floating point context native type for available MIPS. ++#elif defined(ARCH_CPU_RISCV_FAMILY) + #else + #error Port. + #endif // ARCH_CPU_X86 +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/dav1d/config/linux/riscv64/config.h +=================================================================== +--- /dev/null ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/dav1d/config/linux/riscv64/config.h +@@ -0,0 +1,38 @@ ++/* ++ * Autogenerated by the Meson build system. ++ * Do not edit, your changes will be lost. ++ */ ++ ++#pragma once ++ ++#define ARCH_AARCH64 0 ++ ++#define ARCH_ARM 0 ++ ++#define ARCH_PPC64LE 0 ++ ++#define ARCH_X86 0 ++ ++#define ARCH_X86_32 0 ++ ++#define ARCH_X86_64 0 ++ ++#define CONFIG_16BPC 1 ++ ++#define CONFIG_8BPC 1 ++ ++// #define CONFIG_LOG 1 -- Logging is controlled by Chromium ++ ++#define ENDIANNESS_BIG 0 ++ ++#define HAVE_ASM 0 ++ ++#define HAVE_AS_FUNC 0 ++ ++#define HAVE_CLOCK_GETTIME 1 ++ ++#define HAVE_GETAUXVAL 1 ++ ++#define HAVE_POSIX_MEMALIGN 1 ++ ++#define HAVE_UNISTD_H 1 +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/lss/linux_syscall_support.h +=================================================================== +--- qtwebengine-everywhere-src-5.15.7.orig/src/3rdparty/chromium/third_party/lss/linux_syscall_support.h ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/lss/linux_syscall_support.h +@@ -88,7 +88,7 @@ + */ + #if (defined(__i386__) || defined(__x86_64__) || defined(__ARM_ARCH_3__) || \ + defined(__mips__) || defined(__PPC__) || defined(__ARM_EABI__) || \ +- defined(__aarch64__) || defined(__s390__)) \ ++ defined(__aarch64__) || defined(__s390__) || defined(__riscv)) \ + && (defined(__linux) || defined(__ANDROID__)) + + #ifndef SYS_CPLUSPLUS +@@ -301,7 +301,7 @@ struct kernel_old_sigaction { + } __attribute__((packed,aligned(4))); + #elif (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) + #define kernel_old_sigaction kernel_sigaction +-#elif defined(__aarch64__) ++#elif defined(__aarch64__) || defined(__riscv) + // No kernel_old_sigaction defined for arm64. + #endif + +@@ -519,7 +519,7 @@ struct kernel_stat { + int st_blocks; + int st_pad4[14]; + }; +-#elif defined(__aarch64__) ++#elif defined(__aarch64__) || defined(__riscv) + struct kernel_stat { + unsigned long st_dev; + unsigned long st_ino; +@@ -1065,7 +1065,7 @@ struct kernel_statfs { + #define __NR_getrandom (__NR_SYSCALL_BASE + 384) + #endif + /* End of ARM 3/EABI definitions */ +-#elif defined(__aarch64__) ++#elif defined(__aarch64__) || defined(__riscv) + #ifndef __NR_setxattr + #define __NR_setxattr 5 + #endif +@@ -1880,7 +1880,7 @@ struct kernel_statfs { + + #undef LSS_RETURN + #if (defined(__i386__) || defined(__x86_64__) || defined(__ARM_ARCH_3__) \ +- || defined(__ARM_EABI__) || defined(__aarch64__) || defined(__s390__)) ++ || defined(__ARM_EABI__) || defined(__aarch64__) || defined(__s390__) || defined(__riscv)) + /* Failing system calls return a negative result in the range of + * -1..-4095. These are "errno" values with the sign inverted. + */ +@@ -3373,6 +3373,122 @@ struct kernel_statfs { + } + LSS_RETURN(int, __ret); + } ++ #elif defined(__riscv) ++ #undef LSS_REG ++ #define LSS_REG(r,a) register int64_t __r##r __asm__("a"#r) = (int64_t)a ++ #undef LSS_BODY ++ #define LSS_BODY(type,name,args...) \ ++ register int64_t __res_a0 __asm__("a0"); \ ++ register int64_t __a7 __asm__("a7") = __NR_##name; \ ++ int64_t __res; \ ++ __asm__ __volatile__ ("scall\n" \ ++ : "=r"(__res_a0) \ ++ : "r"(__a7) , ## args \ ++ : "memory"); \ ++ __res = __res_a0; \ ++ LSS_RETURN(type, __res) ++ #undef _syscall0 ++ #define _syscall0(type, name) \ ++ type LSS_NAME(name)(void) { \ ++ LSS_BODY(type, name); \ ++ } ++ #undef _syscall1 ++ #define _syscall1(type, name, type1, arg1) \ ++ type LSS_NAME(name)(type1 arg1) { \ ++ LSS_REG(0, arg1); LSS_BODY(type, name, "r"(__r0)); \ ++ } ++ #undef _syscall2 ++ #define _syscall2(type, name, type1, arg1, type2, arg2) \ ++ type LSS_NAME(name)(type1 arg1, type2 arg2) { \ ++ LSS_REG(0, arg1); LSS_REG(1, arg2); \ ++ LSS_BODY(type, name, "r"(__r0), "r"(__r1)); \ ++ } ++ #undef _syscall3 ++ #define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \ ++ type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) { \ ++ LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ ++ LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2)); \ ++ } ++ #undef _syscall4 ++ #define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \ ++ type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ ++ LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ ++ LSS_REG(3, arg4); \ ++ LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3)); \ ++ } ++ #undef _syscall5 ++ #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ ++ type5,arg5) \ ++ type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ ++ type5 arg5) { \ ++ LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ ++ LSS_REG(3, arg4); LSS_REG(4, arg5); \ ++ LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3), \ ++ "r"(__r4)); \ ++ } ++ #undef _syscall6 ++ #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ ++ type5,arg5,type6,arg6) \ ++ type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ ++ type5 arg5, type6 arg6) { \ ++ LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ ++ LSS_REG(3, arg4); LSS_REG(4, arg5); LSS_REG(5, arg6); \ ++ LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3), \ ++ "r"(__r4), "r"(__r5)); \ ++ } ++ ++ LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack, ++ int flags, void *arg, int *parent_tidptr, ++ void *newtls, int *child_tidptr) { ++ int64_t __res; ++ { ++ register int64_t __res_a0 __asm__("a0"); ++ register uint64_t __flags __asm__("a0") = flags; ++ register void *__stack __asm__("a1") = child_stack; ++ register void *__ptid __asm__("a2") = parent_tidptr; ++ register void *__tls __asm__("a3") = newtls; ++ register int *__ctid __asm__("a4") = child_tidptr; ++ __asm__ __volatile__(/* Push "arg" and "fn" onto the stack that will be ++ * used by the child. ++ */ ++ "addi %2,%2,-16\n" ++ "sd %1, 0(%2)\n" ++ "sd %4, 8(%2)\n" ++ ++ /* %a0 = syscall(%a0 = flags, ++ * %a1 = child_stack, ++ * %a2 = parent_tidptr, ++ * %a3 = newtls, ++ * %a4 = child_tidptr) ++ */ ++ "li a7, %8\n" ++ "scall\n" ++ ++ /* if (%a0 != 0) ++ * return %a0; ++ */ ++ "bnez %0, 1f\n" ++ ++ /* In the child, now. Call "fn(arg)". ++ */ ++ "ld a1, 0(sp)\n" ++ "ld a0, 8(sp)\n" ++ "jalr a1\n" ++ ++ /* Call _exit(%a0). ++ */ ++ "li a7, %9\n" ++ "scall\n" ++ "1:\n" ++ : "=r" (__res_a0) ++ : "r"(fn), "r"(__stack), "r"(__flags), "r"(arg), ++ "r"(__ptid), "r"(__tls), "r"(__ctid), ++ "i"(__NR_clone), "i"(__NR_exit) ++ : "cc", "memory"); ++ __res = __res_a0; ++ } ++ LSS_RETURN(int, __res); ++ } + #endif + #define __NR__exit __NR_exit + #define __NR__gettid __NR_gettid +@@ -4181,7 +4297,7 @@ struct kernel_statfs { + LSS_SC_BODY(4, int, 8, d, type, protocol, sv); + } + #endif +- #if defined(__ARM_EABI__) || defined (__aarch64__) ++ #if defined(__ARM_EABI__) || defined (__aarch64__) || defined(__riscv) + LSS_INLINE _syscall3(ssize_t, recvmsg, int, s, struct kernel_msghdr*, msg, + int, flags) + LSS_INLINE _syscall3(ssize_t, sendmsg, int, s, const struct kernel_msghdr*, +@@ -4503,7 +4619,7 @@ struct kernel_statfs { + // TODO: define this in an arch-independant way instead of inlining the clone + // syscall body. + +-# if defined(__aarch64__) ++# if defined(__aarch64__) || defined(__riscv) + LSS_INLINE pid_t LSS_NAME(fork)(void) { + // No fork syscall on aarch64 - implement by means of the clone syscall. + // Note that this does not reset glibc's cached view of the PID/TID, so +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/swiftshader/third_party/llvm-10.0/BUILD.gn +=================================================================== +--- qtwebengine-everywhere-src-5.15.7.orig/src/3rdparty/chromium/third_party/swiftshader/third_party/llvm-10.0/BUILD.gn ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/swiftshader/third_party/llvm-10.0/BUILD.gn +@@ -149,6 +149,7 @@ swiftshader_llvm_source_set("swiftshader + deps += [ ":swiftshader_llvm_ppc" ] + } else if (current_cpu == "x86" || current_cpu == "x64") { + deps += [ ":swiftshader_llvm_x86" ] ++ } else if (current_cpu == "riscv64") { + } else { + assert(false, "Unsupported current_cpu") + } +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/webrtc/modules/desktop_capture/differ_block.cc +=================================================================== +--- qtwebengine-everywhere-src-5.15.7.orig/src/3rdparty/chromium/third_party/webrtc/modules/desktop_capture/differ_block.cc ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/webrtc/modules/desktop_capture/differ_block.cc +@@ -30,7 +30,7 @@ bool VectorDifference(const uint8_t* ima + static bool (*diff_proc)(const uint8_t*, const uint8_t*) = nullptr; + + if (!diff_proc) { +-#if defined(WEBRTC_ARCH_ARM_FAMILY) || defined(WEBRTC_ARCH_MIPS_FAMILY) ++#if defined(WEBRTC_ARCH_ARM_FAMILY) || defined(WEBRTC_ARCH_MIPS_FAMILY) || defined(WEBRTC_ARCH_RISCV_FAMILY) + // For ARM and MIPS processors, always use C version. + // TODO(hclam): Implement a NEON version. + diff_proc = &VectorDifference_C; +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/webrtc/rtc_base/system/arch.h +=================================================================== +--- qtwebengine-everywhere-src-5.15.7.orig/src/3rdparty/chromium/third_party/webrtc/rtc_base/system/arch.h ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/chromium/third_party/webrtc/rtc_base/system/arch.h +@@ -50,6 +50,10 @@ + #elif defined(__EMSCRIPTEN__) + #define WEBRTC_ARCH_32_BITS + #define WEBRTC_ARCH_LITTLE_ENDIAN ++#elif defined(__riscv) && __riscv_xlen == 64 ++#define WEBRTC_ARCH_RISCV_FAMILY ++#define WEBRTC_ARCH_64_BITS ++#define WEBRTC_ARCH_LITTLE_ENDIAN + #else + #error Please add support for your architecture in rtc_base/system/arch.h + #endif +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/gn/tools/gn/args.cc +=================================================================== +--- qtwebengine-everywhere-src-5.15.7.orig/src/3rdparty/gn/tools/gn/args.cc ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/gn/tools/gn/args.cc +@@ -329,6 +329,7 @@ void Args::SetSystemVarsLocked(Scope* de + static const char kMips64[] = "mips64el"; + static const char kS390X[] = "s390x"; + static const char kPPC64[] = "ppc64"; ++ static const char kRiscv64[] = "riscv64"; + const char* arch = nullptr; + + // Set the host CPU architecture based on the underlying OS, not +@@ -353,6 +354,8 @@ void Args::SetSystemVarsLocked(Scope* de + // This allows us to use the same toolchain as ppc64 BE + // and specific flags are included using the host_byteorder logic. + arch = kPPC64; ++ else if (os_arch == "riscv64") ++ arch = kRiscv64; + else + CHECK(false) << "OS architecture not handled. (" << os_arch << ")"; + +Index: qtwebengine-everywhere-src-5.15.7/src/3rdparty/gn/util/build_config.h +=================================================================== +--- qtwebengine-everywhere-src-5.15.7.orig/src/3rdparty/gn/util/build_config.h ++++ qtwebengine-everywhere-src-5.15.7/src/3rdparty/gn/util/build_config.h +@@ -172,6 +172,16 @@ + #define ARCH_CPU_32_BITS 1 + #define ARCH_CPU_BIG_ENDIAN 1 + #endif ++#elif defined(__riscv) ++#define ARCH_CPU_RISCV_FAMILY 1 ++#if __riscv_xlen == 64 ++#define ARCH_CPU_RISCV64 1 ++#define ARCH_CPU_64_BITS 1 ++#else ++#define ARCH_CPU_RISCV32 1 ++#define ARCH_CPU_32_BITS 1 ++#endif ++#define ARCH_CPU_LITTLE_ENDIAN 1 + #else + #error Please add support for your architecture in build_config.h + #endif diff --git a/v8.patch b/v8.patch new file mode 100644 index 0000000..bc7870a --- /dev/null +++ b/v8.patch @@ -0,0 +1,33254 @@ +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;