Adding vector and floating point instruction support for cheriot (experimental).
PiperOrigin-RevId: 660400409
Change-Id: I0bf291905b7242e45cfcc3d0522b2c51b5dd00e9
diff --git a/cheriot/BUILD b/cheriot/BUILD
index 4bb61f5..fceadee 100644
--- a/cheriot/BUILD
+++ b/cheriot/BUILD
@@ -24,15 +24,69 @@
exports_files([
"riscv_cheriot.bin_fmt",
"riscv_cheriot.isa",
+ "riscv_cheriot_f.bin_fmt",
+ "riscv_cheriot_f.isa",
+ "riscv_cheriot_rvv.isa",
+ "riscv_cheriot_vector.bin_fmt",
+ "riscv_cheriot_vector_fp.bin_fmt",
+ "riscv_cheriot_vector.isa",
])
+config_setting(
+ name = "arm_cpu",
+ values = {"cpu": "arm"},
+)
+
+config_setting(
+ name = "darwin_arm64_cpu",
+ values = {"cpu": "darwin_arm64"},
+)
+
mpact_isa_decoder(
name = "riscv_cheriot_isa",
src = "riscv_cheriot.isa",
includes = [],
isa_name = "RiscVCheriot",
deps = [
- ":riscv_cheriot",
+ ":riscv_cheriot_instructions",
+ "@com_google_absl//absl/functional:bind_front",
+ ],
+)
+
+mpact_isa_decoder(
+ name = "riscv_cheriot_rvv_isa",
+ src = "riscv_cheriot_rvv.isa",
+ includes = [
+ "riscv_cheriot.isa",
+ "riscv_cheriot_f.isa",
+ "riscv_cheriot_vector.isa",
+ "riscv_cheriot_vector_fp.isa",
+ ],
+ isa_name = "RiscVCheriotRVV",
+ prefix = "riscv_cheriot_rvv",
+ deps = [
+ ":riscv_cheriot_instructions",
+ ":riscv_cheriot_vector",
+ "@com_google_absl//absl/functional:bind_front",
+ ],
+)
+
+mpact_isa_decoder(
+ name = "riscv_cheriot_rvv_fp_isa",
+ src = "riscv_cheriot_rvv.isa",
+ includes = [
+ "riscv_cheriot.isa",
+ "riscv_cheriot_f.isa",
+ "riscv_cheriot_vector.isa",
+ "riscv_cheriot_vector_fp.isa",
+ ],
+ isa_name = "RiscVCheriotRVVFp",
+ prefix = "riscv_cheriot_rvv_fp",
+ deps = [
+ ":riscv_cheriot_f",
+ ":riscv_cheriot_instructions",
+ ":riscv_cheriot_vector",
+ ":riscv_cheriot_vector_fp",
"@com_google_absl//absl/functional:bind_front",
],
)
@@ -48,34 +102,91 @@
],
)
+mpact_bin_fmt_decoder(
+ name = "riscv_cheriot_rvv_bin_fmt",
+ src = "riscv_cheriot_rvv.bin_fmt",
+ decoder_name = "RiscVCheriotRVV",
+ includes = [
+ "riscv_cheriot.bin_fmt",
+ "riscv_cheriot_f.bin_fmt",
+ "riscv_cheriot_vector.bin_fmt",
+ "riscv_cheriot_vector_fp.bin_fmt",
+ ],
+ deps = [
+ ":riscv_cheriot_rvv_isa",
+ ],
+)
+
+mpact_bin_fmt_decoder(
+ name = "riscv_cheriot_rvv_fp_bin_fmt",
+ src = "riscv_cheriot_rvv.bin_fmt",
+ decoder_name = "RiscVCheriotRVVFp",
+ includes = [
+ "riscv_cheriot.bin_fmt",
+ "riscv_cheriot_f.bin_fmt",
+ "riscv_cheriot_vector.bin_fmt",
+ "riscv_cheriot_vector_fp.bin_fmt",
+ ],
+ prefix = "riscv_cheriot_rvv_fp",
+ deps = [
+ ":riscv_cheriot_rvv_fp_isa",
+ ],
+)
+
cc_library(
- name = "riscv_cheriot",
+ name = "riscv_cheriot_instructions",
srcs = [
- "cheriot_register.cc",
- "cheriot_state.cc",
"riscv_cheriot_a_instructions.cc",
"riscv_cheriot_i_instructions.cc",
"riscv_cheriot_instructions.cc",
"riscv_cheriot_m_instructions.cc",
- "riscv_cheriot_minstret.cc",
"riscv_cheriot_priv_instructions.cc",
"riscv_cheriot_zicsr_instructions.cc",
],
hdrs = [
- "cheriot_register.h",
- "cheriot_state.h",
"riscv_cheriot_a_instructions.h",
- "riscv_cheriot_csr_enum.h",
"riscv_cheriot_i_instructions.h",
- "riscv_cheriot_instruction_helpers.h",
"riscv_cheriot_instructions.h",
"riscv_cheriot_m_instructions.h",
- "riscv_cheriot_minstret.h",
"riscv_cheriot_priv_instructions.h",
"riscv_cheriot_zicsr_instructions.h",
],
tags = ["not_run:arm"],
deps = [
+ ":cheriot_state",
+ ":instruction_helpers",
+ "@com_google_absl//absl/log",
+ "@com_google_absl//absl/numeric:bits",
+ "@com_google_absl//absl/status",
+ "@com_google_absl//absl/strings",
+ "@com_google_mpact-riscv//riscv:riscv_state",
+ "@com_google_mpact-riscv//riscv:stoull_wrapper",
+ "@com_google_mpact-sim//mpact/sim/generic:arch_state",
+ "@com_google_mpact-sim//mpact/sim/generic:core",
+ "@com_google_mpact-sim//mpact/sim/generic:instruction",
+ "@com_google_mpact-sim//mpact/sim/generic:type_helpers",
+ "@com_google_mpact-sim//mpact/sim/util/memory",
+ ],
+)
+
+cc_library(
+ name = "cheriot_state",
+ srcs = [
+ "cheriot_register.cc",
+ "cheriot_state.cc",
+ "cheriot_vector_true_operand.cc",
+ "riscv_cheriot_minstret.cc",
+ ],
+ hdrs = [
+ "cheriot_register.h",
+ "cheriot_state.h",
+ "cheriot_vector_true_operand.h",
+ "riscv_cheriot_csr_enum.h",
+ "riscv_cheriot_minstret.h",
+ "riscv_cheriot_register_aliases.h",
+ ],
+ tags = ["not_run:arm"],
+ deps = [
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/flags:flag",
"@com_google_absl//absl/functional:any_invocable",
@@ -86,8 +197,8 @@
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:str_format",
+ "@com_google_mpact-riscv//riscv:riscv_fp_state",
"@com_google_mpact-riscv//riscv:riscv_state",
- "@com_google_mpact-riscv//riscv:stoull_wrapper",
"@com_google_mpact-sim//mpact/sim/generic:arch_state",
"@com_google_mpact-sim//mpact/sim/generic:core",
"@com_google_mpact-sim//mpact/sim/generic:counters",
@@ -98,6 +209,183 @@
)
cc_library(
+ name = "riscv_cheriot_f",
+ srcs = [
+ "riscv_cheriot_f_instructions.cc",
+ ],
+ hdrs = [
+ "riscv_cheriot_f_instructions.h",
+ ],
+ tags = ["not_run:arm"],
+ deps = [
+ ":cheriot_state",
+ ":instruction_helpers",
+ "@com_google_absl//absl/functional:bind_front",
+ "@com_google_mpact-riscv//riscv:riscv_fp_state",
+ "@com_google_mpact-riscv//riscv:riscv_state",
+ "@com_google_mpact-sim//mpact/sim/generic:arch_state",
+ "@com_google_mpact-sim//mpact/sim/generic:instruction",
+ "@com_google_mpact-sim//mpact/sim/generic:type_helpers",
+ ],
+)
+
+cc_library(
+ name = "cheriot_vector_state",
+ srcs = [
+ "cheriot_vector_state.cc",
+ ],
+ hdrs = [
+ "cheriot_vector_state.h",
+ ],
+ tags = ["not_run:arm"],
+ deps = [
+ ":cheriot_state",
+ "@com_google_absl//absl/log",
+ "@com_google_mpact-riscv//riscv:riscv_state",
+ ],
+)
+
+cc_library(
+ name = "riscv_cheriot_vector",
+ srcs = [
+ "riscv_cheriot_vector_memory_instructions.cc",
+ "riscv_cheriot_vector_opi_instructions.cc",
+ "riscv_cheriot_vector_opm_instructions.cc",
+ "riscv_cheriot_vector_permute_instructions.cc",
+ "riscv_cheriot_vector_reduction_instructions.cc",
+ "riscv_cheriot_vector_unary_instructions.cc",
+ ],
+ hdrs = [
+ "riscv_cheriot_vector_memory_instructions.h",
+ "riscv_cheriot_vector_opi_instructions.h",
+ "riscv_cheriot_vector_opm_instructions.h",
+ "riscv_cheriot_vector_permute_instructions.h",
+ "riscv_cheriot_vector_reduction_instructions.h",
+ "riscv_cheriot_vector_unary_instructions.h",
+ ],
+ copts = [
+ "-O3",
+ "-ffp-model=strict",
+ ] + select({
+ "darwin_arm64_cpu": [],
+ "//conditions:default": ["-fprotect-parens"],
+ }),
+ deps = [
+ ":cheriot_state",
+ ":cheriot_vector_state",
+ ":instruction_helpers",
+ "@com_google_absl//absl/log",
+ "@com_google_absl//absl/status",
+ "@com_google_absl//absl/strings",
+ "@com_google_absl//absl/strings:str_format",
+ "@com_google_absl//absl/types:span",
+ "@com_google_mpact-riscv//riscv:riscv_state",
+ "@com_google_mpact-sim//mpact/sim/generic:arch_state",
+ "@com_google_mpact-sim//mpact/sim/generic:core",
+ "@com_google_mpact-sim//mpact/sim/generic:instruction",
+ "@com_google_mpact-sim//mpact/sim/generic:type_helpers",
+ ],
+)
+
+cc_library(
+ name = "riscv_cheriot_vector_fp",
+ srcs = [
+ "riscv_cheriot_vector_fp_compare_instructions.cc",
+ "riscv_cheriot_vector_fp_instructions.cc",
+ "riscv_cheriot_vector_fp_reduction_instructions.cc",
+ "riscv_cheriot_vector_fp_unary_instructions.cc",
+ ],
+ hdrs = [
+ "riscv_cheriot_vector_fp_compare_instructions.h",
+ "riscv_cheriot_vector_fp_instructions.h",
+ "riscv_cheriot_vector_fp_reduction_instructions.h",
+ "riscv_cheriot_vector_fp_unary_instructions.h",
+ ],
+ copts = [
+ "-O3",
+ "-ffp-model=strict",
+ ] + select({
+ "darwin_arm64_cpu": [],
+ "//conditions:default": ["-fprotect-parens"],
+ }),
+ deps = [
+ ":cheriot_state",
+ ":cheriot_vector_state",
+ ":instruction_helpers",
+ "@com_google_absl//absl/log",
+ "@com_google_absl//absl/status",
+ "@com_google_absl//absl/strings",
+ "@com_google_absl//absl/strings:str_format",
+ "@com_google_mpact-riscv//riscv:riscv_fp_state",
+ "@com_google_mpact-riscv//riscv:riscv_state",
+ "@com_google_mpact-sim//mpact/sim/generic:arch_state",
+ "@com_google_mpact-sim//mpact/sim/generic:core",
+ "@com_google_mpact-sim//mpact/sim/generic:instruction",
+ "@com_google_mpact-sim//mpact/sim/generic:type_helpers",
+ ],
+)
+
+cc_library(
+ name = "instruction_helpers",
+ hdrs = [
+ "riscv_cheriot_instruction_helpers.h",
+ "riscv_cheriot_vector_instruction_helpers.h",
+ ],
+ deps = [
+ ":cheriot_state",
+ ":cheriot_vector_state",
+ "@com_google_absl//absl/log",
+ "@com_google_absl//absl/strings",
+ "@com_google_mpact-riscv//riscv:riscv_fp_state",
+ "@com_google_mpact-riscv//riscv:riscv_state",
+ "@com_google_mpact-sim//mpact/sim/generic:arch_state",
+ "@com_google_mpact-sim//mpact/sim/generic:core",
+ "@com_google_mpact-sim//mpact/sim/generic:instruction",
+ "@com_google_mpact-sim//mpact/sim/generic:type_helpers",
+ ],
+)
+
+cc_library(
+ name = "cheriot_getter_helpers",
+ hdrs = [
+ "cheriot_getter_helpers.h",
+ ],
+ deps = [
+ ":cheriot_state",
+ "@com_google_absl//absl/functional:any_invocable",
+ "@com_google_absl//absl/log",
+ "@com_google_absl//absl/strings",
+ "@com_google_absl//absl/types:span",
+ "@com_google_mpact-riscv//riscv:riscv_state",
+ "@com_google_mpact-sim//mpact/sim/generic:arch_state",
+ "@com_google_mpact-sim//mpact/sim/generic:core",
+ "@com_google_mpact-sim//mpact/sim/generic:instruction",
+ ],
+)
+
+cc_library(
+ name = "cheriot_getters",
+ hdrs = [
+ "cheriot_f_getters.h",
+ "cheriot_getters.h",
+ "cheriot_rvv_fp_getters.h",
+ "cheriot_rvv_getters.h",
+ "riscv_cheriot_encoding_common.h",
+ ],
+ deps = [
+ ":cheriot_getter_helpers",
+ ":cheriot_state",
+ ":riscv_cheriot_bin_fmt",
+ ":riscv_cheriot_isa",
+ "@com_google_absl//absl/container:flat_hash_map",
+ "@com_google_absl//absl/functional:any_invocable",
+ "@com_google_absl//absl/strings",
+ "@com_google_mpact-riscv//riscv:riscv_state",
+ "@com_google_mpact-sim//mpact/sim/generic:core",
+ ],
+)
+
+cc_library(
name = "riscv_cheriot_decoder",
srcs = [
"cheriot_decoder.cc",
@@ -106,10 +394,10 @@
hdrs = [
"cheriot_decoder.h",
"riscv_cheriot_encoding.h",
- "riscv_cheriot_register_aliases.h",
],
deps = [
- ":riscv_cheriot",
+ ":cheriot_getters",
+ ":cheriot_state",
":riscv_cheriot_bin_fmt",
":riscv_cheriot_isa",
"@com_google_absl//absl/container:flat_hash_map",
@@ -127,6 +415,66 @@
)
cc_library(
+ name = "riscv_cheriot_rvv_decoder",
+ srcs = [
+ "cheriot_rvv_decoder.cc",
+ "riscv_cheriot_rvv_encoding.cc",
+ ],
+ hdrs = [
+ "cheriot_rvv_decoder.h",
+ "riscv_cheriot_register_aliases.h",
+ "riscv_cheriot_rvv_encoding.h",
+ ],
+ deps = [
+ ":cheriot_getters",
+ ":cheriot_state",
+ ":riscv_cheriot_rvv_bin_fmt",
+ ":riscv_cheriot_rvv_isa",
+ "@com_google_absl//absl/container:flat_hash_map",
+ "@com_google_absl//absl/functional:any_invocable",
+ "@com_google_absl//absl/log",
+ "@com_google_absl//absl/strings",
+ "@com_google_mpact-riscv//riscv:riscv_state",
+ "@com_google_mpact-sim//mpact/sim/generic:arch_state",
+ "@com_google_mpact-sim//mpact/sim/generic:core",
+ "@com_google_mpact-sim//mpact/sim/generic:instruction",
+ "@com_google_mpact-sim//mpact/sim/generic:program_error",
+ "@com_google_mpact-sim//mpact/sim/generic:type_helpers",
+ "@com_google_mpact-sim//mpact/sim/util/memory",
+ ],
+)
+
+cc_library(
+ name = "riscv_cheriot_rvv_fp_decoder",
+ srcs = [
+ "cheriot_rvv_fp_decoder.cc",
+ "riscv_cheriot_rvv_fp_encoding.cc",
+ ],
+ hdrs = [
+ "cheriot_rvv_fp_decoder.h",
+ "riscv_cheriot_register_aliases.h",
+ "riscv_cheriot_rvv_fp_encoding.h",
+ ],
+ deps = [
+ ":cheriot_getters",
+ ":cheriot_state",
+ ":riscv_cheriot_rvv_fp_bin_fmt",
+ ":riscv_cheriot_rvv_fp_isa",
+ "@com_google_absl//absl/container:flat_hash_map",
+ "@com_google_absl//absl/functional:any_invocable",
+ "@com_google_absl//absl/log",
+ "@com_google_absl//absl/strings",
+ "@com_google_mpact-riscv//riscv:riscv_state",
+ "@com_google_mpact-sim//mpact/sim/generic:arch_state",
+ "@com_google_mpact-sim//mpact/sim/generic:core",
+ "@com_google_mpact-sim//mpact/sim/generic:instruction",
+ "@com_google_mpact-sim//mpact/sim/generic:program_error",
+ "@com_google_mpact-sim//mpact/sim/generic:type_helpers",
+ "@com_google_mpact-sim//mpact/sim/util/memory",
+ ],
+)
+
+cc_library(
name = "cheriot_top",
srcs = [
"cheriot_top.cc",
@@ -137,8 +485,7 @@
copts = ["-O3"],
deps = [
":cheriot_debug_interface",
- ":riscv_cheriot",
- ":riscv_cheriot_decoder",
+ ":cheriot_state",
":riscv_cheriot_isa",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/functional:any_invocable",
@@ -189,8 +536,8 @@
copts = ["-O3"],
deps = [
":cheriot_debug_interface",
+ ":cheriot_state",
":cheriot_top",
- ":riscv_cheriot",
":riscv_cheriot_decoder",
":riscv_cheriot_isa",
"@com_google_absl//absl/container:btree",
@@ -217,11 +564,14 @@
],
copts = ["-O3"],
deps = [
+ ":cheriot_state",
":cheriot_top",
":debug_command_shell",
":instrumentation",
- ":riscv_cheriot",
":riscv_cheriot_decoder",
+ ":riscv_cheriot_instructions",
+ ":riscv_cheriot_rvv_decoder",
+ ":riscv_cheriot_rvv_fp_decoder",
"@com_google_absl//absl/flags:flag",
"@com_google_absl//absl/flags:parse",
"@com_google_absl//absl/flags:usage",
@@ -236,6 +586,7 @@
"@com_google_mpact-riscv//riscv:riscv_arm_semihost",
"@com_google_mpact-riscv//riscv:riscv_clint",
"@com_google_mpact-riscv//riscv:stoull_wrapper",
+ "@com_google_mpact-sim//mpact/sim/generic:core",
"@com_google_mpact-sim//mpact/sim/generic:core_debug_interface",
"@com_google_mpact-sim//mpact/sim/generic:counters",
"@com_google_mpact-sim//mpact/sim/generic:instruction",
@@ -258,7 +609,7 @@
"cheriot_load_filter.h",
],
deps = [
- ":riscv_cheriot",
+ ":cheriot_state",
"@com_google_mpact-sim//mpact/sim/generic:core",
"@com_google_mpact-sim//mpact/sim/generic:counters",
"@com_google_mpact-sim//mpact/sim/util/memory",
@@ -335,10 +686,10 @@
deps = [
":cheriot_debug_info",
":cheriot_debug_interface",
+ ":cheriot_state",
":cheriot_top",
":debug_command_shell",
":instrumentation",
- ":riscv_cheriot",
":riscv_cheriot_decoder",
"@com_google_absl//absl/functional:any_invocable",
"@com_google_absl//absl/functional:bind_front",
@@ -377,7 +728,7 @@
"test_rig_packets.h",
],
deps = [
- ":riscv_cheriot",
+ ":cheriot_state",
":riscv_cheriot_bin_fmt",
":riscv_cheriot_decoder",
":riscv_cheriot_isa",
diff --git a/cheriot/cheriot_decoder.cc b/cheriot/cheriot_decoder.cc
index 63e6103..805d5fd 100644
--- a/cheriot/cheriot_decoder.cc
+++ b/cheriot/cheriot_decoder.cc
@@ -22,7 +22,6 @@
#include "cheriot/riscv_cheriot_encoding.h"
#include "cheriot/riscv_cheriot_enums.h"
#include "mpact/sim/generic/instruction.h"
-#include "mpact/sim/generic/program_error.h"
#include "mpact/sim/generic/type_helpers.h"
#include "mpact/sim/util/memory/memory_interface.h"
#include "riscv//riscv_state.h"
@@ -38,10 +37,6 @@
CheriotDecoder::CheriotDecoder(CheriotState *state,
util::MemoryInterface *memory)
: state_(state), memory_(memory) {
- // Get a handle to the internal error in the program error controller.
- decode_error_ = state->program_error_controller()->GetProgramError(
- generic::ProgramErrorController::kInternalErrorName);
-
// Need a data buffer to load instructions from memory. Allocate a single
// buffer that can be reused for each instruction word.
inst_db_ = db_factory_.Allocate<uint32_t>(1);
diff --git a/cheriot/cheriot_decoder.h b/cheriot/cheriot_decoder.h
index 7912734..f219457 100644
--- a/cheriot/cheriot_decoder.h
+++ b/cheriot/cheriot_decoder.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef MPACT_CHERIOT__CHERIOT_DECODER_H_
-#define MPACT_CHERIOT__CHERIOT_DECODER_H_
+#ifndef MPACT_CHERIOT_CHERIOT_DECODER_H_
+#define MPACT_CHERIOT_CHERIOT_DECODER_H_
#include <cstdint>
#include <memory>
@@ -28,7 +28,6 @@
#include "mpact/sim/generic/data_buffer.h"
#include "mpact/sim/generic/decoder_interface.h"
#include "mpact/sim/generic/instruction.h"
-#include "mpact/sim/generic/program_error.h"
#include "mpact/sim/util/memory/memory_interface.h"
namespace mpact {
@@ -81,7 +80,6 @@
private:
CheriotState *state_;
util::MemoryInterface *memory_;
- std::unique_ptr<generic::ProgramError> decode_error_;
generic::DataBufferFactory db_factory_;
generic::DataBuffer *inst_db_;
isa32::RiscVCheriotEncoding *cheriot_encoding_;
@@ -93,4 +91,4 @@
} // namespace sim
} // namespace mpact
-#endif // MPACT_CHERIOT__CHERIOT_DECODER_H_
+#endif // MPACT_CHERIOT_CHERIOT_DECODER_H_
diff --git a/cheriot/cheriot_f_getters.h b/cheriot/cheriot_f_getters.h
new file mode 100644
index 0000000..c4565ba
--- /dev/null
+++ b/cheriot/cheriot_f_getters.h
@@ -0,0 +1,116 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef MPACT_CHERIOT_CHERIOT_F_GETTERS_H_
+#define MPACT_CHERIOT_CHERIOT_F_GETTERS_H_
+
+#include <cstdint>
+#include <string>
+
+#include "absl/container/flat_hash_map.h"
+#include "absl/functional/any_invocable.h"
+#include "absl/strings/str_cat.h"
+#include "cheriot/cheriot_getter_helpers.h"
+#include "cheriot/riscv_cheriot_encoding_common.h"
+#include "mpact/sim/generic/immediate_operand.h"
+#include "mpact/sim/generic/literal_operand.h"
+#include "mpact/sim/generic/operand_interface.h"
+#include "riscv//riscv_register.h"
+#include "riscv//riscv_state.h"
+
+namespace mpact {
+namespace sim {
+namespace cheriot {
+
+using ::mpact::sim::cheriot::RiscVCheriotEncodingCommon;
+using ::mpact::sim::generic::DestinationOperandInterface;
+using ::mpact::sim::generic::ImmediateOperand;
+using ::mpact::sim::generic::IntLiteralOperand;
+using ::mpact::sim::generic::SourceOperandInterface;
+using ::mpact::sim::riscv::RiscVState;
+using ::mpact::sim::riscv::RVFpRegister;
+
+using SourceOpGetterMap =
+ absl::flat_hash_map<int, absl::AnyInvocable<SourceOperandInterface *()>>;
+using DestOpGetterMap =
+ absl::flat_hash_map<int,
+ absl::AnyInvocable<DestinationOperandInterface *(int)>>;
+
+template <typename Enum, typename Extractors>
+void AddCheriotFSourceGetters(SourceOpGetterMap &getter_map,
+ RiscVCheriotEncodingCommon *common) {
+ Insert(getter_map, *Enum::kFrs1, [common]() {
+ int num = Extractors::RType::ExtractRs1(common->inst_word());
+ return GetRegisterSourceOp<RVFpRegister>(
+ common->state(), absl::StrCat(RiscVState::kFregPrefix, num));
+ });
+ Insert(getter_map, *Enum::kFrs2, [common]() {
+ int num = Extractors::RType::ExtractRs2(common->inst_word());
+ return GetRegisterSourceOp<RVFpRegister>(
+ common->state(), absl::StrCat(RiscVState::kFregPrefix, num));
+ });
+ Insert(getter_map, *Enum::kFrs3, [common]() {
+ int num = Extractors::R4Type::ExtractRs3(common->inst_word());
+ return GetRegisterSourceOp<RVFpRegister>(
+ common->state(), absl::StrCat(RiscVState::kFregPrefix, num));
+ });
+ Insert(getter_map, *Enum::kFs1, [common]() {
+ int num = Extractors::RType::ExtractRs1(common->inst_word());
+ return GetRegisterSourceOp<RVFpRegister>(
+ common->state(), absl::StrCat(RiscVState::kFregPrefix, num));
+ });
+ Insert(getter_map, *Enum::kRm, [common]() -> SourceOperandInterface * {
+ uint32_t rm = (common->inst_word() >> 12) & 0x7;
+ switch (rm) {
+ case 0:
+ return new generic::IntLiteralOperand<0>();
+ case 1:
+ return new generic::IntLiteralOperand<1>();
+ case 2:
+ return new generic::IntLiteralOperand<2>();
+ case 3:
+ return new generic::IntLiteralOperand<3>();
+ case 4:
+ return new generic::IntLiteralOperand<4>();
+ case 5:
+ return new generic::IntLiteralOperand<5>();
+ case 6:
+ return new generic::IntLiteralOperand<6>();
+ case 7:
+ return new generic::IntLiteralOperand<7>();
+ default:
+ return nullptr;
+ }
+ });
+}
+
+template <typename Enum, typename Extractors>
+void AddCheriotFDestGetters(DestOpGetterMap &getter_map,
+ RiscVCheriotEncodingCommon *common) {
+ Insert(getter_map, *Enum::kFrd, [common](int latency) {
+ int num = Extractors::RType::ExtractRd(common->inst_word());
+ return GetRegisterDestinationOp<RVFpRegister>(
+ common->state(), absl::StrCat(RiscVState::kFregPrefix, num), latency);
+ });
+ Insert(getter_map, *Enum::kFflags, [common](int latency) {
+ return GetCSRSetBitsDestinationOp<uint32_t>(common->state(), "fflags",
+ latency, "");
+ });
+}
+
+} // namespace cheriot
+} // namespace sim
+} // namespace mpact
+
+#endif // MPACT_CHERIOT_CHERIOT_F_GETTERS_H_
diff --git a/cheriot/cheriot_getter_helpers.h b/cheriot/cheriot_getter_helpers.h
new file mode 100644
index 0000000..d6546cb
--- /dev/null
+++ b/cheriot/cheriot_getter_helpers.h
@@ -0,0 +1,160 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef MPACT_CHERIOT_CHERIOT_GETTER_HELPERS_H_
+#define MPACT_CHERIOT_CHERIOT_GETTER_HELPERS_H_
+
+#include <string>
+#include <vector>
+
+#include "absl/log/log.h"
+#include "absl/strings/str_cat.h"
+#include "absl/types/span.h"
+#include "cheriot/cheriot_register.h"
+#include "cheriot/cheriot_state.h"
+#include "mpact/sim/generic/operand_interface.h"
+#include "mpact/sim/generic/register.h"
+#include "riscv//riscv_register.h"
+
+namespace mpact {
+namespace sim {
+namespace cheriot {
+
+using ::mpact::sim::generic::DestinationOperandInterface;
+using ::mpact::sim::generic::SourceOperandInterface;
+using ::mpact::sim::riscv::RV32VectorDestinationOperand;
+using ::mpact::sim::riscv::RV32VectorSourceOperand;
+
+constexpr int kNumRegTable[8] = {8, 1, 2, 1, 4, 1, 2, 1};
+
+template <typename M, typename E, typename G>
+inline void Insert(M &map, E entry, G getter) {
+ map.insert(std::make_pair(static_cast<int>(entry), getter));
+}
+
+// Generic helper functions to create register operands.
+template <typename RegType>
+inline DestinationOperandInterface *GetRegisterDestinationOp(
+ CheriotState *state, std::string name, int latency) {
+ auto *reg = state->GetRegister<RegType>(name).first;
+ return reg->CreateDestinationOperand(latency);
+}
+
+template <typename RegType>
+inline DestinationOperandInterface *GetRegisterDestinationOp(
+ CheriotState *state, std::string name, int latency, std::string op_name) {
+ auto *reg = state->GetRegister<RegType>(name).first;
+ return reg->CreateDestinationOperand(latency, op_name);
+}
+
+template <typename T>
+inline DestinationOperandInterface *GetCSRSetBitsDestinationOp(
+ CheriotState *state, std::string name, int latency, std::string op_name) {
+ auto result = state->csr_set()->GetCsr(name);
+ if (!result.ok()) {
+ LOG(ERROR) << "No such CSR '" << name << "'";
+ return nullptr;
+ }
+ auto *csr = result.value();
+ auto *op = csr->CreateSetDestinationOperand(latency, op_name);
+ return op;
+}
+
+template <typename RegType>
+inline SourceOperandInterface *GetRegisterSourceOp(CheriotState *state,
+ std::string name) {
+ auto *reg = state->GetRegister<RegType>(name).first;
+ auto *op = reg->CreateSourceOperand();
+ return op;
+}
+
+template <typename RegType>
+inline SourceOperandInterface *GetRegisterSourceOp(CheriotState *state,
+ std::string name,
+ std::string op_name) {
+ auto *reg = state->GetRegister<RegType>(name).first;
+ auto *op = reg->CreateSourceOperand(op_name);
+ return op;
+}
+
+template <typename RegType>
+inline void GetVRegGroup(CheriotState *state, int reg_num,
+ std::vector<generic::RegisterBase *> *vreg_group) {
+ // The number of registers in a vector register group depends on the register
+ // index: 0, 8, 16, 24 each have 8 registers, 4, 12, 20, 28 each have 4,
+ // 2, 6, 10, 14, 18, 22, 26, 30 each have two, and all odd numbered register
+ // groups have only 1.
+ int num_regs = kNumRegTable[reg_num % 8];
+ for (int i = 0; i < num_regs; i++) {
+ auto vreg_name = absl::StrCat(CheriotState::kVregPrefix, reg_num + i);
+ vreg_group->push_back(state->GetRegister<RegType>(vreg_name).first);
+ }
+}
+template <typename RegType>
+inline SourceOperandInterface *GetVectorRegisterSourceOp(CheriotState *state,
+ int reg_num) {
+ std::vector<generic::RegisterBase *> vreg_group;
+ GetVRegGroup<RegType>(state, reg_num, &vreg_group);
+ auto *v_src_op = new RV32VectorSourceOperand(
+ absl::Span<generic::RegisterBase *>(vreg_group),
+ absl::StrCat(CheriotState::kVregPrefix, reg_num));
+ return v_src_op;
+}
+
+template <typename RegType>
+inline DestinationOperandInterface *GetVectorRegisterDestinationOp(
+ CheriotState *state, int latency, int reg_num) {
+ std::vector<generic::RegisterBase *> vreg_group;
+ GetVRegGroup<RegType>(state, reg_num, &vreg_group);
+ auto *v_dst_op = new RV32VectorDestinationOperand(
+ absl::Span<generic::RegisterBase *>(vreg_group), latency,
+ absl::StrCat(CheriotState::kVregPrefix, reg_num));
+ return v_dst_op;
+}
+
+template <typename RegType>
+inline SourceOperandInterface *GetVectorMaskRegisterSourceOp(
+ CheriotState *state, int reg_num) {
+ // Mask register groups only have a single register.
+ std::vector<generic::RegisterBase *> vreg_group;
+ vreg_group.push_back(state
+ ->GetRegister<RegType>(
+ absl::StrCat(CheriotState::kVregPrefix, reg_num))
+ .first);
+ auto *v_src_op = new RV32VectorSourceOperand(
+ absl::Span<generic::RegisterBase *>(vreg_group),
+ absl::StrCat(CheriotState::kVregPrefix, reg_num));
+ return v_src_op;
+}
+
+template <typename RegType>
+inline DestinationOperandInterface *GetVectorMaskRegisterDestinationOp(
+ CheriotState *state, int latency, int reg_num) {
+ // Mask register groups only have a single register.
+ std::vector<generic::RegisterBase *> vreg_group;
+ vreg_group.push_back(state
+ ->GetRegister<RegType>(
+ absl::StrCat(CheriotState::kVregPrefix, reg_num))
+ .first);
+ auto *v_dst_op = new RV32VectorDestinationOperand(
+ absl::Span<generic::RegisterBase *>(vreg_group), latency,
+ absl::StrCat(CheriotState::kVregPrefix, reg_num));
+ return v_dst_op;
+}
+
+} // namespace cheriot
+} // namespace sim
+} // namespace mpact
+
+#endif // MPACT_CHERIOT_CHERIOT_GETTER_HELPERS_H_
diff --git a/cheriot/cheriot_getters.h b/cheriot/cheriot_getters.h
new file mode 100644
index 0000000..efc2af2
--- /dev/null
+++ b/cheriot/cheriot_getters.h
@@ -0,0 +1,391 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef MPACT_CHERIOT_CHERIOT_GETTERS_H_
+#define MPACT_CHERIOT_CHERIOT_GETTERS_H_
+
+#include <cstdint>
+#include <string>
+
+#include "absl/container/flat_hash_map.h"
+#include "absl/functional/any_invocable.h"
+#include "absl/strings/str_cat.h"
+#include "cheriot/cheriot_getter_helpers.h"
+#include "cheriot/cheriot_register.h"
+#include "cheriot/cheriot_state.h"
+#include "cheriot/riscv_cheriot_encoding_common.h"
+#include "cheriot/riscv_cheriot_register_aliases.h"
+#include "mpact/sim/generic/immediate_operand.h"
+#include "mpact/sim/generic/literal_operand.h"
+#include "mpact/sim/generic/operand_interface.h"
+
+namespace mpact {
+namespace sim {
+namespace cheriot {
+
+using ::mpact::sim::cheriot::RiscVCheriotEncodingCommon;
+using ::mpact::sim::generic::DestinationOperandInterface;
+using ::mpact::sim::generic::ImmediateOperand;
+using ::mpact::sim::generic::IntLiteralOperand;
+using ::mpact::sim::generic::SourceOperandInterface;
+
+using SourceOpGetterMap =
+ absl::flat_hash_map<int, absl::AnyInvocable<SourceOperandInterface *()>>;
+using DestOpGetterMap =
+ absl::flat_hash_map<int,
+ absl::AnyInvocable<DestinationOperandInterface *(int)>>;
+
+template <typename Enum, typename Extractors>
+void AddCheriotSourceGetters(SourceOpGetterMap &getter_map,
+ RiscVCheriotEncodingCommon *common) {
+ // Source operand getters.
+ Insert(getter_map, *Enum::kAAq, [common]() -> SourceOperandInterface * {
+ if (Extractors::Inst32Format::ExtractAq(common->inst_word())) {
+ return new IntLiteralOperand<1>();
+ }
+ return new IntLiteralOperand<0>();
+ });
+ Insert(getter_map, *Enum::kARl, [common]() -> SourceOperandInterface * {
+ if (Extractors::Inst32Format::ExtractRl(common->inst_word())) {
+ return new generic::IntLiteralOperand<1>();
+ }
+ return new generic::IntLiteralOperand<0>();
+ });
+ Insert(getter_map, *Enum::kBImm12, [common]() {
+ return new ImmediateOperand<int32_t>(
+ Extractors::Inst32Format::ExtractBImm(common->inst_word()));
+ });
+ Insert(getter_map, *Enum::kC2, [common]() {
+ return GetRegisterSourceOp<CheriotRegister>(common->state(), "c2", "csp");
+ });
+ Insert(getter_map, *Enum::kC3cs1, [common]() {
+ auto num = Extractors::CS::ExtractRs1(common->inst_word());
+ return GetRegisterSourceOp<CheriotRegister>(
+ common->state(), absl::StrCat(CheriotState::kCregPrefix, num),
+ kCRegisterAliases[num]);
+ });
+ Insert(getter_map, *Enum::kC3cs2, [common]() {
+ auto num = Extractors::CS::ExtractRs2(common->inst_word());
+ return GetRegisterSourceOp<CheriotRegister>(
+ common->state(), absl::StrCat(CheriotState::kCregPrefix, num),
+ kCRegisterAliases[num]);
+ });
+ Insert(getter_map, *Enum::kC3rs1, [common]() {
+ auto num = Extractors::CS::ExtractRs1(common->inst_word());
+ return GetRegisterSourceOp<CheriotRegister>(
+ common->state(), absl::StrCat(CheriotState::kXregPrefix, num),
+ kXRegisterAliases[num]);
+ });
+ Insert(getter_map, *Enum::kC3rs2, [common]() {
+ auto num = Extractors::CS::ExtractRs2(common->inst_word());
+ return GetRegisterSourceOp<CheriotRegister>(
+ common->state(), absl::StrCat(CheriotState::kXregPrefix, num),
+ kXRegisterAliases[num]);
+ });
+ Insert(getter_map, *Enum::kCcs2, [common]() {
+ auto num = Extractors::CSS::ExtractRs2(common->inst_word());
+ return GetRegisterSourceOp<CheriotRegister>(
+ common->state(), absl::StrCat(CheriotState::kCregPrefix, num),
+ kCRegisterAliases[num]);
+ });
+ Insert(getter_map, *Enum::kCgp, [common]() {
+ return GetRegisterSourceOp<CheriotRegister>(common->state(), "c3", "c3");
+ });
+ Insert(getter_map, *Enum::kCSRUimm5, [common]() {
+ return new ImmediateOperand<uint32_t>(
+ Extractors::Inst32Format::ExtractIUimm5(common->inst_word()));
+ });
+ Insert(getter_map, *Enum::kCrs1, [common]() {
+ auto num = Extractors::CR::ExtractRs1(common->inst_word());
+ return GetRegisterSourceOp<CheriotRegister>(
+ common->state(), absl::StrCat(CheriotState::kXregPrefix, num),
+ kXRegisterAliases[num]);
+ });
+ Insert(getter_map, *Enum::kCrs2, [common]() {
+ auto num = Extractors::CR::ExtractRs2(common->inst_word());
+ return GetRegisterSourceOp<CheriotRegister>(
+ common->state(), absl::StrCat(CheriotState::kXregPrefix, num),
+ kXRegisterAliases[num]);
+ });
+ Insert(getter_map, *Enum::kCs1, [common]() {
+ auto num = Extractors::RType::ExtractRs1(common->inst_word());
+ return GetRegisterSourceOp<CheriotRegister>(
+ common->state(), absl::StrCat(CheriotState::kCregPrefix, num),
+ kCRegisterAliases[num]);
+ });
+ Insert(getter_map, *Enum::kCs2, [common]() {
+ auto num = Extractors::RType::ExtractRs2(common->inst_word());
+ return GetRegisterSourceOp<CheriotRegister>(
+ common->state(), absl::StrCat(CheriotState::kCregPrefix, num),
+ kCRegisterAliases[num]);
+ });
+ Insert(getter_map, *Enum::kCsr, [common]() {
+ auto csr_indx = Extractors::IType::ExtractUImm12(common->inst_word());
+ auto res = common->state()->csr_set()->GetCsr(csr_indx);
+ if (!res.ok()) {
+ return new ImmediateOperand<uint32_t>(csr_indx);
+ }
+ auto *csr = res.value();
+ return new ImmediateOperand<uint32_t>(csr_indx, csr->name());
+ });
+ Insert(getter_map, *Enum::kICbImm8, [common]() {
+ return new ImmediateOperand<int32_t>(
+ Extractors::Inst16Format::ExtractBimm(common->inst_word()));
+ });
+ Insert(getter_map, *Enum::kICiImm6, [common]() {
+ return new ImmediateOperand<int32_t>(
+ Extractors::CI::ExtractImm6(common->inst_word()));
+ });
+ Insert(getter_map, *Enum::kICiImm612, [common]() {
+ return new ImmediateOperand<int32_t>(
+ Extractors::Inst16Format::ExtractImm18(common->inst_word()));
+ });
+ Insert(getter_map, *Enum::kICiUimm6, [common]() {
+ return new ImmediateOperand<uint32_t>(
+ Extractors::Inst16Format::ExtractUimm6(common->inst_word()));
+ });
+ Insert(getter_map, *Enum::kICiUimm6x4, [common]() {
+ return new ImmediateOperand<uint32_t>(
+ Extractors::Inst16Format::ExtractCiImmW(common->inst_word()));
+ });
+ Insert(getter_map, *Enum::kICiImm6x16, [common]() {
+ return new ImmediateOperand<int32_t>(
+ Extractors::Inst16Format::ExtractCiImm10(common->inst_word()));
+ });
+ Insert(getter_map, *Enum::kICiUimm6x8, [common]() {
+ return new ImmediateOperand<uint32_t>(
+ Extractors::Inst16Format::ExtractCiImmD(common->inst_word()));
+ });
+ Insert(getter_map, *Enum::kICiwUimm8x4, [common]() {
+ return new ImmediateOperand<uint32_t>(
+ Extractors::Inst16Format::ExtractCiwImm10(common->inst_word()));
+ });
+ Insert(getter_map, *Enum::kICjImm11, [common]() {
+ return new ImmediateOperand<int32_t>(
+ Extractors::Inst16Format::ExtractJimm(common->inst_word()));
+ });
+ Insert(getter_map, *Enum::kIClUimm5x4, [common]() {
+ return new ImmediateOperand<uint32_t>(
+ Extractors::Inst16Format::ExtractClImmW(common->inst_word()));
+ });
+ Insert(getter_map, *Enum::kIClUimm5x8, [common]() {
+ return new ImmediateOperand<uint32_t>(
+ Extractors::Inst16Format::ExtractClImmD(common->inst_word()));
+ });
+ Insert(getter_map, *Enum::kICshUimm6, [common]() {
+ return new ImmediateOperand<uint32_t>(
+ Extractors::CSH::ExtractUimm6(common->inst_word()));
+ });
+ Insert(getter_map, *Enum::kICshImm6, [common]() {
+ return new ImmediateOperand<uint32_t>(
+ Extractors::CSH::ExtractImm6(common->inst_word()));
+ });
+ Insert(getter_map, *Enum::kICssUimm6x4, [common]() {
+ return new ImmediateOperand<uint32_t>(
+ Extractors::Inst16Format::ExtractCssImmW(common->inst_word()));
+ });
+ Insert(getter_map, *Enum::kICssUimm6x8, [common]() {
+ return new ImmediateOperand<uint32_t>(
+ Extractors::Inst16Format::ExtractCssImmD(common->inst_word()));
+ });
+ Insert(getter_map, *Enum::kIImm12, [common]() {
+ return new ImmediateOperand<int32_t>(
+ Extractors::Inst32Format::ExtractImm12(common->inst_word()));
+ });
+ Insert(getter_map, *Enum::kIUimm5, [common]() {
+ return new ImmediateOperand<uint32_t>(
+ Extractors::I5Type::ExtractRUimm5(common->inst_word()));
+ });
+ Insert(getter_map, *Enum::kIUimm12, [common]() {
+ return new ImmediateOperand<uint32_t>(
+ Extractors::Inst32Format::ExtractUImm12(common->inst_word()));
+ });
+ Insert(getter_map, *Enum::kJImm12, [common]() {
+ return new ImmediateOperand<int32_t>(
+ Extractors::Inst32Format::ExtractImm12(common->inst_word()));
+ });
+ Insert(getter_map, *Enum::kJImm20, [common]() {
+ return new ImmediateOperand<int32_t>(
+ Extractors::Inst32Format::ExtractJImm(common->inst_word()));
+ });
+ Insert(getter_map, *Enum::kPcc, [common]() {
+ return GetRegisterSourceOp<CheriotRegister>(common->state(), "pcc", "pcc");
+ });
+ Insert(getter_map, *Enum::kRd, [common]() -> SourceOperandInterface * {
+ int num = Extractors::RType::ExtractRd(common->inst_word());
+ if (num == 0) return new generic::IntLiteralOperand<0>({1});
+ return GetRegisterSourceOp<CheriotRegister>(
+ common->state(), absl::StrCat(CheriotState::kXregPrefix, num),
+ kXRegisterAliases[num]);
+ });
+ Insert(getter_map, *Enum::kRs1, [common]() -> SourceOperandInterface * {
+ int num = Extractors::RType::ExtractRs1(common->inst_word());
+ if (num == 0) return new generic::IntLiteralOperand<0>({1});
+ return GetRegisterSourceOp<CheriotRegister>(
+ common->state(), absl::StrCat(CheriotState::kXregPrefix, num),
+ kXRegisterAliases[num]);
+ });
+ Insert(getter_map, *Enum::kRs2, [common]() -> SourceOperandInterface * {
+ int num = Extractors::RType::ExtractRs2(common->inst_word());
+ if (num == 0) return new generic::IntLiteralOperand<0>({1});
+ return GetRegisterSourceOp<CheriotRegister>(
+ common->state(), absl::StrCat(CheriotState::kXregPrefix, num),
+ kXRegisterAliases[num]);
+ });
+ Insert(getter_map, *Enum::kSImm12, [common]() {
+ return new ImmediateOperand<int32_t>(
+ Extractors::SType::ExtractSImm(common->inst_word()));
+ });
+ Insert(getter_map, *Enum::kScr, [common]() -> SourceOperandInterface * {
+ int csr_indx = Extractors::RType::ExtractRs2(common->inst_word());
+ std::string csr_name;
+ switch (csr_indx) {
+ case 28:
+ csr_name = "mtcc";
+ break;
+ case 29:
+ csr_name = "mtdc";
+ break;
+ case 30:
+ csr_name = "mscratchc";
+ break;
+ case 31:
+ csr_name = "mepcc";
+ break;
+ default:
+ return nullptr;
+ }
+ auto res = common->state()->csr_set()->GetCsr(csr_name);
+ if (!res.ok()) {
+ return GetRegisterSourceOp<CheriotRegister>(common->state(), csr_name,
+ csr_name);
+ }
+ auto *csr = res.value();
+ auto *op = csr->CreateSourceOperand();
+ return op;
+ });
+ Insert(getter_map, *Enum::kSImm20, [common]() {
+ return new ImmediateOperand<int32_t>(
+ Extractors::UType::ExtractSImm(common->inst_word()));
+ });
+ Insert(getter_map, *Enum::kUImm20, [common]() {
+ return new ImmediateOperand<int32_t>(
+ Extractors::Inst32Format::ExtractUImm(common->inst_word()));
+ });
+ Insert(getter_map, *Enum::kX0,
+ []() { return new generic::IntLiteralOperand<0>({1}); });
+ Insert(getter_map, *Enum::kX2, [common]() {
+ return GetRegisterSourceOp<CheriotRegister>(
+ common->state(), absl::StrCat(CheriotState::kXregPrefix, 2),
+ kXRegisterAliases[2]);
+ });
+}
+
+template <typename Enum, typename Extractors>
+void AddCheriotDestGetters(DestOpGetterMap &getter_map,
+ RiscVCheriotEncodingCommon *common) {
+ // Destination operand getters.
+ Insert(getter_map, *Enum::kC2, [common](int latency) {
+ return GetRegisterDestinationOp<CheriotRegister>(common->state(), "c2",
+ latency, "csp");
+ });
+ Insert(getter_map, *Enum::kC3cd, [common](int latency) {
+ int num = Extractors::CL::ExtractRd(common->inst_word());
+ return GetRegisterDestinationOp<CheriotRegister>(
+ common->state(), absl::StrCat(CheriotState::kCregPrefix, num), latency,
+ kCRegisterAliases[num]);
+ });
+ Insert(getter_map, *Enum::kC3rd, [common](int latency) {
+ int num = Extractors::CL::ExtractRd(common->inst_word());
+ if (num == 0) {
+ return GetRegisterDestinationOp<CheriotRegister>(common->state(),
+ "X0Dest", latency);
+ }
+ return GetRegisterDestinationOp<CheriotRegister>(
+ common->state(), absl::StrCat(CheriotState::kXregPrefix, num), latency,
+ kXRegisterAliases[num]);
+ });
+ Insert(getter_map, *Enum::kC3rs1, [common](int latency) {
+ int num = Extractors::CL::ExtractRs1(common->inst_word());
+ return GetRegisterDestinationOp<CheriotRegister>(
+ common->state(), absl::StrCat(CheriotState::kXregPrefix, num), latency,
+ kXRegisterAliases[num]);
+ });
+ Insert(getter_map, *Enum::kCd, [common](int latency) {
+ int num = Extractors::RType::ExtractRd(common->inst_word());
+ if (num == 0) {
+ return GetRegisterDestinationOp<CheriotRegister>(common->state(),
+ "X0Dest", latency);
+ }
+ return GetRegisterDestinationOp<CheriotRegister>(
+ common->state(), absl::StrCat(CheriotState::kCregPrefix, num), latency,
+ kCRegisterAliases[num]);
+ });
+ Insert(getter_map, *Enum::kCsr, [common](int latency) {
+ return GetRegisterDestinationOp<CheriotRegister>(
+ common->state(), CheriotState::kCsrName, latency);
+ });
+ Insert(getter_map, *Enum::kScr,
+ [common](int latency) -> DestinationOperandInterface * {
+ int csr_indx = Extractors::RType::ExtractRs2(common->inst_word());
+ std::string csr_name;
+ switch (csr_indx) {
+ case 28:
+ csr_name = "mtcc";
+ break;
+ case 29:
+ csr_name = "mtdc";
+ break;
+ case 30:
+ csr_name = "mscratchc";
+ break;
+ case 31:
+ csr_name = "mepcc";
+ break;
+ default:
+ return nullptr;
+ }
+ auto res = common->state()->csr_set()->GetCsr(csr_name);
+ if (!res.ok()) {
+ return GetRegisterDestinationOp<CheriotRegister>(
+ common->state(), csr_name, latency);
+ }
+ auto *csr = res.value();
+ auto *op = csr->CreateWriteDestinationOperand(latency, csr_name);
+ return op;
+ });
+ Insert(getter_map, *Enum::kRd,
+ [common](int latency) -> DestinationOperandInterface * {
+ int num = Extractors::RType::ExtractRd(common->inst_word());
+ if (num == 0) {
+ return GetRegisterDestinationOp<CheriotRegister>(common->state(),
+ "X0Dest", 0);
+ } else {
+ return GetRegisterDestinationOp<CheriotRegister>(
+ common->state(), absl::StrCat(CheriotState::kXregPrefix, num),
+ latency, kXRegisterAliases[num]);
+ }
+ });
+ Insert(getter_map, *Enum::kX1, [common](int latency) {
+ return GetRegisterDestinationOp<CheriotRegister>(
+ common->state(), absl::StrCat(CheriotState::kXregPrefix, 1), latency,
+ kXRegisterAliases[1]);
+ });
+}
+
+} // namespace cheriot
+} // namespace sim
+} // namespace mpact
+
+#endif // MPACT_CHERIOT_CHERIOT_GETTERS_H_
diff --git a/cheriot/cheriot_rvv_decoder.cc b/cheriot/cheriot_rvv_decoder.cc
new file mode 100644
index 0000000..bd576d8
--- /dev/null
+++ b/cheriot/cheriot_rvv_decoder.cc
@@ -0,0 +1,103 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cheriot/cheriot_rvv_decoder.h"
+
+#include <cstdint>
+#include <string>
+
+#include "cheriot/cheriot_state.h"
+#include "cheriot/riscv_cheriot_rvv_decoder.h"
+#include "cheriot/riscv_cheriot_rvv_encoding.h"
+#include "cheriot/riscv_cheriot_rvv_enums.h"
+#include "mpact/sim/generic/instruction.h"
+#include "mpact/sim/generic/type_helpers.h"
+#include "mpact/sim/util/memory/memory_interface.h"
+#include "riscv//riscv_state.h"
+
+namespace mpact {
+namespace sim {
+namespace cheriot {
+
+using RV_EC = ::mpact::sim::riscv::ExceptionCode;
+
+using ::mpact::sim::generic::operator*; // NOLINT: is used below (clang error).
+
+CheriotRVVDecoder::CheriotRVVDecoder(CheriotState *state,
+ util::MemoryInterface *memory)
+ : state_(state), memory_(memory) {
+ // Need a data buffer to load instructions from memory. Allocate a single
+ // buffer that can be reused for each instruction word.
+ inst_db_ = db_factory_.Allocate<uint32_t>(1);
+ // Allocate the isa factory class, the top level isa decoder instance, and
+ // the encoding parser.
+ cheriot_rvv_isa_factory_ = new CheriotRVVIsaFactory();
+ cheriot_rvv_isa_ = new isa32_rvv::RiscVCheriotRVVInstructionSet(
+ state, cheriot_rvv_isa_factory_);
+ cheriot_rvv_encoding_ = new isa32_rvv::RiscVCheriotRVVEncoding(state);
+}
+
+CheriotRVVDecoder::~CheriotRVVDecoder() {
+ delete cheriot_rvv_isa_;
+ delete cheriot_rvv_isa_factory_;
+ delete cheriot_rvv_encoding_;
+ inst_db_->DecRef();
+}
+
+generic::Instruction *CheriotRVVDecoder::DecodeInstruction(uint64_t address) {
+ // First check that the address is aligned properly. If not, create and return
+ // an instruction object that will raise an exception.
+ if (address & 0x1) {
+ auto *inst = new generic::Instruction(0, state_);
+ inst->set_size(1);
+ inst->SetDisassemblyString("Misaligned instruction address");
+ inst->set_opcode(*isa32_rvv::OpcodeEnum::kNone);
+ inst->set_address(address);
+ inst->set_semantic_function([this](generic::Instruction *inst) {
+ state_->Trap(/*is_interrupt*/ false, inst->address(),
+ *RV_EC::kInstructionAddressMisaligned, inst->address() ^ 0x1,
+ inst);
+ });
+ return inst;
+ }
+
+ // If the address is greater than the max address, return an instruction
+ // that will raise an exception.
+ if (address > state_->max_physical_address()) {
+ auto *inst = new generic::Instruction(0, state_);
+ inst->set_size(0);
+ inst->SetDisassemblyString("Instruction access fault");
+ inst->set_opcode(*isa32_rvv::OpcodeEnum::kNone);
+ inst->set_address(address);
+ inst->set_semantic_function([this](generic::Instruction *inst) {
+ state_->Trap(/*is_interrupt*/ false, inst->address(),
+ *RV_EC::kInstructionAccessFault, inst->address(), nullptr);
+ });
+ return inst;
+ }
+
+ // Read the instruction word from memory and parse it in the encoding parser.
+ memory_->Load(address, inst_db_, nullptr, nullptr);
+ uint32_t iword = inst_db_->Get<uint32_t>(0);
+ cheriot_rvv_encoding_->ParseInstruction(iword);
+
+ // Call the isa decoder to obtain a new instruction object for the instruction
+ // word that was parsed above.
+ auto *instruction = cheriot_rvv_isa_->Decode(address, cheriot_rvv_encoding_);
+ return instruction;
+}
+
+} // namespace cheriot
+} // namespace sim
+} // namespace mpact
diff --git a/cheriot/cheriot_rvv_decoder.h b/cheriot/cheriot_rvv_decoder.h
new file mode 100644
index 0000000..6731ab1
--- /dev/null
+++ b/cheriot/cheriot_rvv_decoder.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MPACT_CHERIOT_CHERIOT_RVV_DECODER_H_
+#define MPACT_CHERIOT_CHERIOT_RVV_DECODER_H_
+
+#include <cstdint>
+#include <memory>
+
+#include "cheriot/cheriot_state.h"
+#include "cheriot/riscv_cheriot_rvv_decoder.h"
+#include "cheriot/riscv_cheriot_rvv_encoding.h"
+#include "cheriot/riscv_cheriot_rvv_enums.h"
+#include "mpact/sim/generic/arch_state.h"
+#include "mpact/sim/generic/data_buffer.h"
+#include "mpact/sim/generic/decoder_interface.h"
+#include "mpact/sim/generic/instruction.h"
+#include "mpact/sim/util/memory/memory_interface.h"
+
+namespace mpact {
+namespace sim {
+namespace cheriot {
+
+using ::mpact::sim::generic::ArchState;
+
+// This is the factory class needed by the generated decoder. It is responsible
+// for creating the decoder for each slot instance. Since the riscv architecture
+// only has a single slot, it's a pretty simple class.
+class CheriotRVVIsaFactory
+ : public isa32_rvv::RiscVCheriotRVVInstructionSetFactory {
+ public:
+ std::unique_ptr<isa32_rvv::RiscvCheriotRvvSlot> CreateRiscvCheriotRvvSlot(
+ ArchState *state) override {
+ return std::make_unique<isa32_rvv::RiscvCheriotRvvSlot>(state);
+ }
+};
+
+// This class implements the generic DecoderInterface and provides a bridge
+// to the (isa specific) generated decoder classes.
+class CheriotRVVDecoder : public generic::DecoderInterface {
+ public:
+ using SlotEnum = isa32_rvv::SlotEnum;
+ using OpcodeEnum = isa32_rvv::OpcodeEnum;
+
+ CheriotRVVDecoder(CheriotState *state, util::MemoryInterface *memory);
+ CheriotRVVDecoder() = delete;
+ ~CheriotRVVDecoder() override;
+
+ // This will always return a valid instruction that can be executed. In the
+ // case of a decode error, the semantic function in the instruction object
+ // instance will raise an internal simulator error when executed.
+ generic::Instruction *DecodeInstruction(uint64_t address) override;
+
+ // Return the number of opcodes supported by this decoder.
+ int GetNumOpcodes() const override {
+ return static_cast<int>(OpcodeEnum::kPastMaxValue);
+ }
+ // Return the name of the opcode at the given index.
+ const char *GetOpcodeName(int index) const override {
+ return isa32_rvv::kOpcodeNames[index];
+ }
+
+ // Getter.
+ isa32_rvv::RiscVCheriotRVVEncoding *cheriot_rvv_encoding() const {
+ return cheriot_rvv_encoding_;
+ }
+
+ private:
+ CheriotState *state_;
+ util::MemoryInterface *memory_;
+ generic::DataBufferFactory db_factory_;
+ generic::DataBuffer *inst_db_;
+ isa32_rvv::RiscVCheriotRVVEncoding *cheriot_rvv_encoding_;
+ isa32_rvv::RiscVCheriotRVVInstructionSetFactory *cheriot_rvv_isa_factory_;
+ isa32_rvv::RiscVCheriotRVVInstructionSet *cheriot_rvv_isa_;
+};
+
+} // namespace cheriot
+} // namespace sim
+} // namespace mpact
+
+#endif // MPACT_CHERIOT_CHERIOT_RVV_DECODER_H_
diff --git a/cheriot/cheriot_rvv_fp_decoder.cc b/cheriot/cheriot_rvv_fp_decoder.cc
new file mode 100644
index 0000000..d6d3b74
--- /dev/null
+++ b/cheriot/cheriot_rvv_fp_decoder.cc
@@ -0,0 +1,104 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cheriot/cheriot_rvv_fp_decoder.h"
+
+#include <cstdint>
+#include <string>
+
+#include "cheriot/cheriot_state.h"
+#include "cheriot/riscv_cheriot_rvv_fp_decoder.h"
+#include "cheriot/riscv_cheriot_rvv_fp_encoding.h"
+#include "cheriot/riscv_cheriot_rvv_fp_enums.h"
+#include "mpact/sim/generic/instruction.h"
+#include "mpact/sim/generic/type_helpers.h"
+#include "mpact/sim/util/memory/memory_interface.h"
+#include "riscv//riscv_state.h"
+
+namespace mpact {
+namespace sim {
+namespace cheriot {
+
+using RV_EC = ::mpact::sim::riscv::ExceptionCode;
+
+using ::mpact::sim::generic::operator*; // NOLINT: is used below (clang error).
+
+CheriotRVVFPDecoder::CheriotRVVFPDecoder(CheriotState *state,
+ util::MemoryInterface *memory)
+ : state_(state), memory_(memory) {
+ // Need a data buffer to load instructions from memory. Allocate a single
+ // buffer that can be reused for each instruction word.
+ inst_db_ = db_factory_.Allocate<uint32_t>(1);
+ // Allocate the isa factory class, the top level isa decoder instance, and
+ // the encoding parser.
+ cheriot_rvv_fp_isa_factory_ = new CheriotRVVFPIsaFactory();
+ cheriot_rvv_fp_isa_ = new isa32_rvv_fp::RiscVCheriotRVVFpInstructionSet(
+ state, cheriot_rvv_fp_isa_factory_);
+ cheriot_rvv_fp_encoding_ = new isa32_rvv_fp::RiscVCheriotRVVFPEncoding(state);
+}
+
+CheriotRVVFPDecoder::~CheriotRVVFPDecoder() {
+ delete cheriot_rvv_fp_isa_;
+ delete cheriot_rvv_fp_isa_factory_;
+ delete cheriot_rvv_fp_encoding_;
+ inst_db_->DecRef();
+}
+
+generic::Instruction *CheriotRVVFPDecoder::DecodeInstruction(uint64_t address) {
+ // First check that the address is aligned properly. If not, create and return
+ // an instruction object that will raise an exception.
+ if (address & 0x1) {
+ auto *inst = new generic::Instruction(0, state_);
+ inst->set_size(1);
+ inst->SetDisassemblyString("Misaligned instruction address");
+ inst->set_opcode(*isa32_rvv_fp::OpcodeEnum::kNone);
+ inst->set_address(address);
+ inst->set_semantic_function([this](generic::Instruction *inst) {
+ state_->Trap(/*is_interrupt*/ false, inst->address(),
+ *RV_EC::kInstructionAddressMisaligned, inst->address() ^ 0x1,
+ inst);
+ });
+ return inst;
+ }
+
+ // If the address is greater than the max address, return an instruction
+ // that will raise an exception.
+ if (address > state_->max_physical_address()) {
+ auto *inst = new generic::Instruction(0, state_);
+ inst->set_size(0);
+ inst->SetDisassemblyString("Instruction access fault");
+ inst->set_opcode(*isa32_rvv_fp::OpcodeEnum::kNone);
+ inst->set_address(address);
+ inst->set_semantic_function([this](generic::Instruction *inst) {
+ state_->Trap(/*is_interrupt*/ false, inst->address(),
+ *RV_EC::kInstructionAccessFault, inst->address(), nullptr);
+ });
+ return inst;
+ }
+
+ // Read the instruction word from memory and parse it in the encoding parser.
+ memory_->Load(address, inst_db_, nullptr, nullptr);
+ uint32_t iword = inst_db_->Get<uint32_t>(0);
+ cheriot_rvv_fp_encoding_->ParseInstruction(iword);
+
+ // Call the isa decoder to obtain a new instruction object for the instruction
+ // word that was parsed above.
+ auto *instruction =
+ cheriot_rvv_fp_isa_->Decode(address, cheriot_rvv_fp_encoding_);
+ return instruction;
+}
+
+} // namespace cheriot
+} // namespace sim
+} // namespace mpact
diff --git a/cheriot/cheriot_rvv_fp_decoder.h b/cheriot/cheriot_rvv_fp_decoder.h
new file mode 100644
index 0000000..3693dea
--- /dev/null
+++ b/cheriot/cheriot_rvv_fp_decoder.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MPACT_CHERIOT_CHERIOT_RVV_FP_DECODER_H_
+#define MPACT_CHERIOT_CHERIOT_RVV_FP_DECODER_H_
+
+#include <cstdint>
+#include <memory>
+
+#include "cheriot/cheriot_state.h"
+#include "cheriot/riscv_cheriot_rvv_fp_decoder.h"
+#include "cheriot/riscv_cheriot_rvv_fp_encoding.h"
+#include "cheriot/riscv_cheriot_rvv_fp_enums.h"
+#include "mpact/sim/generic/arch_state.h"
+#include "mpact/sim/generic/data_buffer.h"
+#include "mpact/sim/generic/decoder_interface.h"
+#include "mpact/sim/generic/instruction.h"
+#include "mpact/sim/util/memory/memory_interface.h"
+
+namespace mpact {
+namespace sim {
+namespace cheriot {
+
+using ::mpact::sim::generic::ArchState;
+
+// This is the factory class needed by the generated decoder. It is responsible
+// for creating the decoder for each slot instance. Since the riscv architecture
+// only has a single slot, it's a pretty simple class.
+class CheriotRVVFPIsaFactory
+ : public isa32_rvv_fp::RiscVCheriotRVVFpInstructionSetFactory {
+ public:
+ std::unique_ptr<isa32_rvv_fp::RiscvCheriotRvvFpSlot>
+ CreateRiscvCheriotRvvFpSlot(ArchState *state) override {
+ return std::make_unique<isa32_rvv_fp::RiscvCheriotRvvFpSlot>(state);
+ }
+};
+
+// This class implements the generic DecoderInterface and provides a bridge
+// to the (isa specific) generated decoder classes.
+class CheriotRVVFPDecoder : public generic::DecoderInterface {
+ public:
+ using SlotEnum = isa32_rvv_fp::SlotEnum;
+ using OpcodeEnum = isa32_rvv_fp::OpcodeEnum;
+
+ CheriotRVVFPDecoder(CheriotState *state, util::MemoryInterface *memory);
+ CheriotRVVFPDecoder() = delete;
+ ~CheriotRVVFPDecoder() override;
+
+ // This will always return a valid instruction that can be executed. In the
+ // case of a decode error, the semantic function in the instruction object
+ // instance will raise an internal simulator error when executed.
+ generic::Instruction *DecodeInstruction(uint64_t address) override;
+
+ // Return the number of opcodes supported by this decoder.
+ int GetNumOpcodes() const override {
+ return static_cast<int>(OpcodeEnum::kPastMaxValue);
+ }
+ // Return the name of the opcode at the given index.
+ const char *GetOpcodeName(int index) const override {
+ return isa32_rvv_fp::kOpcodeNames[index];
+ }
+
+ // Getter.
+ isa32_rvv_fp::RiscVCheriotRVVFPEncoding *cheriot_rvv_fp_encoding() const {
+ return cheriot_rvv_fp_encoding_;
+ }
+
+ private:
+ CheriotState *state_;
+ util::MemoryInterface *memory_;
+ generic::DataBufferFactory db_factory_;
+ generic::DataBuffer *inst_db_;
+ isa32_rvv_fp::RiscVCheriotRVVFPEncoding *cheriot_rvv_fp_encoding_;
+ isa32_rvv_fp::RiscVCheriotRVVFpInstructionSetFactory
+ *cheriot_rvv_fp_isa_factory_;
+ isa32_rvv_fp::RiscVCheriotRVVFpInstructionSet *cheriot_rvv_fp_isa_;
+};
+
+} // namespace cheriot
+} // namespace sim
+} // namespace mpact
+
+#endif // MPACT_CHERIOT_CHERIOT_RVV_FP_DECODER_H_
diff --git a/cheriot/cheriot_rvv_fp_getters.h b/cheriot/cheriot_rvv_fp_getters.h
new file mode 100644
index 0000000..c3eee6b
--- /dev/null
+++ b/cheriot/cheriot_rvv_fp_getters.h
@@ -0,0 +1,66 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef MPACT_CHERIOT_CHERIOT_RVV_FP_GETTERS_H_
+#define MPACT_CHERIOT_CHERIOT_RVV_FP_GETTERS_H_
+
+#include "absl/container/flat_hash_map.h"
+#include "absl/functional/any_invocable.h"
+#include "absl/strings/str_cat.h"
+#include "cheriot/cheriot_getter_helpers.h"
+#include "cheriot/riscv_cheriot_encoding_common.h"
+#include "mpact/sim/generic/operand_interface.h"
+#include "riscv//riscv_register.h"
+#include "riscv//riscv_state.h"
+
+namespace mpact {
+namespace sim {
+namespace cheriot {
+using ::mpact::sim::cheriot::RiscVCheriotEncodingCommon;
+using ::mpact::sim::generic::DestinationOperandInterface;
+using ::mpact::sim::generic::SourceOperandInterface;
+using ::mpact::sim::riscv::RiscVState;
+using ::mpact::sim::riscv::RVFpRegister;
+
+using SourceOpGetterMap =
+ absl::flat_hash_map<int, absl::AnyInvocable<SourceOperandInterface *()>>;
+using DestOpGetterMap =
+ absl::flat_hash_map<int,
+ absl::AnyInvocable<DestinationOperandInterface *(int)>>;
+
+template <typename Enum, typename Extractors>
+void AddCheriotRVVFPSourceGetters(SourceOpGetterMap &getter_map,
+ RiscVCheriotEncodingCommon *common) {
+ Insert(getter_map, *Enum::kFs1, [common]() {
+ int num = Extractors::VArith::ExtractRs1(common->inst_word());
+ return GetRegisterSourceOp<RVFpRegister>(
+ common->state(), absl::StrCat(RiscVState::kFregPrefix, num));
+ });
+}
+
+template <typename Enum, typename Extractors>
+void AddCheriotRVVFPDestGetters(DestOpGetterMap &getter_map,
+ RiscVCheriotEncodingCommon *common) {
+ Insert(getter_map, *Enum::kFd, [common](int latency) {
+ int num = Extractors::VArith::ExtractRd(common->inst_word());
+ return GetRegisterDestinationOp<RVFpRegister>(
+ common->state(), absl::StrCat(RiscVState::kFregPrefix, num), latency);
+ });
+}
+
+} // namespace cheriot
+} // namespace sim
+} // namespace mpact
+
+#endif // MPACT_CHERIOT_CHERIOT_RVV_FP_GETTERS_H_
diff --git a/cheriot/cheriot_rvv_getters.h b/cheriot/cheriot_rvv_getters.h
new file mode 100644
index 0000000..303a2cb
--- /dev/null
+++ b/cheriot/cheriot_rvv_getters.h
@@ -0,0 +1,124 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef MPACT_CHERIOT_CHERIOT_RVV_GETTERS_H_
+#define MPACT_CHERIOT_CHERIOT_RVV_GETTERS_H_
+
+#include <cstdint>
+
+#include "absl/container/flat_hash_map.h"
+#include "absl/functional/any_invocable.h"
+#include "cheriot/cheriot_getter_helpers.h"
+#include "cheriot/cheriot_state.h"
+#include "cheriot/cheriot_vector_true_operand.h"
+#include "cheriot/riscv_cheriot_encoding_common.h"
+#include "mpact/sim/generic/immediate_operand.h"
+#include "mpact/sim/generic/literal_operand.h"
+#include "mpact/sim/generic/operand_interface.h"
+#include "riscv//riscv_register.h"
+
+namespace mpact {
+namespace sim {
+namespace cheriot {
+
+using ::mpact::sim::cheriot::RiscVCheriotEncodingCommon;
+using ::mpact::sim::generic::DestinationOperandInterface;
+using ::mpact::sim::generic::ImmediateOperand;
+using ::mpact::sim::generic::IntLiteralOperand;
+using ::mpact::sim::generic::SourceOperandInterface;
+using ::mpact::sim::riscv::RV32VectorTrueOperand;
+using ::mpact::sim::riscv::RVVectorRegister;
+
+using SourceOpGetterMap =
+ absl::flat_hash_map<int, absl::AnyInvocable<SourceOperandInterface *()>>;
+using DestOpGetterMap =
+ absl::flat_hash_map<int,
+ absl::AnyInvocable<DestinationOperandInterface *(int)>>;
+
+template <typename Enum, typename Extractors>
+void AddCheriotRVVSourceGetters(SourceOpGetterMap &getter_map,
+ RiscVCheriotEncodingCommon *common) {
+ Insert(getter_map, *Enum::kConst1, [common]() -> SourceOperandInterface * {
+ return new IntLiteralOperand<1>();
+ });
+ Insert(getter_map, *Enum::kNf, [common]() -> SourceOperandInterface * {
+ auto imm = Extractors::VMem::ExtractNf(common->inst_word());
+ return new ImmediateOperand<uint32_t>(imm);
+ });
+ Insert(getter_map, *Enum::kSimm5, [common]() -> SourceOperandInterface * {
+ auto imm = Extractors::VArith::ExtractSimm5(common->inst_word());
+ return new ImmediateOperand<uint32_t>(imm);
+ });
+ Insert(getter_map, *Enum::kUimm5, [common]() -> SourceOperandInterface * {
+ auto imm = Extractors::VArith::ExtractUimm5(common->inst_word());
+ return new ImmediateOperand<int32_t>(imm);
+ });
+ Insert(getter_map, *Enum::kVd, [common]() -> SourceOperandInterface * {
+ auto num = Extractors::VArith::ExtractVd(common->inst_word());
+ return GetVectorRegisterSourceOp<RVVectorRegister>(common->state(), num);
+ });
+ Insert(getter_map, *Enum::kVm, [common]() -> SourceOperandInterface * {
+ auto vm = Extractors::VArith::ExtractVm(common->inst_word());
+ return new ImmediateOperand<uint32_t>(vm);
+ });
+ Insert(getter_map, *Enum::kVmask, [common]() -> SourceOperandInterface * {
+ auto vm = Extractors::VArith::ExtractVm(common->inst_word());
+ if (vm == 1) {
+ // Unmasked, return the True mask.
+ return new CheriotVectorTrueOperand(common->state());
+ }
+ // Masked. Return the mask register.
+ return GetVectorMaskRegisterSourceOp<RVVectorRegister>(common->state(), 0);
+ });
+ Insert(getter_map, *Enum::kVmaskTrue, [common]() -> SourceOperandInterface * {
+ return new CheriotVectorTrueOperand(common->state());
+ });
+ Insert(getter_map, *Enum::kVs1, [common]() -> SourceOperandInterface * {
+ auto num = Extractors::VArith::ExtractVs1(common->inst_word());
+ return GetVectorRegisterSourceOp<RVVectorRegister>(common->state(), num);
+ });
+ Insert(getter_map, *Enum::kVs2, [common]() -> SourceOperandInterface * {
+ auto num = Extractors::VArith::ExtractVs2(common->inst_word());
+ return GetVectorRegisterSourceOp<RVVectorRegister>(common->state(), num);
+ });
+ Insert(getter_map, *Enum::kVs3, [common]() -> SourceOperandInterface * {
+ auto num = Extractors::VMem::ExtractVs3(common->inst_word());
+ return GetVectorRegisterSourceOp<RVVectorRegister>(common->state(), num);
+ });
+ Insert(getter_map, *Enum::kZimm10, [common]() -> SourceOperandInterface * {
+ auto imm = Extractors::VConfig::ExtractZimm10(common->inst_word());
+ return new ImmediateOperand<uint32_t>(imm);
+ });
+ Insert(getter_map, *Enum::kZimm11, [common]() -> SourceOperandInterface * {
+ auto imm = Extractors::VConfig::ExtractZimm11(common->inst_word());
+ return new ImmediateOperand<uint32_t>(imm);
+ });
+}
+
+template <typename Enum, typename Extractors>
+void AddCheriotRVVDestGetters(DestOpGetterMap &getter_map,
+ RiscVCheriotEncodingCommon *common) {
+ Insert(getter_map, Enum::kVd,
+ [common](int latency) -> DestinationOperandInterface * {
+ auto num = Extractors::VArith::ExtractVd(common->inst_word());
+ return GetVectorRegisterDestinationOp<RVVectorRegister>(
+ common->state(), latency, num);
+ });
+}
+
+} // namespace cheriot
+} // namespace sim
+} // namespace mpact
+
+#endif // MPACT_CHERIOT_CHERIOT_RVV_GETTERS_H_
diff --git a/cheriot/cheriot_state.cc b/cheriot/cheriot_state.cc
index 1842f29..882e1cf 100644
--- a/cheriot/cheriot_state.cc
+++ b/cheriot/cheriot_state.cc
@@ -442,15 +442,8 @@
DataBuffer *mask_db, int el_size, DataBuffer *db,
Instruction *child_inst,
ReferenceCount *context) {
- // Check for alignment.
- uint64_t mask = el_size - 1;
- for (auto address : address_db->Get<uint64_t>()) {
- if ((address & mask) != 0) {
- Trap(/*is_interrupt*/ false, address, *EC::kLoadAddressMisaligned,
- inst == nullptr ? 0 : inst->address(), inst);
- return;
- }
- }
+ // For now, we don't check for alignment on vector memory accesses.
+
// Check for physical address violation.
for (auto address : address_db->Get<uint64_t>()) {
if (address < min_physical_address_ || address > max_physical_address_) {
@@ -496,15 +489,8 @@
void CheriotState::StoreMemory(const Instruction *inst, DataBuffer *address_db,
DataBuffer *mask_db, int el_size,
DataBuffer *db) {
- // Check for alignment.
- uint64_t mask = el_size - 1;
- for (auto address : address_db->Get<uint64_t>()) {
- if ((address & mask) != 0) {
- Trap(/*is_interrupt*/ false, address, *EC::kStoreAddressMisaligned,
- inst == nullptr ? 0 : inst->address(), inst);
- return;
- }
- }
+ // Ignore alignment check for vector memory accesses.
+
// Check for physical address violation.
for (auto address : address_db->Get<uint64_t>()) {
if (address < min_physical_address_ || address > max_physical_address_) {
@@ -525,6 +511,10 @@
tagged_memory_->Store(address_db, mask_db, el_size, db);
}
+void CheriotState::DbgStoreMemory(uint64_t address, DataBuffer *db) {
+ tagged_memory_->Store(address, db);
+}
+
void CheriotState::DbgLoadMemory(uint64_t address, DataBuffer *db) {
tagged_memory_->Load(address, db, nullptr, nullptr);
}
diff --git a/cheriot/cheriot_state.h b/cheriot/cheriot_state.h
index c14c73e..6d0ec7e 100644
--- a/cheriot/cheriot_state.h
+++ b/cheriot/cheriot_state.h
@@ -38,8 +38,11 @@
#include "mpact/sim/util/memory/memory_interface.h"
#include "mpact/sim/util/memory/tagged_memory_interface.h"
#include "riscv//riscv_csr.h"
+#include "riscv//riscv_fp_state.h"
#include "riscv//riscv_misa.h"
+#include "riscv//riscv_register.h"
#include "riscv//riscv_state.h"
+#include "riscv//riscv_vector_state.h"
#include "riscv//riscv_xip_xie.h"
#include "riscv//riscv_xstatus.h"
@@ -61,14 +64,17 @@
using ::mpact::sim::riscv::PrivilegeMode;
using ::mpact::sim::riscv::RiscVCsrInterface;
using ::mpact::sim::riscv::RiscVCsrSet;
+using ::mpact::sim::riscv::RiscVFPState;
using ::mpact::sim::riscv::RiscVMIe;
using ::mpact::sim::riscv::RiscVMIp;
using ::mpact::sim::riscv::RiscVMIsa;
using ::mpact::sim::riscv::RiscVMStatus;
using ::mpact::sim::riscv::RiscVSimpleCsr;
+using ::mpact::sim::riscv::RVVectorRegister;
// Forward declare the CHERIoT register type.
class CheriotRegister;
+class CheriotVectorState;
// CHERIoT exception codes. These are used in addition to the ones defined for
// vanilla RiscV.
@@ -159,6 +165,9 @@
std::vector<RiscVCsrInterface *> &);
friend void CreateCsrs<uint64_t>(CheriotState *,
std::vector<RiscVCsrInterface *> &);
+ auto static constexpr kVregPrefix =
+ ::mpact::sim::riscv::RiscVState::kVregPrefix;
+
// Memory footprint of a capability register.
static constexpr int kCapabilitySizeInBytes = 8;
// Pc name.
@@ -191,6 +200,21 @@
return std::make_pair(AddRegister<RegisterType>(name), true);
}
+ // Specialization for RiscV vector registers.
+ template <>
+ std::pair<RVVectorRegister *, bool> GetRegister<RVVectorRegister>(
+ absl::string_view name) {
+ int vector_byte_width = vector_register_width();
+ if (vector_byte_width == 0) return std::make_pair(nullptr, false);
+ auto ptr = registers()->find(std::string(name));
+ if (ptr != registers()->end())
+ return std::make_pair(static_cast<RVVectorRegister *>(ptr->second),
+ false);
+ // Create a new register and return a pointer to the object.
+ return std::make_pair(
+ AddRegister<RVVectorRegister>(name, vector_byte_width), true);
+ }
+
// Add register alias.
template <typename RegisterType>
absl::Status AddRegisterAlias(absl::string_view current_name,
@@ -228,6 +252,7 @@
// Debug memory methods.
void DbgLoadMemory(uint64_t address, DataBuffer *db);
+ void DbgStoreMemory(uint64_t address, DataBuffer *db);
// Called by the fence instruction semantic function to signal a fence
// operation.
void Fence(const Instruction *inst, int fm, int predecessor, int successor);
@@ -341,6 +366,12 @@
on_trap_ = std::move(callback);
}
+ RiscVFPState *rv_fp() { return rv_fp_; }
+ void set_rv_fp(RiscVFPState *rv_fp) { rv_fp_ = rv_fp; }
+ CheriotVectorState *rv_vector() { return rv_vector_; }
+ void set_rv_vector(CheriotVectorState *rv_vector) { rv_vector_ = rv_vector; }
+ void set_vector_register_width(int value) { vector_register_width_ = value; }
+ int vector_register_width() const { return vector_register_width_; }
RiscVMStatus *mstatus() { return mstatus_; }
RiscVMIsa *misa() { return misa_; }
RiscVMIp *mip() { return mip_; }
@@ -382,6 +413,7 @@
CheriotRegister *pcc_ = nullptr;
CheriotRegister *cgp_ = nullptr;
bool branch_ = false;
+ int vector_register_width_ = 0;
uint64_t max_physical_address_;
uint64_t min_physical_address_ = 0;
int num_tags_per_load_;
@@ -396,6 +428,8 @@
absl::AnyInvocable<bool(const Instruction *)> on_wfi_;
absl::AnyInvocable<bool(const Instruction *)> on_cease_;
std::vector<RiscVCsrInterface *> csr_vec_;
+ RiscVFPState *rv_fp_ = nullptr;
+ CheriotVectorState *rv_vector_ = nullptr;
// For interrupt handling.
bool is_interrupt_available_ = false;
int interrupt_handler_depth_ = 0;
diff --git a/cheriot/cheriot_test_rig_decoder.cc b/cheriot/cheriot_test_rig_decoder.cc
index c1ff08f..3a77023 100644
--- a/cheriot/cheriot_test_rig_decoder.cc
+++ b/cheriot/cheriot_test_rig_decoder.cc
@@ -17,9 +17,6 @@
#include <cstdint>
#include <string>
-#include "absl/log/log.h"
-#include "absl/strings/str_cat.h"
-#include "absl/strings/str_format.h"
#include "cheriot/cheriot_state.h"
#include "cheriot/riscv_cheriot_bin_decoder.h"
#include "cheriot/riscv_cheriot_decoder.h"
@@ -171,9 +168,9 @@
rs2 = 0;
break;
case FormatEnum::kCI: // 2 reg operands: rd, rs1.
- // cnop, caddi, cli, caddi16sp, clui, cslli, clwsp, cldsp.
+ // cnop, caddi, cli, caddi16sp, clui, cslli, clwsp, clcsp.
rd = encoding::c_i::ExtractRd(inst_word16);
- if ((opcode == OpcodeEnum::kClwsp) || (opcode == OpcodeEnum::kCldsp)) {
+ if ((opcode == OpcodeEnum::kClwsp) || (opcode == OpcodeEnum::kClcsp)) {
rs1 = 2;
} else {
rs1 = encoding::c_i::ExtractRs1(inst_word16);
diff --git a/cheriot/cheriot_top.cc b/cheriot/cheriot_top.cc
index c357f0e..243d112 100644
--- a/cheriot/cheriot_top.cc
+++ b/cheriot/cheriot_top.cc
@@ -31,10 +31,8 @@
#include "absl/strings/str_format.h"
#include "absl/synchronization/notification.h"
#include "cheriot/cheriot_debug_interface.h"
-#include "cheriot/cheriot_decoder.h"
#include "cheriot/cheriot_register.h"
#include "cheriot/cheriot_state.h"
-#include "cheriot/riscv_cheriot_enums.h"
#include "cheriot/riscv_cheriot_register_aliases.h"
#include "mpact/sim/generic/action_point_manager_base.h"
#include "mpact/sim/generic/breakpoint_manager.h"
@@ -59,8 +57,6 @@
namespace sim {
namespace cheriot {
-constexpr char kCheriotName[] = "CherIoT";
-
using ::mpact::sim::generic::ActionPointManagerBase;
using ::mpact::sim::generic::BreakpointManager;
using ::mpact::sim::riscv::RiscVActionPointMemoryInterface;
@@ -68,7 +64,7 @@
using PB = ::mpact::sim::cheriot::CheriotRegister::PermissionBits;
CheriotTop::CheriotTop(std::string name, CheriotState *state,
- CheriotDecoder *decoder)
+ DecoderInterface *decoder)
: Component(name),
state_(state),
cheriot_decoder_(decoder),
diff --git a/cheriot/cheriot_top.h b/cheriot/cheriot_top.h
index 6c0f5dd..660d188 100644
--- a/cheriot/cheriot_top.h
+++ b/cheriot/cheriot_top.h
@@ -28,7 +28,6 @@
#include "absl/status/statusor.h"
#include "absl/synchronization/notification.h"
#include "cheriot/cheriot_debug_interface.h"
-#include "cheriot/cheriot_decoder.h"
#include "cheriot/cheriot_register.h"
#include "cheriot/cheriot_state.h"
#include "mpact/sim/generic/action_point_manager_base.h"
@@ -51,6 +50,7 @@
using ::mpact::sim::generic::ActionPointManagerBase;
using ::mpact::sim::generic::BreakpointManager;
+using ::mpact::sim::generic::DecoderInterface;
using ::mpact::sim::riscv::RiscVActionPointMemoryInterface;
struct BranchTraceEntry {
@@ -68,7 +68,7 @@
using RunStatus = generic::CoreDebugInterface::RunStatus;
using HaltReason = generic::CoreDebugInterface::HaltReason;
- CheriotTop(std::string name, CheriotState *state, CheriotDecoder *decoder);
+ CheriotTop(std::string name, CheriotState *state, DecoderInterface *decoder);
~CheriotTop() override;
// Methods inherited from CoreDebugInterface.
diff --git a/cheriot/cheriot_vector_state.cc b/cheriot/cheriot_vector_state.cc
new file mode 100644
index 0000000..f5fc5a4
--- /dev/null
+++ b/cheriot/cheriot_vector_state.cc
@@ -0,0 +1,193 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cheriot/cheriot_vector_state.h"
+
+#include <cstdint>
+
+#include "absl/log/log.h"
+#include "cheriot/cheriot_state.h"
+#include "riscv//riscv_csr.h"
+
+namespace mpact {
+namespace sim {
+namespace cheriot {
+namespace {
+
+constexpr char kVlName[] = "vl";
+constexpr uint32_t kVlReadMask = 0xffff'ffff;
+constexpr uint32_t kVlWriteMask = 0;
+constexpr uint32_t kVlInitial = 0;
+
+constexpr char kVtypeName[] = "vtype";
+constexpr uint32_t kVtypeReadMask = 0xffff'ffff;
+constexpr uint32_t kVtypeWriteMask = 0;
+constexpr uint32_t kVtypeInitial = 0;
+
+constexpr char kVlenbName[] = "vlenb";
+constexpr uint32_t kVlenbReadMask = 0xffff'ffff;
+constexpr uint32_t kVlenbWriteMask = 0;
+
+constexpr char kVstartName[] = "vstart";
+constexpr uint32_t kVstartReadMask = 0xffff'ffff;
+constexpr uint32_t kVstartWriteMask = 0;
+constexpr uint32_t kVstartInitial = 0;
+
+constexpr char kVxsatName[] = "vxsat";
+constexpr uint32_t kVxsatReadMask = 1;
+constexpr uint32_t kVxsatWriteMask = 1;
+constexpr uint32_t kVxsatInitial = 0;
+
+constexpr char kVxrmName[] = "vxrm";
+constexpr uint32_t kVxrmReadMask = 3;
+constexpr uint32_t kVxrmWriteMask = 3;
+constexpr uint32_t kVxrmInitial = 0;
+
+constexpr char kVcsrName[] = "vcsr";
+constexpr uint32_t kVcsrReadMask = 7;
+constexpr uint32_t kVcsrWriteMask = 7;
+constexpr uint32_t kVcsrInitial = 0;
+
+// Helper function to avoid some extra code below.
+static inline void LogIfError(absl::Status status) {
+ if (status.ok()) return;
+ LOG(ERROR) << status.message();
+}
+
+} // namespace
+
+using ::mpact::sim::riscv::RiscVCsrEnum;
+using ::mpact::sim::riscv::RiscVSimpleCsr;
+
+CheriotVl::CheriotVl(CheriotVectorState* vector_state)
+ : RiscVSimpleCsr<uint32_t>(kVlName, RiscVCsrEnum::kVl, kVlInitial,
+ kVlReadMask, kVlWriteMask,
+ vector_state->state()),
+ vector_state_(vector_state) {}
+
+uint32_t CheriotVl::AsUint32() { return vector_state_->vector_length(); }
+
+CheriotVtype::CheriotVtype(CheriotVectorState* vector_state)
+ : RiscVSimpleCsr<uint32_t>(kVtypeName, RiscVCsrEnum::kVtype, kVtypeInitial,
+ kVtypeReadMask, kVtypeWriteMask,
+ vector_state->state()),
+ vector_state_(vector_state) {}
+
+uint32_t CheriotVtype::AsUint32() { return vector_state_->vtype(); }
+
+CheriotVstart::CheriotVstart(CheriotVectorState* vector_state)
+ : RiscVSimpleCsr<uint32_t>(kVstartName, RiscVCsrEnum::kVstart,
+ kVstartInitial, kVstartReadMask,
+ kVstartWriteMask, vector_state->state()),
+ vector_state_(vector_state) {}
+
+uint32_t CheriotVstart::AsUint32() { return vector_state_->vstart(); }
+
+void CheriotVstart::Write(uint32_t value) { vector_state_->set_vstart(value); }
+
+CheriotVxsat::CheriotVxsat(CheriotVectorState* vector_state)
+ : RiscVSimpleCsr<uint32_t>(kVxsatName, RiscVCsrEnum::kVxsat, kVxsatInitial,
+ kVxsatReadMask, kVxsatWriteMask,
+ vector_state->state()),
+ vector_state_(vector_state) {}
+
+uint32_t CheriotVxsat::AsUint32() { return vector_state_->vxsat() ? 1 : 0; }
+
+void CheriotVxsat::Write(uint32_t value) {
+ vector_state_->set_vxsat(value & 1);
+}
+
+CheriotVxrm::CheriotVxrm(CheriotVectorState* vector_state)
+ : RiscVSimpleCsr<uint32_t>(kVxrmName, RiscVCsrEnum::kVxrm, kVxrmInitial,
+ kVxrmReadMask, kVxrmWriteMask,
+ vector_state->state()),
+ vector_state_(vector_state) {}
+
+uint32_t CheriotVxrm::AsUint32() { return vector_state_->vxrm(); }
+
+void CheriotVxrm::Write(uint32_t value) {
+ vector_state_->set_vxrm(value & kVxrmWriteMask);
+}
+
+CheriotVcsr::CheriotVcsr(CheriotVectorState* vector_state)
+ : RiscVSimpleCsr<uint32_t>(kVcsrName, RiscVCsrEnum::kVcsr, kVcsrInitial,
+ kVcsrReadMask, kVcsrWriteMask,
+ vector_state->state()),
+ vector_state_(vector_state) {}
+
+uint32_t CheriotVcsr::AsUint32() {
+ const uint32_t vxrm_shifted = (vector_state_->vxrm() & kVxrmWriteMask) << 1;
+ const uint32_t vxsat = vector_state_->vxsat() ? 1 : 0;
+ return vxrm_shifted | vxsat;
+}
+
+void CheriotVcsr::Write(uint32_t value) {
+ const uint32_t vxrm = (value >> 1) & kVxrmWriteMask;
+ const uint32_t vxsat = value & 1;
+ vector_state_->set_vxrm(vxrm);
+ vector_state_->set_vxsat(vxsat);
+}
+
+// Constructor for the vector class. Need to pass in the parent RV32 state and
+// the vector length in bytes.
+CheriotVectorState::CheriotVectorState(CheriotState* state, int byte_length)
+ : vector_register_byte_length_(byte_length),
+ vl_csr_(this),
+ vtype_csr_(this),
+ vlenb_csr_(kVlenbName, RiscVCsrEnum::kVlenb, vector_register_byte_length_,
+ kVlenbReadMask, kVlenbWriteMask, state),
+ vstart_csr_(this),
+ vxsat_csr_(this),
+ vxrm_csr_(this),
+ vcsr_csr_(this) {
+ state_ = state;
+ state->set_rv_vector(this);
+ state->set_vector_register_width(byte_length);
+
+ LogIfError(state->csr_set()->AddCsr(&vl_csr_));
+ LogIfError(state->csr_set()->AddCsr(&vtype_csr_));
+ LogIfError(state->csr_set()->AddCsr(&vlenb_csr_));
+ LogIfError(state->csr_set()->AddCsr(&vstart_csr_));
+ LogIfError(state->csr_set()->AddCsr(&vxsat_csr_));
+ LogIfError(state->csr_set()->AddCsr(&vxrm_csr_));
+ LogIfError(state->csr_set()->AddCsr(&vcsr_csr_));
+}
+
+// This function parses the vector type, as used in the vset* instructions
+// and sets the internal vector state accordingly.
+void CheriotVectorState::SetVectorType(uint32_t vtype) {
+ static const int lmul8_values[8] = {8, 16, 32, 64, 0, 1, 2, 4};
+ static const int sew_values[8] = {8, 16, 32, 64, 0, 0, 0, 0};
+ set_vtype(vtype);
+ // The vtype field is divided into the following fields:
+ // [2..0]: vector length multiplier.
+ // [5..3]: element width specifier.
+ // [6]: vector tail agnostic bit.
+ // [7]: vector mask agnostic bit.
+ // Extract the lmul.
+ set_vector_length_multiplier(lmul8_values[(vtype & 0b111)]);
+ // Extract the sew and convert from bits to bytes.
+ set_selected_element_width(sew_values[(vtype >> 3) & 0b111] >> 3);
+ // Extract the tail and mask agnostic flags.
+ set_vector_tail_agnostic(static_cast<bool>((vtype >> 6) & 0b1));
+ set_vector_mask_agnostic(static_cast<bool>((vtype >> 7) & 0b1));
+ // Compute the new max vector length.
+ max_vector_length_ = vector_register_byte_length() *
+ vector_length_multiplier() /
+ (8 * selected_element_width());
+}
+
+} // namespace cheriot
+} // namespace sim
+} // namespace mpact
diff --git a/cheriot/cheriot_vector_state.h b/cheriot/cheriot_vector_state.h
new file mode 100644
index 0000000..a2898ea
--- /dev/null
+++ b/cheriot/cheriot_vector_state.h
@@ -0,0 +1,198 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef MPACT_CHERIOT_CHERIOT_RISCV_VECTOR_STATE_H_
+#define MPACT_CHERIOT_CHERIOT_RISCV_VECTOR_STATE_H_
+
+#include <cstdint>
+
+#include "riscv//riscv_csr.h"
+
+// This file contains the definition of the vector state class. This class
+// is used by the vector instructions to obtain information about the state
+// and configuration of the vector unit. This class is also used to provide
+// values that are read from CSRs, and updated by by values written to CSRs.
+
+namespace mpact {
+namespace sim {
+namespace cheriot {
+
+using ::mpact::sim::riscv::RiscVSimpleCsr;
+
+class CheriotState;
+class CheriotVectorState;
+
+// Implementation of the 'vl' CSR.
+class CheriotVl : public RiscVSimpleCsr<uint32_t> {
+ public:
+ explicit CheriotVl(CheriotVectorState* vector_state);
+
+ // Overrides. Note that this CSR is read-only.
+ uint32_t AsUint32() override;
+ uint64_t AsUint64() override { return static_cast<uint64_t>(AsUint32()); }
+
+ private:
+ const CheriotVectorState* const vector_state_;
+};
+
+// Implementation of the 'vtype' CSR.
+class CheriotVtype : public RiscVSimpleCsr<uint32_t> {
+ public:
+ explicit CheriotVtype(CheriotVectorState* vector_state);
+
+ // Overrides. Note that this CSR is read-only.
+ uint32_t AsUint32() override;
+ uint64_t AsUint64() override { return static_cast<uint64_t>(AsUint32()); }
+
+ private:
+ const CheriotVectorState* const vector_state_;
+};
+
+// Implementation of the 'vstart' CSR.
+class CheriotVstart : public RiscVSimpleCsr<uint32_t> {
+ public:
+ explicit CheriotVstart(CheriotVectorState* vector_state);
+
+ // Overrides.
+ uint32_t AsUint32() override;
+ uint64_t AsUint64() override { return static_cast<uint64_t>(AsUint32()); }
+ void Write(uint32_t value) override;
+ void Write(uint64_t value) override { Write(static_cast<uint32_t>(value)); }
+
+ private:
+ CheriotVectorState* const vector_state_;
+};
+
+// Implementation of the 'vxsat' CSR.
+class CheriotVxsat : public RiscVSimpleCsr<uint32_t> {
+ public:
+ explicit CheriotVxsat(CheriotVectorState* vector_state);
+
+ // Overrides.
+ uint32_t AsUint32() override;
+ uint64_t AsUint64() override { return static_cast<uint64_t>(AsUint32()); }
+ void Write(uint32_t value) override;
+ void Write(uint64_t value) override { Write(static_cast<uint32_t>(value)); }
+
+ private:
+ CheriotVectorState* const vector_state_;
+};
+
+// Implementation of the 'vxrm' CSR.
+class CheriotVxrm : public RiscVSimpleCsr<uint32_t> {
+ public:
+ explicit CheriotVxrm(CheriotVectorState* vector_state);
+
+ // Overrides.
+ uint32_t AsUint32() override;
+ uint64_t AsUint64() override { return static_cast<uint64_t>(AsUint32()); }
+ void Write(uint32_t value) override;
+ void Write(uint64_t value) override { Write(static_cast<uint32_t>(value)); }
+
+ private:
+ CheriotVectorState* const vector_state_;
+};
+
+// Implementation of the 'vcsr' CSR. This CSR mirrors the bits in 'vxsat' and
+// 'vxrm' as follows:
+//
+// bits 2:1 - vxrm
+// bits 0:0 - vxsat
+class CheriotVcsr : public RiscVSimpleCsr<uint32_t> {
+ public:
+ explicit CheriotVcsr(CheriotVectorState* vector_state);
+
+ // Overrides.
+ uint32_t AsUint32() override;
+ uint64_t AsUint64() override { return static_cast<uint64_t>(AsUint32()); }
+ void Write(uint32_t value) override;
+ void Write(uint64_t value) override { Write(static_cast<uint32_t>(value)); }
+
+ private:
+ CheriotVectorState* const vector_state_;
+};
+
+class CheriotVectorState {
+ public:
+ CheriotVectorState(CheriotState* state, int byte_length);
+
+ void SetVectorType(uint32_t vtype);
+
+ // Public getters and setters.
+ int vstart() const { return vstart_; }
+ void clear_vstart() { vstart_ = 0; }
+ void set_vstart(int value) { vstart_ = value; }
+ int vector_length() const { return vector_length_; }
+ void set_vector_length(int value) { vector_length_ = value; }
+ bool vector_tail_agnostic() const { return vector_tail_agnostic_; }
+ bool vector_mask_agnostic() const { return vector_mask_agnostic_; }
+ int vector_length_multiplier() const { return vector_length_multiplier_; }
+ int selected_element_width() const { return selected_element_width_; }
+ bool vector_exception() const { return vector_exception_; }
+ void clear_vector_exception() { vector_exception_ = false; }
+ void set_vector_exception() { vector_exception_ = true; }
+ uint32_t vtype() const { return vtype_; }
+ void set_vtype(uint32_t value) { vtype_ = value; }
+ int vector_register_byte_length() const {
+ return vector_register_byte_length_;
+ }
+ int max_vector_length() const { return max_vector_length_; }
+ bool vxsat() const { return vxsat_; }
+ void set_vxsat(bool value) { vxsat_ = value; }
+ int vxrm() const { return vxrm_; }
+ void set_vxrm(int value) { vxrm_ = value & 0x3; }
+
+ const CheriotState* state() const { return state_; }
+ CheriotState* state() { return state_; }
+
+ private:
+ // Vector length multiplier is scaled by 8, to provide integer representation
+ // of values from 1/8, 1/4, 1/2, 1, 2, 4, 8, as 1, 2, 4, 8, 16, 32, 64.
+ void set_vector_length_multiplier(int value) {
+ vector_length_multiplier_ = value;
+ }
+ void set_selected_element_width(int value) {
+ selected_element_width_ = value;
+ }
+ void set_vector_tail_agnostic(bool value) { vector_tail_agnostic_ = value; }
+ void set_vector_mask_agnostic(bool value) { vector_mask_agnostic_ = value; }
+
+ CheriotState* state_ = nullptr;
+ uint32_t vtype_;
+ bool vector_exception_ = false;
+ int vector_register_byte_length_ = 0;
+ int vstart_ = 0;
+ int max_vector_length_ = 0;
+ int vector_length_ = 0;
+ int vector_length_multiplier_ = 8;
+ // Selected element width (SEW) in bytes.
+ int selected_element_width_ = 1;
+ bool vector_tail_agnostic_ = false;
+ bool vector_mask_agnostic_ = false;
+ bool vxsat_ = false;
+ int vxrm_ = 0;
+
+ CheriotVl vl_csr_;
+ CheriotVtype vtype_csr_;
+ RiscVSimpleCsr<uint32_t> vlenb_csr_;
+ CheriotVstart vstart_csr_;
+ CheriotVxsat vxsat_csr_;
+ CheriotVxrm vxrm_csr_;
+ CheriotVcsr vcsr_csr_;
+};
+
+} // namespace cheriot
+} // namespace sim
+} // namespace mpact
+#endif // MPACT_CHERIOT_CHERIOT_RISCV_VECTOR_STATE_H_
diff --git a/cheriot/cheriot_vector_true_operand.cc b/cheriot/cheriot_vector_true_operand.cc
new file mode 100644
index 0000000..549020d
--- /dev/null
+++ b/cheriot/cheriot_vector_true_operand.cc
@@ -0,0 +1,40 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cheriot/cheriot_vector_true_operand.h"
+
+#include <cstdint>
+#include <limits>
+
+#include "cheriot/cheriot_state.h"
+#include "riscv//riscv_register.h"
+
+namespace mpact {
+namespace sim {
+namespace cheriot {
+
+CheriotVectorTrueOperand::CheriotVectorTrueOperand(CheriotState *state)
+ : RV32VectorSourceOperand(
+ state->GetRegister<RVVectorRegister>(kName).first) {
+ // Ensure the value is all ones.
+ auto *reg = state->GetRegister<RVVectorRegister>(kName).first;
+ auto data = reg->data_buffer()->Get<uint64_t>();
+ for (int i = 0; i < data.size(); i++) {
+ data[i] = std::numeric_limits<uint64_t>::max();
+ }
+}
+
+} // namespace cheriot
+} // namespace sim
+} // namespace mpact
diff --git a/cheriot/cheriot_vector_true_operand.h b/cheriot/cheriot_vector_true_operand.h
new file mode 100644
index 0000000..2a880c1
--- /dev/null
+++ b/cheriot/cheriot_vector_true_operand.h
@@ -0,0 +1,57 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef MPACT_CHERIOT_CHERIOT_VECTOR_TRUE_OPERAND_H_
+#define MPACT_CHERIOT_CHERIOT_VECTOR_TRUE_OPERAND_H_
+
+#include <cstdint>
+#include <string>
+
+#include "riscv//riscv_register.h"
+
+// File defines a Cheriot version of the RV32VectorTrueOperand registers.
+
+namespace mpact {
+namespace sim {
+namespace cheriot {
+
+using ::mpact::sim::riscv::RV32VectorSourceOperand;
+
+class CheriotState;
+
+class CheriotVectorTrueOperand : public RV32VectorSourceOperand {
+ public:
+ explicit CheriotVectorTrueOperand(CheriotState *state);
+
+ CheriotVectorTrueOperand() = delete;
+ bool AsBool(int) final { return true; }
+ int8_t AsInt8(int) final { return 0xff; }
+ uint8_t AsUint8(int) final { return 0xff; }
+ int16_t AsInt16(int) final { return 0xffff; }
+ uint16_t AsUint16(int) final { return 0xffff; }
+ int32_t AsInt32(int) final { return 0xffff'ffff; }
+ uint32_t AsUint32(int) final { return 0xffff'ffff; }
+ int64_t AsInt64(int) final { return 0xffff'ffff'ffff'ffffULL; }
+ uint64_t AsUint64(int) final { return 0xffff'ffff'ffff'ffffLL; }
+ std::string AsString() const override { return ""; }
+
+ private:
+ static constexpr char kName[] = "__VectorTrue__";
+};
+
+} // namespace cheriot
+} // namespace sim
+} // namespace mpact
+
+#endif // MPACT_CHERIOT_CHERIOT_VECTOR_TRUE_OPERAND_H_
diff --git a/cheriot/mpact_cheriot.cc b/cheriot/mpact_cheriot.cc
index 9174c63..a15cfee 100644
--- a/cheriot/mpact_cheriot.cc
+++ b/cheriot/mpact_cheriot.cc
@@ -41,11 +41,14 @@
#include "absl/time/time.h"
#include "cheriot/cheriot_decoder.h"
#include "cheriot/cheriot_instrumentation_control.h"
+#include "cheriot/cheriot_rvv_decoder.h"
+#include "cheriot/cheriot_rvv_fp_decoder.h"
#include "cheriot/cheriot_top.h"
#include "cheriot/debug_command_shell.h"
#include "cheriot/riscv_cheriot_minstret.h"
#include "mpact/sim/generic/core_debug_interface.h"
#include "mpact/sim/generic/counters.h"
+#include "mpact/sim/generic/decoder_interface.h"
#include "mpact/sim/generic/instruction.h"
#include "mpact/sim/proto/component_data.pb.h"
#include "mpact/sim/util/memory/atomic_memory.h"
@@ -67,7 +70,10 @@
using AddressRange = mpact::sim::util::MemoryWatcher::AddressRange;
using ::mpact::sim::cheriot::CheriotDecoder;
using ::mpact::sim::cheriot::CheriotInstrumentationControl;
+using ::mpact::sim::cheriot::CheriotRVVDecoder;
+using ::mpact::sim::cheriot::CheriotRVVFPDecoder;
using ::mpact::sim::cheriot::CheriotState;
+using ::mpact::sim::generic::DecoderInterface;
using ::mpact::sim::proto::ComponentData;
using ::mpact::sim::util::InstructionProfiler;
using ::mpact::sim::util::TaggedMemoryUseProfiler;
@@ -137,6 +143,12 @@
// Enable memory use profiling.
ABSL_FLAG(bool, mem_profile, false, "Enable memory use profiling");
+// Enable RiscV Vector instructions
+ABSL_FLAG(bool, rvv, false, "Enable RVV");
+
+// Enable RiscV Vector instructions + FP.
+ABSL_FLAG(bool, rvv_fp, false, "Enable RVV + FP");
+
constexpr char kStackEndSymbolName[] = "__stack_end";
constexpr char kStackSizeSymbolName[] = "__stack_size";
@@ -256,10 +268,20 @@
}
CheriotState cheriot_state("CherIoT", data_memory,
static_cast<AtomicMemoryOpInterface *>(router));
- CheriotDecoder cheriot_decoder(&cheriot_state,
- static_cast<MemoryInterface *>(router));
- CheriotTop cheriot_top("Cheriot", &cheriot_state, &cheriot_decoder);
+ DecoderInterface *decoder = nullptr;
+ if (absl::GetFlag(FLAGS_rvv_fp)) {
+ decoder = new CheriotRVVFPDecoder(&cheriot_state,
+ static_cast<MemoryInterface *>(router));
+ } else if (absl::GetFlag(FLAGS_rvv_fp)) {
+ decoder = new CheriotRVVDecoder(&cheriot_state,
+ static_cast<MemoryInterface *>(router));
+ } else {
+ decoder = new CheriotDecoder(&cheriot_state,
+ static_cast<MemoryInterface *>(router));
+ }
+
+ CheriotTop cheriot_top("Cheriot", &cheriot_state, decoder);
// Enable instruction profiling if the flag is set.
InstructionProfiler *inst_profiler = nullptr;
diff --git a/cheriot/riscv_cheriot.bin_fmt b/cheriot/riscv_cheriot.bin_fmt
index db6799c..0d2cdf2 100644
--- a/cheriot/riscv_cheriot.bin_fmt
+++ b/cheriot/riscv_cheriot.bin_fmt
@@ -1,3 +1,17 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
// RiscV 32 bit CHERIoT instruction decoder.
decoder RiscVCheriot {
namespace mpact::sim::cheriot::encoding;
@@ -435,9 +449,9 @@
instruction group RiscVCheriotInst16[16] : Inst16Format {
caddi4spn : CIW: func3 == 0b000, op == 0b00, imm8 != 0;
clw : CL : func3 == 0b010, op == 0b00;
- cld : CL : func3 == 0b011, op == 0b00;
+ clc : CL : func3 == 0b011, op == 0b00;
csw : CS : func3 == 0b110, op == 0b00;
- csd : CS : func3 == 0b111, op == 0b00;
+ csc : CS : func3 == 0b111, op == 0b00;
cnop : CI : func3 == 0b000, imm1 == 0, rs1 == 0, imm5 == 0, op == 0b01;
chint : CI : func3 == 0b000, imm6 != 0, rs1 == 0, op == 0b01;
caddi : CI : func3 == 0b000, imm6 != 0, rd != 0, op == 0b01;
@@ -465,13 +479,13 @@
chint : CI : func3 == 0b000, imm1 == 0, rs1 == 0, imm5 != 0, op == 0b10;
chint : CI : func3 == 0b000, imm6 == 0, op == 0b10;
clwsp : CI : func3 == 0b010, rd != 0, op == 0b10;
- cldsp : CI : func3 == 0b011, rd != 0, op == 0b10;
+ clcsp : CI : func3 == 0b011, rd != 0, op == 0b10;
cmv : CR : func4 == 0b1000, rs1 != 0, rs2 != 0, op == 0b10;
cebreak : Inst16Format : func3 == 0b100, bits == 0b1'00000'00000, op == 0b10;
cadd : CR : func4 == 0b1001, rs1 != 0, rs2 != 0, op == 0b10;
chint : CR : func4 == 0b1001, rs1 == 0, rs2 != 0, op == 0b10;
cswsp : CSS: func3 == 0b110, op == 0b10;
- csdsp : CSS: func3 == 0b111, op == 0b10;
+ cscsp : CSS: func3 == 0b111, op == 0b10;
cheriot_cj : CJ : func3 == 0b101, op == 0b01;
cheriot_cjal : CJ : func3 == 0b001, op == 0b01;
cheriot_cjr : CR : func4 == 0b1000, rs1 > 1, rs2 == 0, op == 0b10;
diff --git a/cheriot/riscv_cheriot.isa b/cheriot/riscv_cheriot.isa
index fea34c3..5c14c15 100644
--- a/cheriot/riscv_cheriot.isa
+++ b/cheriot/riscv_cheriot.isa
@@ -1,4 +1,18 @@
-// This file contains the ISA description for the RiscV32G architecture.
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// This file contains the ISA description for the Cheriot architecture.
includes {
#include "absl/functional/bind_front.h"
@@ -459,11 +473,11 @@
clwsp{(: c2, I_ci_uimm6x4 : ), (: : rd)},
disasm: "c.lw", "%rd, %I_ci_uimm6x4(%c2)",
semfunc: "&RiscVILw", "&RiscVILwChild";
- cflwsp{(: c2, I_ci_uimm6x4 : ), (: : frd)},
- disasm: "c.flw", "%frd, %I_ci_uimm6x4(%c2)",
- semfunc: "&RiscVILw", "&RiscVILwChild";
+ // cflwsp{(: c2, I_ci_uimm6x4 : ), (: : frd)},
+ // disasm: "c.flw", "%frd, %I_ci_uimm6x4(%c2)",
+ // semfunc: "&RiscVILw", "&RiscVILwChild";
// Reused for clc
- cldsp{(: c2, I_ci_uimm6x8 : ), (: : cd)},
+ clcsp{(: c2, I_ci_uimm6x8 : ), (: : cd)},
disasm: "c.clc", "%cd, %I_ci_uimm6x8(%c2)",
semfunc: "&CheriotCLc", "&CheriotCLcChild";
// cfldsp{(: x2, I_ci_uimm6x8 : ), (: : drd)},
@@ -472,11 +486,11 @@
cswsp{: c2, I_css_uimm6x4, crs2 : },
disasm: "c.csw", "%crs2, %I_css_uimm6x4(%c2)",
semfunc: "&RiscVISw";
- cfswsp{: c2, I_css_uimm6x4, cfrs2 : },
- disasm: ".cfsw", "%cfrs2, %I_css_uimm6x4(%c2)",
- semfunc: "&RiscVISw";
+ // cfswsp{: c2, I_css_uimm6x4, cfrs2 : },
+ // disasm: ".cfsw", "%cfrs2, %I_css_uimm6x4(%c2)",
+ // semfunc: "&RiscVISw";
// Reused for csc
- csdsp{: c2, I_css_uimm6x8, ccs2 : },
+ cscsp{: c2, I_css_uimm6x8, ccs2 : },
disasm: "c.csc", "%ccs2, %I_css_uimm6x8(%c2)",
semfunc: "&CheriotCSc";
// cfsdsp{: x2, I_css_uimm6x8, rdrs2 : },
@@ -486,7 +500,7 @@
disasm: "c.clw", "%c3rd, %I_cl_uimm5x4(%c3rs1)",
semfunc: "&RiscVILw", "&RiscVILwChild";
// Reused for clc
- cld{(: c3cs1, I_cl_uimm5x8 : ), (: : c3cd)},
+ clc{(: c3cs1, I_cl_uimm5x8 : ), (: : c3cd)},
disasm: "c.clc", "%c3cd, %I_cl_uimm5x8(%c3cs1)",
semfunc: "&CheriotCLc", "&CheriotCLcChild";
// cfld{(: c3rs1, I_cl_uimm5x8 : ), (: : c3drd)},
@@ -496,7 +510,7 @@
disasm: "c.csw", "%c3cs2, %I_cl_uimm5x4(%c3rs1)",
semfunc: "&RiscVISw";
// Reused for csc
- csd{: c3cs1, I_cl_uimm5x8, c3cs2 : },
+ csc{: c3cs1, I_cl_uimm5x8, c3cs2 : },
disasm: "c.csc", "%c3cs2, %I_cl_uimm5x8(%c3cs1)",
semfunc: "&CheriotCSc";
// cfsd{: c3rs1, I_cl_uimm5x8, c3drs2 : },
diff --git a/cheriot/riscv_cheriot_encoding.cc b/cheriot/riscv_cheriot_encoding.cc
index df9b3cd..be376d7 100644
--- a/cheriot/riscv_cheriot_encoding.cc
+++ b/cheriot/riscv_cheriot_encoding.cc
@@ -15,484 +15,43 @@
#include "cheriot/riscv_cheriot_encoding.h"
#include <cstdint>
-#include <string>
#include "absl/log/log.h"
#include "absl/strings/str_cat.h"
+#include "cheriot/cheriot_getters.h"
#include "cheriot/cheriot_register.h"
#include "cheriot/cheriot_state.h"
#include "cheriot/riscv_cheriot_bin_decoder.h"
#include "cheriot/riscv_cheriot_decoder.h"
+#include "cheriot/riscv_cheriot_encoding_common.h"
#include "cheriot/riscv_cheriot_enums.h"
-#include "cheriot/riscv_cheriot_register_aliases.h"
-#include "mpact/sim/generic/immediate_operand.h"
-#include "mpact/sim/generic/literal_operand.h"
-#include "mpact/sim/generic/type_helpers.h"
-#include "riscv//riscv_register.h"
namespace mpact {
namespace sim {
namespace cheriot {
namespace isa32 {
-using ::mpact::sim::generic::operator*; // NOLINT: is used below (clang error).
-using ::mpact::sim::riscv::RVFpRegister;
-
-// Generic helper functions to create register operands.
-template <typename RegType>
-inline DestinationOperandInterface *GetRegisterDestinationOp(
- CheriotState *state, std::string name, int latency) {
- auto *reg = state->GetRegister<RegType>(name).first;
- return reg->CreateDestinationOperand(latency);
-}
-
-template <typename RegType>
-inline DestinationOperandInterface *GetRegisterDestinationOp(
- CheriotState *state, std::string name, int latency, std::string op_name) {
- auto *reg = state->GetRegister<RegType>(name).first;
- return reg->CreateDestinationOperand(latency, op_name);
-}
-
-template <typename T>
-inline DestinationOperandInterface *GetCSRSetBitsDestinationOp(
- CheriotState *state, std::string name, int latency, std::string op_name) {
- auto result = state->csr_set()->GetCsr(name);
- if (!result.ok()) {
- LOG(ERROR) << "No such CSR '" << name << "'";
- return nullptr;
- }
- auto *csr = result.value();
- auto *op = csr->CreateSetDestinationOperand(latency, op_name);
- return op;
-}
-
-template <typename RegType>
-inline SourceOperandInterface *GetRegisterSourceOp(CheriotState *state,
- std::string name) {
- auto *reg = state->GetRegister<RegType>(name).first;
- auto *op = reg->CreateSourceOperand();
- return op;
-}
-
-template <typename RegType>
-inline SourceOperandInterface *GetRegisterSourceOp(CheriotState *state,
- std::string name,
- std::string op_name) {
- auto *reg = state->GetRegister<RegType>(name).first;
- auto *op = reg->CreateSourceOperand(op_name);
- return op;
-}
+using Extractors = ::mpact::sim::cheriot::encoding::Extractors;
RiscVCheriotEncoding::RiscVCheriotEncoding(CheriotState *state)
- : state_(state) {
- InitializeSourceOperandGetters();
- InitializeDestinationOperandGetters();
-}
-
-void RiscVCheriotEncoding::InitializeSourceOperandGetters() {
- // Source operand getters.
- source_op_getters_.emplace(
- *SourceOpEnum::kAAq, [this]() -> SourceOperandInterface * {
- if (encoding::inst32_format::ExtractAq(inst_word_)) {
- return new generic::IntLiteralOperand<1>();
- }
- return new generic::IntLiteralOperand<0>();
- });
- source_op_getters_.emplace(
- *SourceOpEnum::kARl, [this]() -> SourceOperandInterface * {
- if (encoding::inst32_format::ExtractRl(inst_word_)) {
- return new generic::IntLiteralOperand<1>();
- }
- return new generic::IntLiteralOperand<0>();
- });
- source_op_getters_.emplace(*SourceOpEnum::kBImm12, [this]() {
- return new generic::ImmediateOperand<int32_t>(
- encoding::inst32_format::ExtractBImm(inst_word_));
- });
- source_op_getters_.emplace(*SourceOpEnum::kC2, [this]() {
- return GetRegisterSourceOp<CheriotRegister>(state_, "c2", "csp");
- });
- source_op_getters_.emplace(*SourceOpEnum::kC3cs1, [this]() {
- auto num = encoding::c_s::ExtractRs1(inst_word_);
- return GetRegisterSourceOp<CheriotRegister>(
- state_, absl::StrCat(CheriotState::kCregPrefix, num),
- kCRegisterAliases[num]);
- });
- source_op_getters_.emplace(*SourceOpEnum::kC3cs2, [this]() {
- auto num = encoding::c_s::ExtractRs2(inst_word_);
- return GetRegisterSourceOp<CheriotRegister>(
- state_, absl::StrCat(CheriotState::kCregPrefix, num),
- kCRegisterAliases[num]);
- });
- source_op_getters_.emplace(*SourceOpEnum::kC3rs1, [this]() {
- auto num = encoding::c_s::ExtractRs1(inst_word_);
- return GetRegisterSourceOp<CheriotRegister>(
- state_, absl::StrCat(CheriotState::kXregPrefix, num),
- kXRegisterAliases[num]);
- });
- source_op_getters_.emplace(*SourceOpEnum::kC3rs2, [this]() {
- auto num = encoding::c_s::ExtractRs2(inst_word_);
- return GetRegisterSourceOp<CheriotRegister>(
- state_, absl::StrCat(CheriotState::kXregPrefix, num),
- kXRegisterAliases[num]);
- });
- source_op_getters_.emplace(*SourceOpEnum::kCcs2, [this]() {
- auto num = encoding::c_s_s::ExtractRs2(inst_word_);
- return GetRegisterSourceOp<CheriotRegister>(
- state_, absl::StrCat(CheriotState::kCregPrefix, num),
- kCRegisterAliases[num]);
- });
- source_op_getters_.emplace(*SourceOpEnum::kCgp, [this]() {
- return GetRegisterSourceOp<CheriotRegister>(state_, "c3", "c3");
- });
- source_op_getters_.emplace(*SourceOpEnum::kCSRUimm5, [this]() {
- return new generic::ImmediateOperand<uint32_t>(
- encoding::inst32_format::ExtractIUimm5(inst_word_));
- });
- source_op_getters_.emplace(*SourceOpEnum::kCfrs2, [this]() {
- auto num = encoding::c_r::ExtractRs2(inst_word_);
- return GetRegisterSourceOp<RVFpRegister>(
- state_, absl::StrCat(CheriotState::kFregPrefix, num),
- kFRegisterAliases[num]);
- });
- source_op_getters_.emplace(*SourceOpEnum::kCrs1, [this]() {
- auto num = encoding::c_r::ExtractRs1(inst_word_);
- return GetRegisterSourceOp<CheriotRegister>(
- state_, absl::StrCat(CheriotState::kXregPrefix, num),
- kXRegisterAliases[num]);
- });
- source_op_getters_.emplace(*SourceOpEnum::kCrs2, [this]() {
- auto num = encoding::c_r::ExtractRs2(inst_word_);
- return GetRegisterSourceOp<CheriotRegister>(
- state_, absl::StrCat(CheriotState::kXregPrefix, num),
- kXRegisterAliases[num]);
- });
- source_op_getters_.emplace(*SourceOpEnum::kCs1, [this]() {
- auto num = encoding::r_type::ExtractRs1(inst_word_);
- return GetRegisterSourceOp<CheriotRegister>(
- state_, absl::StrCat(CheriotState::kCregPrefix, num),
- kCRegisterAliases[num]);
- });
- source_op_getters_.emplace(*SourceOpEnum::kCs2, [this]() {
- auto num = encoding::r_type::ExtractRs2(inst_word_);
- return GetRegisterSourceOp<CheriotRegister>(
- state_, absl::StrCat(CheriotState::kCregPrefix, num),
- kCRegisterAliases[num]);
- });
- source_op_getters_.emplace(*SourceOpEnum::kCsr, [this]() {
- auto csr_indx = encoding::i_type::ExtractUImm12(inst_word_);
- auto res = state_->csr_set()->GetCsr(csr_indx);
- if (!res.ok()) {
- return new generic::ImmediateOperand<uint32_t>(csr_indx);
- }
- auto *csr = res.value();
- return new generic::ImmediateOperand<uint32_t>(csr_indx, csr->name());
- });
- /*
- source_op_getters_.emplace(*SourceOpEnum::kFrs1, [this]() {
- int num = encoding::r_type::ExtractRs1(inst_word_);
- return GetRegisterSourceOp<RVFpRegister>(
- state_, absl::StrCat(CheriotState::kFregPrefix, num),
- kFRegisterAliases[num]);
- });
- source_op_getters_.emplace(*SourceOpEnum::kFrs2, [this]() {
- int num = encoding::r_type::ExtractRs2(inst_word_);
- return GetRegisterSourceOp<RVFpRegister>(
- state_, absl::StrCat(CheriotState::kFregPrefix, num),
- kFRegisterAliases[num]);
- });
- source_op_getters_.emplace(*SourceOpEnum::kFrs3, [this]() {
- int num = encoding::r4_type::ExtractRs3(inst_word_);
- return GetRegisterSourceOp<RVFpRegister>(
- state_, absl::StrCat(CheriotState::kFregPrefix, num),
- kFRegisterAliases[num]);
- });
- */
- source_op_getters_.emplace(*SourceOpEnum::kICbImm8, [this]() {
- return new generic::ImmediateOperand<int32_t>(
- encoding::inst16_format::ExtractBimm(inst_word_));
- });
- source_op_getters_.emplace(*SourceOpEnum::kICiImm6, [this]() {
- return new generic::ImmediateOperand<int32_t>(
- encoding::c_i::ExtractImm6(inst_word_));
- });
- source_op_getters_.emplace(*SourceOpEnum::kICiImm612, [this]() {
- return new generic::ImmediateOperand<int32_t>(
- encoding::inst16_format::ExtractImm18(inst_word_));
- });
- source_op_getters_.emplace(*SourceOpEnum::kICiUimm6, [this]() {
- return new generic::ImmediateOperand<uint32_t>(
- encoding::inst16_format::ExtractUimm6(inst_word_));
- });
- source_op_getters_.emplace(*SourceOpEnum::kICiUimm6x4, [this]() {
- return new generic::ImmediateOperand<uint32_t>(
- encoding::inst16_format::ExtractCiImmW(inst_word_));
- });
- source_op_getters_.emplace(*SourceOpEnum::kICiImm6x16, [this]() {
- return new generic::ImmediateOperand<int32_t>(
- encoding::inst16_format::ExtractCiImm10(inst_word_));
- });
- source_op_getters_.emplace(*SourceOpEnum::kICiUimm6x8, [this]() {
- return new generic::ImmediateOperand<uint32_t>(
- encoding::inst16_format::ExtractCiImmD(inst_word_));
- });
- source_op_getters_.emplace(*SourceOpEnum::kICiwUimm8x4, [this]() {
- return new generic::ImmediateOperand<uint32_t>(
- encoding::inst16_format::ExtractCiwImm10(inst_word_));
- });
- source_op_getters_.emplace(*SourceOpEnum::kICjImm11, [this]() {
- return new generic::ImmediateOperand<int32_t>(
- encoding::inst16_format::ExtractJimm(inst_word_));
- });
- source_op_getters_.emplace(*SourceOpEnum::kIClUimm5x4, [this]() {
- return new generic::ImmediateOperand<uint32_t>(
- encoding::inst16_format::ExtractClImmW(inst_word_));
- });
- source_op_getters_.emplace(*SourceOpEnum::kIClUimm5x8, [this]() {
- return new generic::ImmediateOperand<uint32_t>(
- encoding::inst16_format::ExtractClImmD(inst_word_));
- });
- source_op_getters_.emplace(*SourceOpEnum::kICshUimm6, [this]() {
- return new generic::ImmediateOperand<uint32_t>(
- encoding::c_s_h::ExtractUimm6(inst_word_));
- });
- source_op_getters_.emplace(*SourceOpEnum::kICshImm6, [this]() {
- return new generic::ImmediateOperand<uint32_t>(
- encoding::c_s_h::ExtractImm6(inst_word_));
- });
- source_op_getters_.emplace(*SourceOpEnum::kICssUimm6x4, [this]() {
- return new generic::ImmediateOperand<uint32_t>(
- encoding::inst16_format::ExtractCssImmW(inst_word_));
- });
- source_op_getters_.emplace(*SourceOpEnum::kICssUimm6x8, [this]() {
- return new generic::ImmediateOperand<uint32_t>(
- encoding::inst16_format::ExtractCssImmD(inst_word_));
- });
- source_op_getters_.emplace(*SourceOpEnum::kIImm12, [this]() {
- return new generic::ImmediateOperand<int32_t>(
- encoding::inst32_format::ExtractImm12(inst_word_));
- });
- source_op_getters_.emplace(*SourceOpEnum::kIUimm5, [this]() {
- return new generic::ImmediateOperand<uint32_t>(
- encoding::i5_type::ExtractRUimm5(inst_word_));
- });
- source_op_getters_.emplace(*SourceOpEnum::kIUimm12, [this]() {
- return new generic::ImmediateOperand<uint32_t>(
- encoding::inst32_format::ExtractUImm12(inst_word_));
- });
- source_op_getters_.emplace(*SourceOpEnum::kJImm12, [this]() {
- return new generic::ImmediateOperand<int32_t>(
- encoding::inst32_format::ExtractImm12(inst_word_));
- });
- source_op_getters_.emplace(*SourceOpEnum::kJImm20, [this]() {
- return new generic::ImmediateOperand<int32_t>(
- encoding::inst32_format::ExtractJImm(inst_word_));
- });
- source_op_getters_.emplace(*SourceOpEnum::kPcc, [this]() {
- return GetRegisterSourceOp<CheriotRegister>(state_, "pcc", "pcc");
- });
- /*
- source_op_getters_.emplace(*SourceOpEnum::kRm,
- [this]() -> SourceOperandInterface * {
- uint32_t rm = (inst_word_ >> 12) & 0x7;
- switch (rm) {
- case 0:
- return new generic::IntLiteralOperand<0>();
- case 1:
- return new generic::IntLiteralOperand<1>();
- case 2:
- return new generic::IntLiteralOperand<2>();
- case 3:
- return new generic::IntLiteralOperand<3>();
- case 4:
- return new generic::IntLiteralOperand<4>();
- case 5:
- return new generic::IntLiteralOperand<5>();
- case 6:
- return new generic::IntLiteralOperand<6>();
- case 7:
- return new generic::IntLiteralOperand<7>();
- default:
- return nullptr;
- }
- });
- */
- source_op_getters_.emplace(
- *SourceOpEnum::kRd, [this]() -> SourceOperandInterface * {
- int num = encoding::r_type::ExtractRd(inst_word_);
- if (num == 0) return new generic::IntLiteralOperand<0>({1});
- return GetRegisterSourceOp<CheriotRegister>(
- state_, absl::StrCat(CheriotState::kXregPrefix, num),
- kXRegisterAliases[num]);
- });
- source_op_getters_.emplace(
- *SourceOpEnum::kRs1, [this]() -> SourceOperandInterface * {
- int num = encoding::r_type::ExtractRs1(inst_word_);
- if (num == 0) return new generic::IntLiteralOperand<0>({1});
- return GetRegisterSourceOp<CheriotRegister>(
- state_, absl::StrCat(CheriotState::kXregPrefix, num),
- kXRegisterAliases[num]);
- });
- source_op_getters_.emplace(
- *SourceOpEnum::kRs2, [this]() -> SourceOperandInterface * {
- int num = encoding::r_type::ExtractRs2(inst_word_);
- if (num == 0) return new generic::IntLiteralOperand<0>({1});
- return GetRegisterSourceOp<CheriotRegister>(
- state_, absl::StrCat(CheriotState::kXregPrefix, num),
- kXRegisterAliases[num]);
- });
- source_op_getters_.emplace(*SourceOpEnum::kSImm12, [this]() {
- return new generic::ImmediateOperand<int32_t>(
- encoding::s_type::ExtractSImm(inst_word_));
- });
- source_op_getters_.emplace(
- *SourceOpEnum::kScr, [this]() -> SourceOperandInterface * {
- int csr_indx = encoding::r_type::ExtractRs2(inst_word_);
- std::string csr_name;
- switch (csr_indx) {
- case 28:
- csr_name = "mtcc";
- break;
- case 29:
- csr_name = "mtdc";
- break;
- case 30:
- csr_name = "mscratchc";
- break;
- case 31:
- csr_name = "mepcc";
- break;
- default:
- return nullptr;
- }
- auto res = state_->csr_set()->GetCsr(csr_name);
- if (!res.ok()) {
- return GetRegisterSourceOp<CheriotRegister>(state_, csr_name,
- csr_name);
- }
- auto *csr = res.value();
- auto *op = csr->CreateSourceOperand();
- return op;
- });
- source_op_getters_.emplace(*SourceOpEnum::kSImm20, [this]() {
- return new generic::ImmediateOperand<int32_t>(
- encoding::u_type::ExtractSImm(inst_word_));
- });
- source_op_getters_.emplace(*SourceOpEnum::kUImm20, [this]() {
- return new generic::ImmediateOperand<int32_t>(
- encoding::inst32_format::ExtractUImm(inst_word_));
- });
- source_op_getters_.emplace(*SourceOpEnum::kX0, []() {
- return new generic::IntLiteralOperand<0>({1});
- });
- source_op_getters_.emplace(*SourceOpEnum::kX2, [this]() {
- return GetRegisterSourceOp<CheriotRegister>(
- state_, absl::StrCat(CheriotState::kXregPrefix, 2),
- kXRegisterAliases[2]);
- });
+ : RiscVCheriotEncodingCommon(state) {
source_op_getters_.emplace(*SourceOpEnum::kNone, []() { return nullptr; });
-}
-
-void RiscVCheriotEncoding::InitializeDestinationOperandGetters() {
- // Destination operand getters.
- dest_op_getters_.emplace(*DestOpEnum::kC2, [this](int latency) {
- return GetRegisterDestinationOp<CheriotRegister>(state_, "c2", latency,
- "csp");
- });
- dest_op_getters_.emplace(*DestOpEnum::kC3cd, [this](int latency) {
- int num = encoding::c_l::ExtractRd(inst_word_);
- return GetRegisterDestinationOp<CheriotRegister>(
- state_, absl::StrCat(CheriotState::kCregPrefix, num), latency,
- kCRegisterAliases[num]);
- });
- dest_op_getters_.emplace(*DestOpEnum::kC3rd, [this](int latency) {
- int num = encoding::c_l::ExtractRd(inst_word_);
- if (num == 0) {
- return GetRegisterDestinationOp<CheriotRegister>(state_, "X0Dest",
- latency);
- }
- return GetRegisterDestinationOp<CheriotRegister>(
- state_, absl::StrCat(CheriotState::kXregPrefix, num), latency,
- kXRegisterAliases[num]);
- });
- dest_op_getters_.emplace(*DestOpEnum::kC3rs1, [this](int latency) {
- int num = encoding::c_l::ExtractRs1(inst_word_);
- return GetRegisterDestinationOp<CheriotRegister>(
- state_, absl::StrCat(CheriotState::kXregPrefix, num), latency,
- kXRegisterAliases[num]);
- });
- dest_op_getters_.emplace(*DestOpEnum::kCd, [this](int latency) {
- int num = encoding::r_type::ExtractRd(inst_word_);
- if (num == 0) {
- return GetRegisterDestinationOp<CheriotRegister>(state_, "X0Dest",
- latency);
- }
- return GetRegisterDestinationOp<CheriotRegister>(
- state_, absl::StrCat(CheriotState::kCregPrefix, num), latency,
- kCRegisterAliases[num]);
- });
- dest_op_getters_.emplace(*DestOpEnum::kCsr, [this](int latency) {
- return GetRegisterDestinationOp<CheriotRegister>(
- state_, CheriotState::kCsrName, latency);
- });
- dest_op_getters_.emplace(*DestOpEnum::kFrd, [this](int latency) {
- int num = encoding::r_type::ExtractRd(inst_word_);
- return GetRegisterDestinationOp<RVFpRegister>(
- state_, absl::StrCat(CheriotState::kFregPrefix, num), latency,
- kFRegisterAliases[num]);
- });
- dest_op_getters_.emplace(
- *DestOpEnum::kScr, [this](int latency) -> DestinationOperandInterface * {
- int csr_indx = encoding::r_type::ExtractRs2(inst_word_);
- std::string csr_name;
- switch (csr_indx) {
- case 28:
- csr_name = "mtcc";
- break;
- case 29:
- csr_name = "mtdc";
- break;
- case 30:
- csr_name = "mscratchc";
- break;
- case 31:
- csr_name = "mepcc";
- break;
- default:
- return nullptr;
- }
- auto res = state_->csr_set()->GetCsr(csr_name);
- if (!res.ok()) {
- return GetRegisterDestinationOp<CheriotRegister>(state_, csr_name,
- latency);
- }
- auto *csr = res.value();
- auto *op = csr->CreateWriteDestinationOperand(latency, csr_name);
- return op;
- });
- dest_op_getters_.emplace(
- *DestOpEnum::kRd, [this](int latency) -> DestinationOperandInterface * {
- int num = encoding::r_type::ExtractRd(inst_word_);
- if (num == 0) {
- return GetRegisterDestinationOp<CheriotRegister>(state_, "X0Dest", 0);
- } else {
- return GetRegisterDestinationOp<RVFpRegister>(
- state_, absl::StrCat(CheriotState::kXregPrefix, num), latency,
- kXRegisterAliases[num]);
- }
- });
- dest_op_getters_.emplace(*DestOpEnum::kX1, [this](int latency) {
- return GetRegisterDestinationOp<CheriotRegister>(
- state_, absl::StrCat(CheriotState::kXregPrefix, 1), latency,
- kXRegisterAliases[1]);
- });
- /*
- dest_op_getters_.emplace(*DestOpEnum::kFflags, [this](int latency) {
- return GetCSRSetBitsDestinationOp<uint32_t>(state_, "fflags", latency, "");
- });
- */
dest_op_getters_.emplace(*DestOpEnum::kNone,
[](int latency) { return nullptr; });
+ // Add Cheriot ISA source and destination operand getters.
+ AddCheriotSourceGetters<SourceOpEnum, Extractors>(source_op_getters_, this);
+ AddCheriotDestGetters<DestOpEnum, Extractors>(dest_op_getters_, this);
+ // Verify that all source and destination op enum values have a getter.
+ for (int i = *SourceOpEnum::kNone; i < *SourceOpEnum::kPastMaxValue; ++i) {
+ if (source_op_getters_.find(i) == source_op_getters_.end()) {
+ LOG(ERROR) << "No getter for source op enum value " << i;
+ }
+ }
+ for (int i = *DestOpEnum::kNone; i < *DestOpEnum::kPastMaxValue; ++i) {
+ if (dest_op_getters_.find(i) == dest_op_getters_.end()) {
+ LOG(ERROR) << "No getter for destination op enum value " << i;
+ }
+ }
}
// Parse the instruction word to determine the opcode.
diff --git a/cheriot/riscv_cheriot_encoding.h b/cheriot/riscv_cheriot_encoding.h
index de50ca2..b5af035 100644
--- a/cheriot/riscv_cheriot_encoding.h
+++ b/cheriot/riscv_cheriot_encoding.h
@@ -24,6 +24,7 @@
#include "cheriot/cheriot_state.h"
#include "cheriot/riscv_cheriot_bin_decoder.h"
#include "cheriot/riscv_cheriot_decoder.h"
+#include "cheriot/riscv_cheriot_encoding_common.h"
#include "cheriot/riscv_cheriot_enums.h"
namespace mpact {
@@ -38,7 +39,8 @@
// instructions) and the instruction representation. This class provides methods
// to return the opcode, source operands, and destination operands for
// instructions according to the operand fields in the encoding.
-class RiscVCheriotEncoding : public RiscVCheriotEncodingBase {
+class RiscVCheriotEncoding : public RiscVCheriotEncodingCommon,
+ public RiscVCheriotEncodingBase {
public:
explicit RiscVCheriotEncoding(CheriotState *state);
@@ -96,15 +98,8 @@
using DestOpGetterMap = absl::flat_hash_map<
int, absl::AnyInvocable<DestinationOperandInterface *(int)>>;
- // These two methods initialize the source and destination operand getter
- // arrays.
- void InitializeSourceOperandGetters();
- void InitializeDestinationOperandGetters();
-
SourceOpGetterMap source_op_getters_;
DestOpGetterMap dest_op_getters_;
- CheriotState *state_;
- uint32_t inst_word_;
OpcodeEnum opcode_;
FormatEnum format_;
};
diff --git a/cheriot/riscv_cheriot_encoding_common.h b/cheriot/riscv_cheriot_encoding_common.h
new file mode 100644
index 0000000..7a355b9
--- /dev/null
+++ b/cheriot/riscv_cheriot_encoding_common.h
@@ -0,0 +1,47 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef MPACT_CHERIOT_RISCV_CHERIOT_ENCODING_COMMON_H_
+#define MPACT_CHERIOT_RISCV_CHERIOT_ENCODING_COMMON_H_
+
+#include <cstdint>
+
+namespace mpact {
+namespace sim {
+namespace cheriot {
+
+class CheriotState;
+
+// This class provides a common interface for accessing the state and
+// instruction word for the RiscVCheriotEncoding classes (scalar, vector, vector
+// + fp).
+
+class RiscVCheriotEncodingCommon {
+ public:
+ explicit RiscVCheriotEncodingCommon(CheriotState *state) : state_(state) {}
+
+ // Accessors.
+ CheriotState *state() const { return state_; }
+ uint32_t inst_word() const { return inst_word_; }
+
+ protected:
+ CheriotState *state_;
+ uint32_t inst_word_;
+};
+
+} // namespace cheriot
+} // namespace sim
+} // namespace mpact
+
+#endif // MPACT_CHERIOT_RISCV_CHERIOT_ENCODING_COMMON_H_
diff --git a/cheriot/riscv_cheriot_f.bin_fmt b/cheriot/riscv_cheriot_f.bin_fmt
new file mode 100644
index 0000000..f2f5741
--- /dev/null
+++ b/cheriot/riscv_cheriot_f.bin_fmt
@@ -0,0 +1,43 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+instruction group RiscVFInst32[32] : Inst32Format {
+ // RiscV32 single precision floating point instructions.
+ flw : IType : func3 == 0b010, opcode == 0b000'0111;
+ fsw : SType : func3 == 0b010, opcode == 0b010'0111;
+ fmadd_s : R4Type : func2 == 0b00, opcode == 0b100'0011;
+ fmsub_s : R4Type : func2 == 0b00, opcode == 0b100'0111;
+ fnmsub_s : R4Type : func2 == 0b00, opcode == 0b100'1011;
+ fnmadd_s : R4Type : func2 == 0b00, opcode == 0b100'1111;
+ fadd_s : RType : func7 == 0b000'0000, opcode == 0b101'0011;
+ fsub_s : RType : func7 == 0b000'0100, opcode == 0b101'0011;
+ fmul_s : RType : func7 == 0b000'1000, opcode == 0b101'0011;
+ fdiv_s : RType : func7 == 0b000'1100, opcode == 0b101'0011;
+ fsqrt_s : RType : func7 == 0b010'1100, rs2 == 0, opcode == 0b101'0011;
+ fsgnj_s : RType : func7 == 0b001'0000, func3 == 0b000, opcode == 0b101'0011;
+ fsgnjn_s : RType : func7 == 0b001'0000, func3 == 0b001, opcode == 0b101'0011;
+ fsgnjx_s : RType : func7 == 0b001'0000, func3 == 0b010, opcode == 0b101'0011;
+ fmin_s : RType : func7 == 0b001'0100, func3 == 0b000, opcode == 0b101'0011;
+ fmax_s : RType : func7 == 0b001'0100, func3 == 0b001, opcode == 0b101'0011;
+ fcvt_ws : RType : func7 == 0b110'0000, rs2 == 0, opcode == 0b101'0011;
+ fcvt_wus : RType : func7 == 0b110'0000, rs2 == 1, opcode == 0b101'0011;
+ fmv_xw : RType : func7 == 0b111'0000, rs2 == 0, func3 == 0b000, opcode == 0b101'0011;
+ fcmpeq_s : RType : func7 == 0b101'0000, func3 == 0b010, opcode == 0b101'0011;
+ fcmplt_s : RType : func7 == 0b101'0000, func3 == 0b001, opcode == 0b101'0011;
+ fcmple_s : RType : func7 == 0b101'0000, func3 == 0b000, opcode == 0b101'0011;
+ fclass_s : RType : func7 == 0b111'0000, rs2 == 0, func3 == 0b001, opcode == 0b101'0011;
+ fcvt_sw : RType : func7 == 0b110'1000, rs2 == 0, opcode == 0b101'0011;
+ fcvt_swu : RType : func7 == 0b110'1000, rs2 == 1, opcode == 0b101'0011;
+ fmv_wx : RType : func7 == 0b111'1000, rs2 == 0, func3 == 0b000, opcode == 0b101'0011;
+};
diff --git a/cheriot/riscv_cheriot_f.isa b/cheriot/riscv_cheriot_f.isa
new file mode 100644
index 0000000..fda14dc
--- /dev/null
+++ b/cheriot/riscv_cheriot_f.isa
@@ -0,0 +1,132 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+// RiscV32 F (single precision floating point) instructions.
+slot riscv_cheriot_f {
+ includes {
+ #include "cheriot/riscv_cheriot_f_instructions.h"
+ }
+ default size = 4;
+ default latency = global_latency;
+ resources TwoOp = { next_pc, frs1 : frd[0..]};
+ resources ThreeOp = { next_pc, frs1, frs2 : frd[0..]};
+ resources FourOp = { next_pc, frs1, frs2, frs3 : frd[0..]};
+ opcodes {
+ flw{(: rs1, I_imm12 : ), (: : frd)},
+ resources: { next_pc, rs1 : frd[0..]},
+ semfunc: "&RiscVILw", "&RiscVIFlwChild",
+ disasm: "flw", "%frd, %I_imm12(%rs1)";
+ fsw{: rs1, S_imm12, frs2},
+ resources: { next_pc, rs1, frs2},
+ semfunc: "&RV32::RiscVFSw",
+ disasm: "fsw", "%frs2, %S_imm12(%rs1)";
+ fadd_s{: frs1, frs2, rm : frd},
+ resources: ThreeOp,
+ semfunc: "&RiscVFAdd",
+ disasm: "fadd", "%frd, %frs1, %frs2";
+ fsub_s{: frs1, frs2, rm : frd},
+ resources: ThreeOp,
+ semfunc: "&RiscVFSub",
+ disasm: "fsub", "%frd, %frs1, %frs2";
+ fmul_s{: frs1, frs2, rm : frd},
+ resources: ThreeOp,
+ semfunc: "&RiscVFMul",
+ disasm: "fmul", "%frd, %frs1, %frs2";
+ fdiv_s{: frs1, frs2, rm : frd},
+ resources: ThreeOp,
+ semfunc: "&RiscVFDiv",
+ disasm: "fdiv", "%frd, %frs1, %frs2";
+ fsqrt_s{: frs1, rm : frd},
+ resources: TwoOp,
+ semfunc: "&RiscVFSqrt",
+ disasm: "fsqrt", "%frd, %frs1";
+ fmin_s{: frs1, frs2 : frd, fflags},
+ resources: ThreeOp,
+ semfunc: "&RiscVFMin",
+ disasm: "fmin", "%frd, %frs1, %frs2";
+ fmax_s{: frs1, frs2 : frd, fflags},
+ resources: ThreeOp,
+ semfunc: "&RiscVFMax",
+ disasm: "fmax", "%frd, %frs1, %frs2";
+ fmadd_s{: frs1, frs2, frs3, rm : frd, fflags},
+ resources: FourOp,
+ semfunc: "&RiscVFMadd",
+ disasm: "fmadd", "%frd, %frs1, %frs2, %frs3";
+ fmsub_s{: frs1, frs2, frs3, rm : frd, fflags},
+ resources: FourOp,
+ semfunc: "&RiscVFMsub",
+ disasm: "fmsub", "%frd, %frs1, %frs2, %frs3";
+ fnmadd_s{: frs1, frs2, frs3, rm : frd, fflags},
+ resources: FourOp,
+ semfunc: "&RiscVFNmadd",
+ disasm: "fnmadd", "%frd, %frs1, %frs2, %frs3";
+ fnmsub_s{: frs1, frs2, frs3, rm : frd, fflags},
+ resources: FourOp,
+ semfunc: "&RiscVFNmsub",
+ disasm: "fnmsub", "%frd, %frs1, %frs2, %frs3";
+ fcvt_ws{: frs1, rm : rd, fflags},
+ resources: TwoOp,
+ semfunc: "&RV32::RiscVFCvtWs",
+ disasm: "fcvt.w.s", "%rd, %frs1";
+ fcvt_sw{: rs1, rm : frd},
+ resources: TwoOp,
+ semfunc: "&RiscVFCvtSw",
+ disasm: "fcvt.s.w", "%frd, %rs1";
+ fcvt_wus{: frs1, rm : rd, fflags},
+ resources: TwoOp,
+ semfunc: "&RV32::RiscVFCvtWus",
+ disasm: "fcvt.wu.s", "%rd, %frs1";
+ fcvt_swu{: rs1, rm : frd},
+ resources: TwoOp,
+ semfunc: "&RiscVFCvtSwu",
+ disasm: "fcvt.s.wu", "%frd, %rs1";
+ fsgnj_s{: frs1, frs2 : frd},
+ resources: ThreeOp,
+ semfunc: "&RiscVFSgnj",
+ disasm: "fsgn.s", "%frd, %frs1, %frs2";
+ fsgnjn_s{: frs1, frs2 : frd},
+ resources: ThreeOp,
+ semfunc: "&RiscVFSgnjn",
+ disasm: "fsgnjx.s", "%frd, %frs1, %frs2";
+ fsgnjx_s{: frs1, frs2 : frd},
+ resources: ThreeOp,
+ semfunc: "&RiscVFSgnjx",
+ disasm: "fsgnjx.s", "%frd, %frs1, %frs2";
+ fmv_xw{: frs1 : rd},
+ resources: { next_pc, frs1 : rd[0..]},
+ disasm: "mv.x.w", "%rd, %frs1",
+ semfunc: "&RV32::RiscVFMvxw";
+ fmv_wx{: rs1 : frd},
+ resources: { next_pc, rs1 : frd[0..]},
+ disasm: "mv.w.x", "%frd, %rs1",
+ semfunc: "&RiscVFMvwx";
+ fcmpeq_s{: frs1, frs2 : rd, fflags},
+ resources: { next_pc, frs1, frs2 : rd[0..]},
+ semfunc: "&RV32::RiscVFCmpeq",
+ disasm: "fcmpeq", "%rd, %frs1, %frs2";
+ fcmplt_s{: frs1, frs2 : rd, fflags},
+ resources: { next_pc, frs1, frs2 : rd[0..]},
+ semfunc: "&RV32::RiscVFCmplt",
+ disasm: "fcmplt", "%rd, %frs1, %frs2";
+ fcmple_s{: frs1, frs2 : rd, fflags},
+ resources: { next_pc, frs1, frs2 : rd[0..]},
+ semfunc: "&RV32::RiscVFCmple",
+ disasm: "fcmple", "%rd, %frs1, %frs2";
+ fclass_s{: frs1 : rd},
+ resources: { next_pc, frs1 : rd[0..]},
+ semfunc: "&RV32::RiscVFClass",
+ disasm: "fclass", "%rd, %frs1";
+ }
+}
\ No newline at end of file
diff --git a/cheriot/riscv_cheriot_f_instructions.cc b/cheriot/riscv_cheriot_f_instructions.cc
index a5d9b15..26e2dd6 100644
--- a/cheriot/riscv_cheriot_f_instructions.cc
+++ b/cheriot/riscv_cheriot_f_instructions.cc
@@ -17,9 +17,7 @@
#include <cmath>
#include <cstdint>
#include <functional>
-#include <iostream>
#include <limits>
-#include <tuple>
#include <type_traits>
#include "cheriot/cheriot_register.h"
@@ -27,6 +25,7 @@
#include "cheriot/riscv_cheriot_instruction_helpers.h"
#include "mpact/sim/generic/register.h"
#include "mpact/sim/generic/type_helpers.h"
+#include "riscv//riscv_fp_info.h"
#include "riscv//riscv_register.h"
#include "riscv//riscv_state.h"
@@ -34,6 +33,8 @@
namespace sim {
namespace cheriot {
+using ::mpact::sim::generic::FPTypeInfo;
+using ::mpact::sim::riscv::FPExceptions;
using ::mpact::sim::riscv::LoadContext;
// The following instruction semantic functions implement the single precision
@@ -63,22 +64,20 @@
// Convert float to signed 32 bit integer.
template <typename XInt>
static inline void RVFCvtWs(const Instruction *instruction) {
- RiscVConvertFloatWithFflagsOp<CheriotRegister, XInt, float, int32_t>(
- instruction);
+ RVCheriotConvertFloatWithFflagsOp<XInt, float, int32_t>(instruction);
}
// Convert float to unsigned 32 bit integer.
template <typename XInt>
static inline void RVFCvtWus(const Instruction *instruction) {
- RiscVConvertFloatWithFflagsOp<CheriotRegister, XInt, float, uint32_t>(
- instruction);
+ RVCheriotConvertFloatWithFflagsOp<XInt, float, uint32_t>(instruction);
}
// Single precision compare equal.
template <typename XRegister>
static inline void RVFCmpeq(const Instruction *instruction) {
- RVCheriotBinaryNaNBoxOp<typename XRegister::ValueType,
- typename XRegister::ValueType, float>(
+ RVCheriotBinaryOp<typename XRegister::ValueType,
+ typename XRegister::ValueType, float>(
instruction,
[instruction](float a, float b) -> typename XRegister::ValueType {
if (FPTypeInfo<float>::IsSNaN(a) || FPTypeInfo<float>::IsSNaN(b)) {
@@ -93,8 +92,8 @@
// Single precicion compare less than.
template <typename XRegister>
static inline void RVFCmplt(const Instruction *instruction) {
- RVCheriotBinaryNaNBoxOp<typename XRegister::ValueType,
- typename XRegister::ValueType, float>(
+ RVCheriotBinaryOp<typename XRegister::ValueType,
+ typename XRegister::ValueType, float>(
instruction,
[instruction](float a, float b) -> typename XRegister::ValueType {
if (FPTypeInfo<float>::IsNaN(a) || FPTypeInfo<float>::IsNaN(b)) {
@@ -109,8 +108,8 @@
// Single precision compare less than or equal.
template <typename XRegister>
static inline void RVFCmple(const Instruction *instruction) {
- RVCheriotBinaryNaNBoxOp<typename XRegister::ValueType,
- typename XRegister::ValueType, float>(
+ RVCheriotBinaryOp<typename XRegister::ValueType,
+ typename XRegister::ValueType, float>(
instruction,
[instruction](float a, float b) -> typename XRegister::ValueType {
if (FPTypeInfo<float>::IsNaN(a) || FPTypeInfo<float>::IsNaN(b)) {
@@ -152,29 +151,29 @@
// Basic arithmetic instructions.
void RiscVFAdd(const Instruction *instruction) {
- RiscVBinaryFloatNaNBoxOp<FPRegister::ValueType, float, float>(
+ RVCheriotBinaryFloatNaNBoxOp<FPRegister::ValueType, float, float>(
instruction, [](float a, float b) { return a + b; });
}
void RiscVFSub(const Instruction *instruction) {
- RiscVBinaryFloatNaNBoxOp<FPRegister::ValueType, float, float>(
+ RVCheriotBinaryFloatNaNBoxOp<FPRegister::ValueType, float, float>(
instruction, [](float a, float b) { return a - b; });
}
void RiscVFMul(const Instruction *instruction) {
- RiscVBinaryFloatNaNBoxOp<FPRegister::ValueType, float, float>(
+ RVCheriotBinaryFloatNaNBoxOp<FPRegister::ValueType, float, float>(
instruction, [](float a, float b) { return a * b; });
}
void RiscVFDiv(const Instruction *instruction) {
- RiscVBinaryFloatNaNBoxOp<FPRegister::ValueType, float, float>(
+ RVCheriotBinaryFloatNaNBoxOp<FPRegister::ValueType, float, float>(
instruction, [](float a, float b) { return a / b; });
}
// Square root uses the library square root.
void RiscVFSqrt(const Instruction *instruction) {
- RiscVUnaryFloatNaNBoxOp<FPRegister::ValueType, FPRegister::ValueType, float,
- float>(instruction, [](float a) -> float {
+ RVCheriotUnaryFloatNaNBoxOp<FPRegister::ValueType, FPRegister::ValueType,
+ float, float>(instruction, [](float a) -> float {
float res = sqrt(a);
if (std::isnan(res))
return *reinterpret_cast<const float *>(
@@ -239,131 +238,66 @@
void RiscVFMadd(const Instruction *instruction) {
using T = float;
- RiscVTernaryFloatNaNBoxOp<FPRegister::ValueType, T, T>(
+ RVCheriotTernaryFloatNaNBoxOp<FPRegister::ValueType, T, T>(
instruction, [instruction](T a, T b, T c) -> T {
- // Propagate any NaNs.
- if (FPTypeInfo<T>::IsNaN(a)) return internal::CanonicalizeNaN(a);
- if (FPTypeInfo<T>::IsNaN(b)) return internal::CanonicalizeNaN(b);
if ((std::isinf(a) && (b == 0.0)) || ((std::isinf(b) && (a == 0.0)))) {
auto *flag_db = instruction->Destination(1)->AllocateDataBuffer();
flag_db->Set<uint32_t>(0, *FPExceptions::kInvalidOp);
flag_db->Submit();
}
- if (FPTypeInfo<T>::IsNaN(c)) return internal::CanonicalizeNaN(c);
- if (std::isinf(c) && !std::isinf(a) && !std::isinf(b)) return c;
- if (c == 0.0) {
- if ((a == 0.0 && !std::isinf(b)) || (b == 0.0 && !std::isinf(a))) {
- FPUInt c_sign = *reinterpret_cast<FPUInt *>(&c) >>
- (FPTypeInfo<T>::kBitSize - 1);
- FPUInt ua = *reinterpret_cast<FPUInt *>(&a);
- FPUInt ub = *reinterpret_cast<FPUInt *>(&b);
- FPUInt prod_sign = (ua ^ ub) >> (FPTypeInfo<T>::kBitSize - 1);
- if (prod_sign != c_sign) return 0.0;
- return c;
- }
- return internal::CanonicalizeNaN(a * b);
- }
- return internal::CanonicalizeNaN((a * b) + c);
+ return internal::CanonicalizeNaN(fma(a, b, c));
});
}
void RiscVFMsub(const Instruction *instruction) {
using T = float;
- RiscVTernaryFloatNaNBoxOp<FPRegister::ValueType, T, T>(
+ RVCheriotTernaryFloatNaNBoxOp<FPRegister::ValueType, T, T>(
instruction, [instruction](T a, T b, T c) -> T {
- if (FPTypeInfo<T>::IsNaN(a)) return internal::CanonicalizeNaN(a);
- if (FPTypeInfo<T>::IsNaN(b)) return internal::CanonicalizeNaN(b);
if ((std::isinf(a) && (b == 0.0)) || ((std::isinf(b) && (a == 0.0)))) {
auto *flag_db = instruction->Destination(1)->AllocateDataBuffer();
flag_db->Set<uint32_t>(0, *FPExceptions::kInvalidOp);
flag_db->Submit();
}
- if (FPTypeInfo<T>::IsNaN(c)) return internal::CanonicalizeNaN(c);
- if (std::isinf(c) && !std::isinf(a) && !std::isinf(b)) return -c;
- if (c == 0.0) {
- if ((a == 0.0 && !std::isinf(b)) || (b == 0.0 && !std::isinf(a))) {
- FPUInt c_sign = -*reinterpret_cast<FPUInt *>(&c) >>
- (FPTypeInfo<T>::kBitSize - 1);
- FPUInt ua = *reinterpret_cast<FPUInt *>(&a);
- FPUInt ub = *reinterpret_cast<FPUInt *>(&b);
- FPUInt prod_sign = (ua ^ ub) >> (FPTypeInfo<T>::kBitSize - 1);
- if (prod_sign == c_sign) return 0.0;
- return -c;
- }
- return internal::CanonicalizeNaN(a * b);
- }
- return internal::CanonicalizeNaN((a * b) - c);
+ return internal::CanonicalizeNaN(fma(a, b, -c));
});
}
void RiscVFNmadd(const Instruction *instruction) {
using T = float;
- RiscVTernaryFloatNaNBoxOp<FPRegister::ValueType, T, T>(
+ RVCheriotTernaryFloatNaNBoxOp<FPRegister::ValueType, T, T>(
instruction, [instruction](T a, T b, T c) -> T {
- if (FPTypeInfo<T>::IsNaN(a)) return internal::CanonicalizeNaN(a);
- if (FPTypeInfo<T>::IsNaN(b)) return internal::CanonicalizeNaN(b);
if ((std::isinf(a) && (b == 0.0)) || ((std::isinf(b) && (a == 0.0)))) {
auto *flag_db = instruction->Destination(1)->AllocateDataBuffer();
flag_db->Set<uint32_t>(0, *FPExceptions::kInvalidOp);
flag_db->Submit();
}
- if (FPTypeInfo<T>::IsNaN(c)) return internal::CanonicalizeNaN(c);
- if (std::isinf(c) && !std::isinf(a) && !std::isinf(b)) return -c;
- if (c == 0.0) {
- if ((a == 0.0 && !std::isinf(b)) || (b == 0.0 && !std::isinf(a))) {
- FPUInt c_sign = *reinterpret_cast<FPUInt *>(&c) >>
- (FPTypeInfo<T>::kBitSize - 1);
- FPUInt ua = *reinterpret_cast<FPUInt *>(&a);
- FPUInt ub = *reinterpret_cast<FPUInt *>(&b);
- FPUInt prod_sign = (ua ^ ub) >> (FPTypeInfo<T>::kBitSize - 1);
- if (prod_sign != c_sign) return 0.0;
- return -c;
- }
- return internal::CanonicalizeNaN(-a * b);
- }
- return internal::CanonicalizeNaN(-((a * b) + c));
+ return internal::CanonicalizeNaN(fma(-a, b, -c));
});
}
void RiscVFNmsub(const Instruction *instruction) {
using T = float;
- RiscVTernaryFloatNaNBoxOp<FPRegister::ValueType, T, T>(
+ RVCheriotTernaryFloatNaNBoxOp<FPRegister::ValueType, T, T>(
instruction, [instruction](T a, T b, T c) -> T {
- if (FPTypeInfo<T>::IsNaN(a)) return internal::CanonicalizeNaN(a);
- if (FPTypeInfo<T>::IsNaN(b)) return internal::CanonicalizeNaN(b);
if ((std::isinf(a) && (b == 0.0)) || ((std::isinf(b) && (a == 0.0)))) {
auto *flag_db = instruction->Destination(1)->AllocateDataBuffer();
flag_db->Set<uint32_t>(0, *FPExceptions::kInvalidOp);
flag_db->Submit();
}
- if (FPTypeInfo<T>::IsNaN(c)) return internal::CanonicalizeNaN(c);
- if (std::isinf(c) && !std::isinf(a) && !std::isinf(b)) return c;
- if (c == 0.0) {
- if ((a == 0.0 && !std::isinf(b)) || (b == 0.0 && !std::isinf(a))) {
- FPUInt c_sign = -*reinterpret_cast<FPUInt *>(&c) >>
- (FPTypeInfo<T>::kBitSize - 1);
- FPUInt ua = *reinterpret_cast<FPUInt *>(&a);
- FPUInt ub = *reinterpret_cast<FPUInt *>(&b);
- FPUInt prod_sign = (ua ^ ub) >> (FPTypeInfo<T>::kBitSize - 1);
- if (prod_sign != c_sign) return 0.0;
- return c;
- }
- return internal::CanonicalizeNaN(-a * b);
- }
- return internal::CanonicalizeNaN(-((a * b) - c));
+ return internal::CanonicalizeNaN(fma(-a, b, c));
});
}
// Set sign of the first operand to that of the second.
void RiscVFSgnj(const Instruction *instruction) {
- RiscVBinaryNaNBoxOp<FPRegister::ValueType, FPUInt, FPUInt>(
+ RVCheriotBinaryNaNBoxOp<FPRegister::ValueType, FPUInt, FPUInt>(
instruction,
[](FPUInt a, FPUInt b) { return (a & 0x7fff'ffff) | (b & 0x8000'0000); });
}
// Set the sign of the first operand to the opposite of the second.
void RiscVFSgnjn(const Instruction *instruction) {
- RiscVBinaryNaNBoxOp<FPRegister::ValueType, FPUInt, FPUInt>(
+ RVCheriotBinaryNaNBoxOp<FPRegister::ValueType, FPUInt, FPUInt>(
instruction, [](FPUInt a, FPUInt b) {
return (a & 0x7fff'ffff) | (~b & 0x8000'0000);
});
@@ -372,7 +306,7 @@
// Set the sign of the first operand to the xor of the signs of the two
// operands.
void RiscVFSgnjx(const Instruction *instruction) {
- RiscVBinaryNaNBoxOp<FPRegister::ValueType, FPUInt, FPUInt>(
+ RVCheriotBinaryNaNBoxOp<FPRegister::ValueType, FPUInt, FPUInt>(
instruction, [](FPUInt a, FPUInt b) {
return (a & 0x7fff'ffff) | ((a ^ b) & 0x8000'0000);
});
@@ -380,22 +314,24 @@
// Convert signed 32 bit integer to float.
void RiscVFCvtSw(const Instruction *instruction) {
- RiscVUnaryFloatNaNBoxOp<FPRegister::ValueType, uint32_t, float, int32_t>(
+ RVCheriotUnaryFloatNaNBoxOp<FPRegister::ValueType, uint32_t, float, int32_t>(
instruction, [](int32_t a) -> float { return static_cast<float>(a); });
}
// Convert unsigned 32 bit integer to float.
void RiscVFCvtSwu(const Instruction *instruction) {
- RiscVUnaryFloatNaNBoxOp<FPRegister::ValueType, uint32_t, float, uint32_t>(
+ RVCheriotUnaryFloatNaNBoxOp<FPRegister::ValueType, uint32_t, float, uint32_t>(
instruction, [](uint32_t a) -> float { return static_cast<float>(a); });
}
// Single precision move instruction from integer to fp register file.
void RiscVFMvwx(const Instruction *instruction) {
- RiscVUnaryNaNBoxOp<FPRegister::ValueType, uint32_t, uint32_t, uint32_t>(
+ RVCheriotUnaryNaNBoxOp<FPRegister::ValueType, uint32_t, uint32_t, uint32_t>(
instruction, [](uint32_t a) -> uint32_t { return a; });
}
+namespace RV32 {
+
using XRegister = CheriotRegister;
using XUint = typename std::make_unsigned<XRegister::ValueType>::type;
using XInt = typename std::make_signed<XRegister::ValueType>::type;
@@ -447,6 +383,8 @@
[](float a) -> uint32_t { return static_cast<uint32_t>(ClassifyFP(a)); });
}
+} // namespace RV32
+
} // namespace cheriot
} // namespace sim
} // namespace mpact
diff --git a/cheriot/riscv_cheriot_f_instructions.h b/cheriot/riscv_cheriot_f_instructions.h
index 12c30f9..b7a680f 100644
--- a/cheriot/riscv_cheriot_f_instructions.h
+++ b/cheriot/riscv_cheriot_f_instructions.h
@@ -65,6 +65,8 @@
// The move instruction takes a single register source operand and a single
void RiscVFMvwx(const Instruction *instruction);
+namespace RV32 {
+
// Store float instruction semantic function, source operand 0 is the base
// register, source operand 1 is the offset, while source operand 2 is the value
// to be stored referred to by rs2.
@@ -86,6 +88,8 @@
// and a single destination register operand.
void RiscVFClass(const Instruction *instruction);
+} // namespace RV32
+
} // namespace cheriot
} // namespace sim
} // namespace mpact
diff --git a/cheriot/riscv_cheriot_fp_state.h b/cheriot/riscv_cheriot_fp_state.h
deleted file mode 100644
index 138e139..0000000
--- a/cheriot/riscv_cheriot_fp_state.h
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright 2024 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef MPACT_CHERIOT__RISCV_CHERIOT_FP_STATE_H_
-#define MPACT_CHERIOT__RISCV_CHERIOT_FP_STATE_H_
-
-#include <cstdint>
-
-#include "riscv//riscv_csr.h"
-#include "riscv//riscv_fp_host.h"
-#include "riscv//riscv_fp_info.h"
-
-// This file contains code that manages the fp state of the RiscV processor.
-
-namespace mpact {
-namespace sim {
-namespace cheriot {
-
-class RiscVCheriotFPState;
-class CheriotState;
-
-using ::mpact::sim::riscv::FPRoundingMode;
-using ::mpact::sim::riscv::HostFloatingPointInterface;
-using ::mpact::sim::riscv::RiscVSimpleCsr;
-
-// Floating point CSR.
-class RiscVFcsr : public RiscVSimpleCsr<uint32_t> {
- public:
- RiscVFcsr() = delete;
- explicit RiscVFcsr(RiscVCheriotFPState *fp_state);
- ~RiscVFcsr() override = default;
-
- // Overrides.
- uint32_t AsUint32() override;
- uint64_t AsUint64() override;
- void Write(uint32_t value) override;
- void Write(uint64_t value) override;
-
- private:
- RiscVCheriotFPState *fp_state_;
-};
-
-// Floating point rounding mode csr.
-class RiscVFrm : public RiscVSimpleCsr<uint32_t> {
- public:
- RiscVFrm() = delete;
- explicit RiscVFrm(RiscVCheriotFPState *fp_state);
- ~RiscVFrm() override = default;
-
- // Overrides.
- uint32_t AsUint32() override;
- uint64_t AsUint64() override { return AsUint32(); }
- void Write(uint32_t value) override;
- void Write(uint64_t value) override { Write(static_cast<uint32_t>(value)); }
- uint32_t GetUint32() override;
- uint64_t GetUint64() override { return GetUint32(); }
- void Set(uint32_t value) override;
- void Set(uint64_t value) override { Set(static_cast<uint32_t>(value)); }
-
- private:
- RiscVCheriotFPState *fp_state_;
-};
-
-// Floating point status flags csr.
-class RiscVFflags : public RiscVSimpleCsr<uint32_t> {
- public:
- RiscVFflags() = delete;
- explicit RiscVFflags(RiscVCheriotFPState *fp_state);
- ~RiscVFflags() override = default;
-
- // Overrides.
- uint32_t AsUint32() override;
- uint64_t AsUint64() override { return AsUint32(); }
- void Write(uint32_t value) override;
- void Write(uint64_t value) override { Write(static_cast<uint32_t>(value)); }
- uint32_t GetUint32() override;
- uint64_t GetUint64() override { return GetUint32(); }
- void Set(uint32_t value) override;
- void Set(uint64_t value) override { Set(static_cast<uint32_t>(value)); }
-
- private:
- RiscVCheriotFPState *fp_state_;
-};
-
-class RiscVCheriotFPState {
- public:
- RiscVCheriotFPState() = delete;
- RiscVCheriotFPState(const RiscVCheriotFPState &) = delete;
- explicit RiscVCheriotFPState(CheriotState *rv_state);
- ~RiscVCheriotFPState();
-
- FPRoundingMode GetRoundingMode() const;
-
- void SetRoundingMode(FPRoundingMode mode);
-
- bool rounding_mode_valid() const { return rounding_mode_valid_; }
-
- // FP CSRs.
- RiscVFcsr *fcsr() const { return fcsr_; }
- RiscVFrm *frm() const { return frm_; }
- RiscVFflags *fflags() const { return fflags_; }
- // Parent state.
- CheriotState *rv_state() const { return rv_state_; }
- // Host interface.
- HostFloatingPointInterface *host_fp_interface() const {
- return host_fp_interface_;
- }
-
- private:
- CheriotState *rv_state_;
- RiscVFcsr *fcsr_ = nullptr;
- RiscVFrm *frm_ = nullptr;
- RiscVFflags *fflags_ = nullptr;
- HostFloatingPointInterface *host_fp_interface_;
-
- bool rounding_mode_valid_ = true;
- FPRoundingMode rounding_mode_ = FPRoundingMode::kRoundToNearest;
-};
-
-} // namespace cheriot
-} // namespace sim
-} // namespace mpact
-
-#endif // MPACT_CHERIOT__RISCV_CHERIOT_FP_STATE_H_
diff --git a/cheriot/riscv_cheriot_instruction_helpers.h b/cheriot/riscv_cheriot_instruction_helpers.h
index ee5af8d..28fd75a 100644
--- a/cheriot/riscv_cheriot_instruction_helpers.h
+++ b/cheriot/riscv_cheriot_instruction_helpers.h
@@ -33,6 +33,9 @@
#include "mpact/sim/generic/operand_interface.h"
#include "mpact/sim/generic/register.h"
#include "mpact/sim/generic/type_helpers.h"
+#include "riscv//riscv_fp_host.h"
+#include "riscv//riscv_fp_info.h"
+#include "riscv//riscv_fp_state.h"
#include "riscv//riscv_state.h"
namespace mpact {
@@ -41,7 +44,12 @@
using ::mpact::sim::generic::Instruction;
using ::mpact::sim::generic::operator*;
+using ::mpact::sim::generic::FPTypeInfo;
using ::mpact::sim::generic::RegisterBase;
+using ::mpact::sim::riscv::FPExceptions;
+using ::mpact::sim::riscv::FPRoundingMode;
+using ::mpact::sim::riscv::ScopedFPStatus;
+
using CapReg = CheriotRegister;
using PB = ::mpact::sim::cheriot::CheriotRegister::PermissionBits;
@@ -62,6 +70,256 @@
cap_reg->set_is_null();
}
+// Templated helper function for convert instruction semantic functions.
+template <typename From, typename To>
+inline std::tuple<To, uint32_t> CvtHelper(From value) {
+ constexpr From kMax = static_cast<From>(std::numeric_limits<To>::max());
+ constexpr From kMin = static_cast<From>(std::numeric_limits<To>::min());
+
+ if (FPTypeInfo<From>::IsNaN(value)) {
+ return std::make_tuple(std::numeric_limits<To>::max(),
+ *FPExceptions::kInvalidOp);
+ }
+ if (value > kMax) {
+ return std::make_tuple(std::numeric_limits<To>::max(),
+ *FPExceptions::kInvalidOp);
+ }
+ if (value < kMin) {
+ if (std::is_unsigned<To>::value && (value > -1.0)) {
+ using SignedTo = typename std::make_signed<To>::type;
+ SignedTo signed_val = static_cast<SignedTo>(value);
+ if (signed_val == 0) {
+ return std::make_tuple(0, *FPExceptions::kInexact);
+ }
+ }
+ return std::make_tuple(std::numeric_limits<To>::min(),
+ *FPExceptions::kInvalidOp);
+ }
+
+ auto output_value = static_cast<To>(value);
+ return std::make_tuple(output_value, 0);
+}
+
+// Generic helper function for floating op instructions that do not require
+// NaN boxing since they produce non fp-values, but set fflags.
+template <typename Result, typename From, typename To>
+inline void RVCheriotConvertFloatWithFflagsOp(const Instruction *instruction) {
+ constexpr To kMax = std::numeric_limits<To>::max();
+ constexpr To kMin = std::numeric_limits<To>::min();
+
+ From lhs = generic::GetInstructionSource<From>(instruction, 0);
+
+ uint32_t flags = 0;
+ uint32_t rm = generic::GetInstructionSource<uint32_t>(instruction, 1);
+ // Dynamic rounding mode will get rounding mode from the global state.
+ if (rm == *FPRoundingMode::kDynamic) {
+ auto *rv_fp = static_cast<CheriotState *>(instruction->state())->rv_fp();
+ if (!rv_fp->rounding_mode_valid()) {
+ LOG(ERROR) << "Invalid rounding mode";
+ return;
+ }
+ rm = *rv_fp->GetRoundingMode();
+ }
+ To value = 0;
+ if (FPTypeInfo<From>::IsNaN(lhs)) {
+ value = std::numeric_limits<To>::max();
+ flags = *FPExceptions::kInvalidOp;
+ } else if (lhs == 0.0) {
+ value = 0;
+ } else {
+ // static_cast<>() doesn't necessarily round, so will have to force
+ // rounding before converting to the integer type if necessary.
+ using FromUint = typename FPTypeInfo<From>::UIntType;
+ auto constexpr kBias = FPTypeInfo<From>::kExpBias;
+ auto constexpr kExpMask = FPTypeInfo<From>::kExpMask;
+ auto constexpr kSigSize = FPTypeInfo<From>::kSigSize;
+ auto constexpr kSigMask = FPTypeInfo<From>::kSigMask;
+ auto constexpr kBitSize = FPTypeInfo<From>::kBitSize;
+ FromUint lhs_u = *reinterpret_cast<FromUint *>(&lhs);
+ const bool sign = (lhs_u & (1ULL << (kBitSize - 1))) != 0;
+ FromUint exp = kExpMask & lhs_u;
+ int exp_value = exp >> kSigSize;
+ int unbiased_exp = exp_value - kBias;
+ FromUint sig = kSigMask & lhs_u;
+
+ // Get fraction part of the number, and right shift it to leave 1 bits
+ // length of fraction part.
+ // In forms of "<integer_value>.<fraction>"", where fraction is 2 bits.
+ // e.g., 1.75 -> 0b1.11
+ // (float32) -> base = 0b110'0000'0000'0000'0000'0000'(23bits)
+ // exp = 127 (unbiased exp = 0)
+ // After right shift to leave 1 bit in fraction part.
+ // base = 0b1
+ // rightshift_compressed = 1 (compress the right-shift eliminated number)
+ int right_shift = exp ? kSigSize - 1 - unbiased_exp : 1;
+ uint64_t base = sig;
+ bool rightshift_compressed = 0;
+ if (exp == 0) {
+ // Denormalized value.
+ // Format: (-1)^sign * 2 ^(exp - bias + 1) * 0.{sig}
+ // fraction part is too small keep it as 1 bits if not zero.
+ rightshift_compressed = base != 0;
+ base = 0;
+ flags = *FPExceptions::kInexact;
+ } else {
+ // Normalized value.
+ // Format: (-1)^sign * 2 ^(exp - bias) * 1.{sig
+ // base = 1.{sig} (total (1 + `kSigSize`) bits)
+ base |= 1ULL << kSigSize;
+
+ // Right shift all base part out.
+ if (right_shift > (kBitSize - 1)) {
+ rightshift_compressed = base != 0;
+ flags = *FPExceptions::kInexact;
+ base = 0;
+ } else if (right_shift > 0) {
+ // Right shift to leave only 1 bit in the fraction part, compressed the
+ // right-shift eliminated number.
+ right_shift = std::min(right_shift, kBitSize);
+ uint64_t right_shifted_sig_mask = (1ULL << right_shift) - 1;
+ rightshift_compressed = (base & right_shifted_sig_mask) != 0;
+ base >>= right_shift;
+ }
+ }
+
+ // Handle fraction part rounding.
+ if (right_shift >= 0) {
+ switch (rm) {
+ case *FPRoundingMode::kRoundToNearest:
+ // 0.5, tie condition
+ if (rightshift_compressed == 0 && base & 0b1) {
+ // <odd>.5 -> <odd>.5 + 0.5 = even
+ if ((base & 0b11) == 0b11) {
+ flags = *FPExceptions::kInexact;
+ base += 0b01;
+ }
+ } else if (base & 0b1 || rightshift_compressed) {
+ // not tie condition, round to nearest integer, it equals to add
+ // 0.5(=base + 0b01) and eliminate the fraction part.
+ base += 0b01;
+ }
+ break;
+ case *FPRoundingMode::kRoundTowardsZero:
+ // Round towards zero will eliminate the fraction part.
+ // Do nothing on fraction part.
+ // 1.2 -> 1.0, -1.5 -> -1.0, -0.7 -> 0.0
+ break;
+ case *FPRoundingMode::kRoundDown:
+ // Positive float will eliminate the fraction part.
+ // Negative float with fraction part will subtract 1(= base + 0b10),
+ // and eliminate the fraction part.
+ // e.g., 1.2 -> 1.0, -1.5 -> -2.0, -0.7 -> -1.0
+ if (sign && (base & 0b1 || rightshift_compressed)) {
+ base += 0b10;
+ }
+ break;
+ case *FPRoundingMode::kRoundUp:
+ // Positive float will add 1(= base + 0b10), and eliminate the
+ // fraction part.
+ // Negative float will eliminate the fraction part.
+ // e.g., 1.2 -> 2.0, -1.5 -> -1.0, -0.7 -> 0.0
+ if (!sign && (base & 0b1 || rightshift_compressed)) {
+ base += 0b10;
+ }
+ break;
+ case *FPRoundingMode::kRoundToNearestTiesToMax:
+ // Round to nearest integer that is far from zero.
+ // e.g., 1.2 -> 2.0, -1.5 -> -2.0, -0.7 -> -1.0
+ if (base & 0b1 || rightshift_compressed) {
+ base += 0b1;
+ }
+ break;
+ default:
+ LOG(ERROR) << "Invalid rounding mode";
+ return;
+ }
+ }
+ uint64_t unsigned_value;
+ // Handle base with fraction part and store it to `unsigned_value`.
+ if (right_shift >= 0) {
+ // Set inexact flag if floating value has fraction part.
+ if (base & 0b1 || rightshift_compressed) {
+ flags = *FPExceptions::kInexact;
+ }
+ unsigned_value = base >> 1;
+ } else {
+ // Handle base without fraction part but need to left shift.
+ int left_shift = -right_shift - 1;
+ auto prev = unsigned_value = base;
+ while (left_shift) {
+ unsigned_value <<= 1;
+ // Check if overflow happened and set the flag.
+ if (prev > unsigned_value) {
+ flags = *FPExceptions::kInvalidOp;
+ unsigned_value = sign ? kMin : kMax;
+ break;
+ }
+ prev = unsigned_value;
+ --left_shift;
+ }
+ }
+
+ // Handle the case that value is out of range, and final convert to value
+ // with sign.
+ if (std::is_signed<To>::value) {
+ // Positive value but exceeds the max value.
+ if (!sign && unsigned_value > kMax) {
+ flags = *FPExceptions::kInvalidOp;
+ value = kMax;
+ } else if (sign && (unsigned_value > 0 && -unsigned_value < kMin)) {
+ // Negative value but exceeds the min value.
+ flags = *FPExceptions::kInvalidOp;
+ value = kMin;
+ } else {
+ value = sign ? -((To)unsigned_value) : unsigned_value;
+ }
+ } else {
+ // Positive value but exceeds the max value.
+ if (unsigned_value > kMax) {
+ flags = *FPExceptions::kInvalidOp;
+ value = sign ? kMin : kMax;
+ } else if (sign && unsigned_value != 0) {
+ // float is negative value this is out of range of valid unsigned value.
+ flags = *FPExceptions::kInvalidOp;
+ value = kMin;
+ } else {
+ value = sign ? -((To)unsigned_value) : unsigned_value;
+ }
+ }
+ }
+ using SignedTo = typename std::make_signed<To>::type;
+ // The final value is sign-extended to the register width, even if it's
+ // conversion to an unsigned value.
+ SignedTo signed_value = static_cast<SignedTo>(value);
+ Result dest_value = static_cast<Result>(signed_value);
+ WriteCapIntResult(instruction, 0, dest_value);
+ if (flags) {
+ auto *flag_db = instruction->Destination(1)->AllocateDataBuffer();
+ flag_db->Set<uint32_t>(0, flags);
+ flag_db->Submit();
+ }
+}
+
+// Helper function to read a NaN boxed source value, converting it to NaN if
+// it isn't formatted properly.
+template <typename RegValue, typename Argument>
+inline Argument GetNaNBoxedSource(const Instruction *instruction, int arg) {
+ if (sizeof(RegValue) <= sizeof(Argument)) {
+ return generic::GetInstructionSource<Argument>(instruction, arg);
+ } else {
+ using SInt = typename std::make_signed<RegValue>::type;
+ using UInt = typename std::make_unsigned<RegValue>::type;
+ SInt val = generic::GetInstructionSource<SInt>(instruction, arg);
+ UInt uval = static_cast<UInt>(val);
+ UInt mask = std::numeric_limits<UInt>::max() << (sizeof(Argument) * 8);
+ if (((mask & uval) != mask)) {
+ return *reinterpret_cast<const Argument *>(
+ &FPTypeInfo<Argument>::kCanonicalNaN);
+ }
+ return generic::GetInstructionSource<Argument>(instruction, arg);
+ }
+}
+
template <typename Register, typename Result, typename Argument>
inline void RiscVBinaryOp(const Instruction *instruction,
std::function<Result(Argument, Argument)> operation) {
@@ -255,6 +513,304 @@
db->DecRef();
}
+// Generic helper function for binary instructions with NaN boxing. This is
+// used for those instructions that produce results in fp registers, but are
+// not really executing an fp operation that requires rounding.
+template <typename RegValue, typename Result, typename Argument>
+inline void RVCheriotBinaryNaNBoxOp(
+ const Instruction *instruction,
+ std::function<Result(Argument, Argument)> operation) {
+ Argument lhs = GetNaNBoxedSource<RegValue, Argument>(instruction, 0);
+ Argument rhs = GetNaNBoxedSource<RegValue, Argument>(instruction, 1);
+ Result dest_value = operation(lhs, rhs);
+ auto *reg = static_cast<generic::RegisterDestinationOperand<RegValue> *>(
+ instruction->Destination(0))
+ ->GetRegister();
+ // Check to see if we need to NaN box the result.
+ if (sizeof(RegValue) > sizeof(Result)) {
+ // If the floating point value is narrower than the register, the upper
+ // bits have to be set to all ones.
+ using UReg = typename std::make_unsigned<RegValue>::type;
+ using UInt = typename FPTypeInfo<Result>::UIntType;
+ auto dest_u_value = *reinterpret_cast<UInt *>(&dest_value);
+ UReg reg_value = std::numeric_limits<UReg>::max();
+ int shift = 8 * (sizeof(RegValue) - sizeof(Result));
+ reg_value = (reg_value << shift) | dest_u_value;
+ reg->data_buffer()->template Set<RegValue>(0, reg_value);
+ return;
+ }
+ reg->data_buffer()->template Set<Result>(0, dest_value);
+}
+
+// Generic helper function for unary instructions with NaN boxing.
+template <typename DstRegValue, typename SrcRegValue, typename Result,
+ typename Argument>
+inline void RVCheriotUnaryNaNBoxOp(const Instruction *instruction,
+ std::function<Result(Argument)> operation) {
+ Argument lhs = GetNaNBoxedSource<SrcRegValue, Argument>(instruction, 0);
+ Result dest_value = operation(lhs);
+ auto *reg = static_cast<generic::RegisterDestinationOperand<DstRegValue> *>(
+ instruction->Destination(0))
+ ->GetRegister();
+ // Check to see if we need to NaN box the result.
+ if (sizeof(DstRegValue) > sizeof(Result)) {
+ // If the floating point value is narrower than the register, the upper
+ // bits have to be set to all ones.
+ using UReg = typename std::make_unsigned<DstRegValue>::type;
+ using UInt = typename FPTypeInfo<Result>::UIntType;
+ auto dest_u_value = *reinterpret_cast<UInt *>(&dest_value);
+ UReg reg_value = std::numeric_limits<UReg>::max();
+ int shift = 8 * (sizeof(DstRegValue) - sizeof(Result));
+ reg_value = (reg_value << shift) | dest_u_value;
+ WriteCapIntResult(instruction, 0, reg_value);
+ reg->data_buffer()->template Set<DstRegValue>(0, reg_value);
+ return;
+ }
+ reg->data_buffer()->template Set<Result>(0, dest_value);
+}
+
+// Generic helper function for unary floating point instructions. The main
+// difference is that it handles rounding mode and performs NaN boxing.
+template <typename DstRegValue, typename SrcRegValue, typename Result,
+ typename Argument>
+inline void RVCheriotUnaryFloatNaNBoxOp(
+ const Instruction *instruction, std::function<Result(Argument)> operation) {
+ using ResUint = typename FPTypeInfo<Result>::UIntType;
+ Argument lhs = GetNaNBoxedSource<SrcRegValue, Argument>(instruction, 0);
+ // Get the rounding mode.
+ int rm_value = generic::GetInstructionSource<int>(instruction, 1);
+
+ // If the rounding mode is dynamic, read it from the current state.
+ auto *rv_fp = static_cast<CheriotState *>(instruction->state())->rv_fp();
+ if (rm_value == *FPRoundingMode::kDynamic) {
+ if (!rv_fp->rounding_mode_valid()) {
+ LOG(ERROR) << "Invalid rounding mode";
+ return;
+ }
+ rm_value = *(rv_fp->GetRoundingMode());
+ }
+ Result dest_value;
+ {
+ ScopedFPStatus set_fp_status(rv_fp->host_fp_interface(), rm_value);
+ dest_value = operation(lhs);
+ }
+ if (std::isnan(dest_value) && std::signbit(dest_value)) {
+ ResUint res_value = *reinterpret_cast<ResUint *>(&dest_value);
+ res_value &= FPTypeInfo<Result>::kInfMask;
+ dest_value = *reinterpret_cast<Result *>(&res_value);
+ }
+ auto *dest = instruction->Destination(0);
+ auto *reg_dest =
+ static_cast<generic::RegisterDestinationOperand<DstRegValue> *>(dest);
+ auto *reg = reg_dest->GetRegister();
+ // Check to see if we need to NaN box the result.
+ if (sizeof(DstRegValue) > sizeof(Result)) {
+ // If the floating point Value is narrower than the register, the upper
+ // bits have to be set to all ones.
+ using UReg = typename std::make_unsigned<DstRegValue>::type;
+ using UInt = typename FPTypeInfo<Result>::UIntType;
+ auto dest_u_value = *reinterpret_cast<UInt *>(&dest_value);
+ UReg reg_value = std::numeric_limits<UReg>::max();
+ int shift = 8 * (sizeof(DstRegValue) - sizeof(Result));
+ reg_value = (reg_value << shift) | dest_u_value;
+ reg->data_buffer()->template Set<DstRegValue>(0, reg_value);
+ return;
+ }
+ reg->data_buffer()->template Set<Result>(0, dest_value);
+}
+
+// Generic helper function for floating op instructions that do not require
+// NaN boxing since they produce non fp-values.
+template <typename Result, typename Argument>
+inline void RVCheriotUnaryFloatOp(const Instruction *instruction,
+ std::function<Result(Argument)> operation) {
+ Argument lhs = generic::GetInstructionSource<Argument>(instruction, 0);
+ // Get the rounding mode.
+ int rm_value = generic::GetInstructionSource<int>(instruction, 1);
+
+ auto *rv_fp = static_cast<CheriotState *>(instruction->state())->rv_fp();
+ // If the rounding mode is dynamic, read it from the current state.
+ if (rm_value == *FPRoundingMode::kDynamic) {
+ if (!rv_fp->rounding_mode_valid()) {
+ LOG(ERROR) << "Invalid rounding mode";
+ return;
+ }
+ rm_value = *rv_fp->GetRoundingMode();
+ }
+ Result dest_value;
+ {
+ ScopedFPStatus set_fp_status(rv_fp->host_fp_interface(), rm_value);
+ dest_value = operation(lhs);
+ }
+ auto *dest = instruction->Destination(0);
+ using UInt = typename FPTypeInfo<Result>::UIntType;
+ auto *reg_dest =
+ static_cast<generic::RegisterDestinationOperand<UInt> *>(dest);
+ auto *reg = reg_dest->GetRegister();
+ reg->data_buffer()->template Set<Result>(0, dest_value);
+}
+
+// Generic helper function for floating op instructions that do not require
+// NaN boxing since they produce non fp-values, but set fflags.
+template <typename Result, typename Argument>
+inline void RVCheriotUnaryFloatWithFflagsOp(
+ const Instruction *instruction,
+ std::function<Result(Argument, uint32_t &)> operation) {
+ Argument lhs = generic::GetInstructionSource<Argument>(instruction, 0);
+ // Get the rounding mode.
+ int rm_value = generic::GetInstructionSource<int>(instruction, 1);
+
+ auto *rv_fp = static_cast<CheriotState *>(instruction->state())->rv_fp();
+ // If the rounding mode is dynamic, read it from the current state.
+ if (rm_value == *FPRoundingMode::kDynamic) {
+ if (!rv_fp->rounding_mode_valid()) {
+ LOG(ERROR) << "Invalid rounding mode";
+ return;
+ }
+ rm_value = *rv_fp->GetRoundingMode();
+ }
+ uint32_t flag = 0;
+ Result dest_value;
+ {
+ ScopedFPStatus set_fp_status(rv_fp->host_fp_interface(), rm_value);
+ dest_value = operation(lhs, flag);
+ }
+ auto *dest = instruction->Destination(0);
+ using UInt = typename FPTypeInfo<Result>::UIntType;
+ auto *reg_dest =
+ static_cast<generic::RegisterDestinationOperand<UInt> *>(dest);
+ auto *reg = reg_dest->GetRegister();
+ reg->data_buffer()->template Set<Result>(0, dest_value);
+ auto *flag_db = instruction->Destination(1)->AllocateDataBuffer();
+ flag_db->Set<uint32_t>(0, flag);
+ flag_db->Submit();
+}
+
+// Generic helper function for binary floating point instructions. The main
+// difference is that it handles rounding mode.
+template <typename Register, typename Result, typename Argument>
+inline void RVCheriotBinaryFloatNaNBoxOp(
+ const Instruction *instruction,
+ std::function<Result(Argument, Argument)> operation) {
+ Argument lhs = GetNaNBoxedSource<Register, Argument>(instruction, 0);
+ Argument rhs = GetNaNBoxedSource<Register, Argument>(instruction, 1);
+ // Argument lhs = generic::GetInstructionSource<Argument>(instruction, 0);
+ // Argument rhs = generic::GetInstructionSource<Argument>(instruction, 1);
+
+ // Get the rounding mode.
+ int rm_value = generic::GetInstructionSource<int>(instruction, 2);
+
+ auto *rv_fp = static_cast<CheriotState *>(instruction->state())->rv_fp();
+ // If the rounding mode is dynamic, read it from the current state.
+ if (rm_value == *FPRoundingMode::kDynamic) {
+ if (!rv_fp->rounding_mode_valid()) {
+ LOG(ERROR) << "Invalid rounding mode";
+ return;
+ }
+ rm_value = *rv_fp->GetRoundingMode();
+ }
+ Result dest_value;
+ {
+ ScopedFPStatus fp_status(rv_fp->host_fp_interface(), rm_value);
+ dest_value = operation(lhs, rhs);
+ }
+ if (std::isnan(dest_value)) {
+ *reinterpret_cast<typename FPTypeInfo<Result>::UIntType *>(&dest_value) =
+ FPTypeInfo<Result>::kCanonicalNaN;
+ }
+ auto *reg = static_cast<generic::RegisterDestinationOperand<Register> *>(
+ instruction->Destination(0))
+ ->GetRegister();
+ // Check to see if we need to NaN box the result.
+ if (sizeof(Register) > sizeof(Result)) {
+ // If the floating point value is narrower than the register, the upper
+ // bits have to be set to all ones.
+ using UReg = typename std::make_unsigned<Register>::type;
+ using UInt = typename FPTypeInfo<Result>::UIntType;
+ auto dest_u_value = *reinterpret_cast<UInt *>(&dest_value);
+ UReg reg_value = std::numeric_limits<UReg>::max();
+ int shift = 8 * (sizeof(Register) - sizeof(Result));
+ reg_value = (reg_value << shift) | dest_u_value;
+ reg->data_buffer()->template Set<Register>(0, reg_value);
+ return;
+ }
+ reg->data_buffer()->template Set<Result>(0, dest_value);
+}
+
+// Generic helper function for ternary floating point instructions.
+template <typename Register, typename Result, typename Argument>
+inline void RVCheriotTernaryFloatNaNBoxOp(
+ const Instruction *instruction,
+ std::function<Result(Argument, Argument, Argument)> operation) {
+ Argument rs1 = generic::GetInstructionSource<Argument>(instruction, 0);
+ Argument rs2 = generic::GetInstructionSource<Argument>(instruction, 1);
+ Argument rs3 = generic::GetInstructionSource<Argument>(instruction, 2);
+ // Get the rounding mode.
+ int rm_value = generic::GetInstructionSource<int>(instruction, 3);
+
+ auto *rv_fp = static_cast<CheriotState *>(instruction->state())->rv_fp();
+ // If the rounding mode is dynamic, read it from the current state.
+ if (rm_value == *FPRoundingMode::kDynamic) {
+ if (!rv_fp->rounding_mode_valid()) {
+ LOG(ERROR) << "Invalid rounding mode";
+ return;
+ }
+ rm_value = *rv_fp->GetRoundingMode();
+ }
+ Result dest_value;
+ {
+ ScopedFPStatus fp_status(rv_fp->host_fp_interface(), rm_value);
+ dest_value = operation(rs1, rs2, rs3);
+ }
+ auto *reg = static_cast<generic::RegisterDestinationOperand<Register> *>(
+ instruction->Destination(0))
+ ->GetRegister();
+ // Check to see if we need to NaN box the result.
+ if (sizeof(Register) > sizeof(Result)) {
+ // If the floating point value is narrower than the register, the upper
+ // bits have to be set to all ones.
+ using UReg = typename std::make_unsigned<Register>::type;
+ using UInt = typename FPTypeInfo<Result>::UIntType;
+ auto dest_u_value = *reinterpret_cast<UInt *>(&dest_value);
+ UReg reg_value = std::numeric_limits<UReg>::max();
+ int shift = 8 * (sizeof(Register) - sizeof(Result));
+ reg_value = (reg_value << shift) | dest_u_value;
+ reg->data_buffer()->template Set<Register>(0, reg_value);
+ return;
+ }
+ reg->data_buffer()->template Set<Result>(0, dest_value);
+}
+
+// Helper function to classify floating point values.
+template <typename T>
+typename FPTypeInfo<T>::UIntType ClassifyFP(T val) {
+ using UIntType = typename FPTypeInfo<T>::UIntType;
+ auto int_value = *reinterpret_cast<UIntType *>(&val);
+ UIntType sign = int_value >> (FPTypeInfo<T>::kBitSize - 1);
+ UIntType exp_mask = (1 << FPTypeInfo<T>::kExpSize) - 1;
+ UIntType exp = (int_value >> FPTypeInfo<T>::kSigSize) & exp_mask;
+ UIntType sig =
+ int_value & ((static_cast<UIntType>(1) << FPTypeInfo<T>::kSigSize) - 1);
+ if (exp == 0) { // The number is denormal or zero.
+ if (sig == 0) { // The number is zero.
+ return sign ? 1 << 3 : 1 << 4;
+ } else { // subnormal.
+ return sign ? 1 << 2 : 1 << 5;
+ }
+ } else if (exp == exp_mask) { // The number is infinity or NaN.
+ if (sig == 0) { // infinity
+ return sign ? 1 : 1 << 7;
+ } else {
+ if ((sig >> (FPTypeInfo<T>::kSigSize - 1)) != 0) { // Quiet NaN.
+ return 1 << 9;
+ } else { // signaling NaN.
+ return 1 << 8;
+ }
+ }
+ }
+ return sign ? 1 << 1 : 1 << 6;
+}
+
} // namespace cheriot
} // namespace sim
} // namespace mpact
diff --git a/cheriot/riscv_cheriot_rvv.bin_fmt b/cheriot/riscv_cheriot_rvv.bin_fmt
new file mode 100644
index 0000000..757f4aa
--- /dev/null
+++ b/cheriot/riscv_cheriot_rvv.bin_fmt
@@ -0,0 +1,44 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Cheriot RVV FP binary decoder.
+decoder RiscVCheriotRVVFp {
+ namespace mpact::sim::cheriot::encoding_rvv_fp;
+ opcode_enum = "cheriot::isa32_rvv_fp::OpcodeEnum";
+ includes {
+ #include "cheriot/riscv_cheriot_rvv_fp_decoder.h"
+ }
+ // Group these instruction groups in the same decoder function.
+ RiscVCheriotRVVFPInst32 = {RiscVFInst32, RiscVVInst32, RiscVVFPInst32, RiscVCheriotInst32};
+ // Keep this separate (different base format).
+ RiscVCheriotRVVFPInst16 = {RiscVCheriotInst16};
+};
+
+// Cheriot RVV binary decoder.
+decoder RiscVCheriotRVV {
+ namespace mpact::sim::cheriot::encoding_rvv;
+ opcode_enum = "cheriot::isa32_rvv::OpcodeEnum";
+ includes {
+ #include "cheriot/riscv_cheriot_rvv_decoder.h"
+ }
+ // Group these instruction groups in the same decoder function.
+ RiscVCheriotRVVInst32 = {RiscVVInst32, RiscVCheriotInst32};
+ // Keep this separate (different base format).
+ RiscVCheriotRVVInst16 = {RiscVCheriotInst16};
+};
+
+#include "cheriot/riscv_cheriot.bin_fmt"
+#include "cheriot/riscv_cheriot_f.bin_fmt"
+#include "cheriot/riscv_cheriot_vector.bin_fmt"
+#include "cheriot/riscv_cheriot_vector_fp.bin_fmt"
\ No newline at end of file
diff --git a/cheriot/riscv_cheriot_rvv.isa b/cheriot/riscv_cheriot_rvv.isa
new file mode 100644
index 0000000..071e515
--- /dev/null
+++ b/cheriot/riscv_cheriot_rvv.isa
@@ -0,0 +1,48 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cheriot/riscv_cheriot_vector.isa"
+#include "cheriot/riscv_cheriot_vector_fp.isa"
+#include "cheriot/riscv_cheriot_f.isa"
+#include "cheriot/riscv_cheriot.isa"
+
+includes {
+ #include "absl/functional/bind_front.h"
+}
+
+disasm widths = {-18};
+
+isa RiscVCheriotRVV {
+ namespace mpact::sim::cheriot::isa32_rvv;
+ slots { riscv_cheriot_rvv; }
+}
+
+isa RiscVCheriotRVVFp {
+ namespace mpact::sim::cheriot::isa32_rvv_fp;
+ slots { riscv_cheriot_rvv_fp; }
+}
+
+slot riscv_cheriot_rvv : riscv32_cheriot, riscv_cheriot_vector {
+ default size = 4;
+ default opcode =
+ disasm: "Illegal instruction at 0x%(@:08x)",
+ semfunc: "&RiscVIllegalInstruction";
+}
+
+slot riscv_cheriot_rvv_fp : riscv32_cheriot, riscv_cheriot_vector, riscv_cheriot_vector_fp, riscv_cheriot_f {
+ default size = 4;
+ default opcode =
+ disasm: "Illegal instruction at 0x%(@:08x)",
+ semfunc: "&RiscVIllegalInstruction";
+}
\ No newline at end of file
diff --git a/cheriot/riscv_cheriot_rvv_encoding.cc b/cheriot/riscv_cheriot_rvv_encoding.cc
new file mode 100644
index 0000000..b1b3f3c
--- /dev/null
+++ b/cheriot/riscv_cheriot_rvv_encoding.cc
@@ -0,0 +1,111 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cheriot/riscv_cheriot_rvv_encoding.h"
+
+#include <cstdint>
+
+#include "absl/log/log.h"
+#include "absl/strings/str_cat.h"
+#include "cheriot/cheriot_getters.h"
+#include "cheriot/cheriot_register.h"
+#include "cheriot/cheriot_rvv_getters.h"
+#include "cheriot/cheriot_state.h"
+#include "cheriot/riscv_cheriot_encoding_common.h"
+#include "cheriot/riscv_cheriot_rvv_bin_decoder.h"
+#include "cheriot/riscv_cheriot_rvv_decoder.h"
+#include "cheriot/riscv_cheriot_rvv_enums.h"
+#include "mpact/sim/generic/type_helpers.h"
+
+namespace mpact {
+namespace sim {
+namespace cheriot {
+namespace isa32_rvv {
+
+using Extractors = ::mpact::sim::cheriot::encoding_rvv::Extractors;
+
+RiscVCheriotRVVEncoding::RiscVCheriotRVVEncoding(CheriotState *state)
+ : RiscVCheriotEncodingCommon(state) {
+ source_op_getters_.emplace(*SourceOpEnum::kNone, []() { return nullptr; });
+ dest_op_getters_.emplace(*DestOpEnum::kNone,
+ [](int latency) { return nullptr; });
+ // Add Cheriot ISA source and destination operand getters.
+ AddCheriotSourceGetters<SourceOpEnum, Extractors>(source_op_getters_, this);
+ AddCheriotDestGetters<DestOpEnum, Extractors>(dest_op_getters_, this);
+ // Add non-fp RVV source and destination operand getters.
+ AddCheriotRVVSourceGetters<SourceOpEnum, Extractors>(source_op_getters_,
+ this);
+ AddCheriotRVVDestGetters<DestOpEnum, Extractors>(dest_op_getters_, this);
+ // Verify that all source and destination op enum values have a getter.
+ for (int i = *SourceOpEnum::kNone; i < *SourceOpEnum::kPastMaxValue; ++i) {
+ if (source_op_getters_.find(i) == source_op_getters_.end()) {
+ LOG(ERROR) << "No getter for source op enum value " << i;
+ }
+ }
+ for (int i = *DestOpEnum::kNone; i < *DestOpEnum::kPastMaxValue; ++i) {
+ if (dest_op_getters_.find(i) == dest_op_getters_.end()) {
+ LOG(ERROR) << "No getter for destination op enum value " << i;
+ }
+ }
+}
+
+// Parse the instruction word to determine the opcode.
+void RiscVCheriotRVVEncoding::ParseInstruction(uint32_t inst_word) {
+ inst_word_ = inst_word;
+ if ((inst_word_ & 0x3) == 3) {
+ auto [opcode, format] = mpact::sim::cheriot::encoding_rvv::
+ DecodeRiscVCheriotRVVInst32WithFormat(inst_word_);
+ opcode_ = opcode;
+ format_ = format;
+ return;
+ }
+
+ auto [opcode, format] =
+ mpact::sim::cheriot::encoding_rvv::DecodeRiscVCheriotRVVInst16WithFormat(
+ static_cast<uint16_t>(inst_word_ & 0xffff));
+ opcode_ = opcode;
+ format_ = format;
+}
+
+DestinationOperandInterface *RiscVCheriotRVVEncoding::GetDestination(
+ SlotEnum, int, OpcodeEnum opcode, DestOpEnum dest_op, int dest_no,
+ int latency) {
+ int index = static_cast<int>(dest_op);
+ auto iter = dest_op_getters_.find(index);
+ if (iter == dest_op_getters_.end()) {
+ LOG(ERROR) << absl::StrCat("No getter for destination op enum value ",
+ index, " for instruction ",
+ kOpcodeNames[static_cast<int>(opcode)]);
+ return nullptr;
+ }
+ return (iter->second)(latency);
+}
+
+SourceOperandInterface *RiscVCheriotRVVEncoding::GetSource(
+ SlotEnum, int, OpcodeEnum opcode, SourceOpEnum source_op, int source_no) {
+ int index = static_cast<int>(source_op);
+ auto iter = source_op_getters_.find(index);
+ if (iter == source_op_getters_.end()) {
+ LOG(ERROR) << absl::StrCat("No getter for source op enum value ", index,
+ " for instruction ",
+ kOpcodeNames[static_cast<int>(opcode)]);
+ return nullptr;
+ }
+ return (iter->second)();
+}
+
+} // namespace isa32_rvv
+} // namespace cheriot
+} // namespace sim
+} // namespace mpact
diff --git a/cheriot/riscv_cheriot_rvv_encoding.h b/cheriot/riscv_cheriot_rvv_encoding.h
new file mode 100644
index 0000000..2ac1bde
--- /dev/null
+++ b/cheriot/riscv_cheriot_rvv_encoding.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MPACT_CHERIOT_RISCV_CHERIOT_RVV_ENCODING_H_
+#define MPACT_CHERIOT_RISCV_CHERIOT_RVV_ENCODING_H_
+
+#include <cstdint>
+
+#include "absl/container/flat_hash_map.h"
+#include "absl/functional/any_invocable.h"
+#include "cheriot/cheriot_state.h"
+#include "cheriot/riscv_cheriot_encoding_common.h"
+#include "cheriot/riscv_cheriot_rvv_bin_decoder.h"
+#include "cheriot/riscv_cheriot_rvv_decoder.h"
+#include "cheriot/riscv_cheriot_rvv_enums.h"
+
+namespace mpact {
+namespace sim {
+namespace cheriot {
+namespace isa32_rvv {
+
+using ::mpact::sim::cheriot::encoding_rvv::FormatEnum;
+
+// This class provides the interface between the generated instruction decoder
+// framework (which is agnostic of the actual bit representation of
+// instructions) and the instruction representation. This class provides methods
+// to return the opcode, source operands, and destination operands for
+// instructions according to the operand fields in the encoding.
+class RiscVCheriotRVVEncoding : public RiscVCheriotEncodingCommon,
+ public RiscVCheriotRVVEncodingBase {
+ public:
+ explicit RiscVCheriotRVVEncoding(CheriotState *state);
+
+ // Parses an instruction and determines the opcode.
+ void ParseInstruction(uint32_t inst_word);
+
+ // RiscV32 CHERIoT has a single slot type and single entry, so the following
+ // methods ignore those parameters.
+
+ // Returns the opcode in the current instruction representation.
+ OpcodeEnum GetOpcode(SlotEnum, int) override { return opcode_; }
+
+ // Returns the instruction format in the current instruction representation.
+ FormatEnum GetFormat(SlotEnum, int) { return format_; }
+
+ // There is no predicate, so return nullptr.
+ PredicateOperandInterface *GetPredicate(SlotEnum, int, OpcodeEnum,
+ PredOpEnum) override {
+ return nullptr;
+ }
+
+ // Currently no resources modeled for RiscV CHERIoT.
+ ResourceOperandInterface *GetSimpleResourceOperand(
+ SlotEnum, int, OpcodeEnum, SimpleResourceVector &resource_vec,
+ int end) override {
+ return nullptr;
+ }
+
+ ResourceOperandInterface *GetComplexResourceOperand(
+ SlotEnum, int, OpcodeEnum, ComplexResourceEnum resource, int begin,
+ int end) override {
+ return nullptr;
+ }
+
+ // The following method returns a source operand that corresponds to the
+ // particular operand field.
+ SourceOperandInterface *GetSource(SlotEnum, int, OpcodeEnum, SourceOpEnum op,
+ int source_no) override;
+
+ // The following method returns a destination operand that corresponds to the
+ // particular operand field.
+ DestinationOperandInterface *GetDestination(SlotEnum, int, OpcodeEnum,
+ DestOpEnum op, int dest_no,
+ int latency) override;
+ // This method returns latency for any destination operand for which the
+ // latency specifier in the .isa file is '*'. Since there are none, just
+ // return 0.
+ int GetLatency(SlotEnum, int, OpcodeEnum, DestOpEnum, int) override {
+ return 0;
+ }
+
+ private:
+ using SourceOpGetterMap =
+ absl::flat_hash_map<int, absl::AnyInvocable<SourceOperandInterface *()>>;
+ using DestOpGetterMap = absl::flat_hash_map<
+ int, absl::AnyInvocable<DestinationOperandInterface *(int)>>;
+
+ SourceOpGetterMap source_op_getters_;
+ DestOpGetterMap dest_op_getters_;
+ OpcodeEnum opcode_;
+ FormatEnum format_;
+};
+
+} // namespace isa32_rvv
+} // namespace cheriot
+} // namespace sim
+} // namespace mpact
+
+#endif // MPACT_CHERIOT_RISCV_CHERIOT_RVV_ENCODING_H_
diff --git a/cheriot/riscv_cheriot_rvv_fp_encoding.cc b/cheriot/riscv_cheriot_rvv_fp_encoding.cc
new file mode 100644
index 0000000..c7f3930
--- /dev/null
+++ b/cheriot/riscv_cheriot_rvv_fp_encoding.cc
@@ -0,0 +1,120 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cheriot/riscv_cheriot_rvv_fp_encoding.h"
+
+#include <cstdint>
+
+#include "absl/log/log.h"
+#include "absl/strings/str_cat.h"
+#include "cheriot/cheriot_f_getters.h"
+#include "cheriot/cheriot_getters.h"
+#include "cheriot/cheriot_register.h"
+#include "cheriot/cheriot_rvv_fp_getters.h"
+#include "cheriot/cheriot_rvv_getters.h"
+#include "cheriot/cheriot_state.h"
+#include "cheriot/riscv_cheriot_encoding_common.h"
+#include "cheriot/riscv_cheriot_rvv_fp_bin_decoder.h"
+#include "cheriot/riscv_cheriot_rvv_fp_decoder.h"
+#include "cheriot/riscv_cheriot_rvv_fp_enums.h"
+#include "mpact/sim/generic/type_helpers.h"
+
+namespace mpact {
+namespace sim {
+namespace cheriot {
+namespace isa32_rvv_fp {
+
+using Extractors = ::mpact::sim::cheriot::encoding_rvv_fp::Extractors;
+
+RiscVCheriotRVVFPEncoding::RiscVCheriotRVVFPEncoding(CheriotState *state)
+ : RiscVCheriotEncodingCommon(state) {
+ source_op_getters_.emplace(*SourceOpEnum::kNone, []() { return nullptr; });
+ dest_op_getters_.emplace(*DestOpEnum::kNone,
+ [](int latency) { return nullptr; });
+ // Add Cheriot ISA source and destination operand getters.
+ AddCheriotSourceGetters<SourceOpEnum, Extractors>(source_op_getters_, this);
+ AddCheriotDestGetters<DestOpEnum, Extractors>(dest_op_getters_, this);
+ // Add RVV source and destination operand getters.
+ AddCheriotRVVSourceGetters<SourceOpEnum, Extractors>(source_op_getters_,
+ this);
+ AddCheriotRVVDestGetters<DestOpEnum, Extractors>(dest_op_getters_, this);
+ // Add RVV FP source and destination operand getters.
+ AddCheriotRVVFPSourceGetters<SourceOpEnum, Extractors>(source_op_getters_,
+ this);
+ AddCheriotRVVFPDestGetters<DestOpEnum, Extractors>(dest_op_getters_, this);
+ // Add FP source and destination operand getters.
+ AddCheriotFSourceGetters<SourceOpEnum, Extractors>(source_op_getters_, this);
+ AddCheriotFDestGetters<DestOpEnum, Extractors>(dest_op_getters_, this);
+ // Verify that all source and destination op enum values have a getter.
+ for (int i = *SourceOpEnum::kNone; i < *SourceOpEnum::kPastMaxValue; ++i) {
+ if (source_op_getters_.find(i) == source_op_getters_.end()) {
+ LOG(ERROR) << "No getter for source op enum value " << i;
+ }
+ }
+ for (int i = *DestOpEnum::kNone; i < *DestOpEnum::kPastMaxValue; ++i) {
+ if (dest_op_getters_.find(i) == dest_op_getters_.end()) {
+ LOG(ERROR) << "No getter for destination op enum value " << i;
+ }
+ }
+}
+
+// Parse the instruction word to determine the opcode.
+void RiscVCheriotRVVFPEncoding::ParseInstruction(uint32_t inst_word) {
+ inst_word_ = inst_word;
+ if ((inst_word_ & 0x3) == 3) {
+ auto [opcode, format] = mpact::sim::cheriot::encoding_rvv_fp::
+ DecodeRiscVCheriotRVVFPInst32WithFormat(inst_word_);
+ opcode_ = opcode;
+ format_ = format;
+ return;
+ }
+
+ auto [opcode, format] = mpact::sim::cheriot::encoding_rvv_fp::
+ DecodeRiscVCheriotRVVFPInst16WithFormat(
+ static_cast<uint16_t>(inst_word_ & 0xffff));
+ opcode_ = opcode;
+ format_ = format;
+}
+
+DestinationOperandInterface *RiscVCheriotRVVFPEncoding::GetDestination(
+ SlotEnum, int, OpcodeEnum opcode, DestOpEnum dest_op, int dest_no,
+ int latency) {
+ int index = static_cast<int>(dest_op);
+ auto iter = dest_op_getters_.find(index);
+ if (iter == dest_op_getters_.end()) {
+ LOG(ERROR) << absl::StrCat("No getter for destination op enum value ",
+ index, " for instruction ",
+ kOpcodeNames[static_cast<int>(opcode)]);
+ return nullptr;
+ }
+ return (iter->second)(latency);
+}
+
+SourceOperandInterface *RiscVCheriotRVVFPEncoding::GetSource(
+ SlotEnum, int, OpcodeEnum opcode, SourceOpEnum source_op, int source_no) {
+ int index = static_cast<int>(source_op);
+ auto iter = source_op_getters_.find(index);
+ if (iter == source_op_getters_.end()) {
+ LOG(ERROR) << absl::StrCat("No getter for source op enum value ", index,
+ " for instruction ",
+ kOpcodeNames[static_cast<int>(opcode)]);
+ return nullptr;
+ }
+ return (iter->second)();
+}
+
+} // namespace isa32_rvv_fp
+} // namespace cheriot
+} // namespace sim
+} // namespace mpact
diff --git a/cheriot/riscv_cheriot_rvv_fp_encoding.h b/cheriot/riscv_cheriot_rvv_fp_encoding.h
new file mode 100644
index 0000000..7f52735
--- /dev/null
+++ b/cheriot/riscv_cheriot_rvv_fp_encoding.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MPACT_CHERIOT_RISCV_CHERIOT_RVV_FP_ENCODING_H_
+#define MPACT_CHERIOT_RISCV_CHERIOT_RVV_FP_ENCODING_H_
+
+#include <cstdint>
+
+#include "absl/container/flat_hash_map.h"
+#include "absl/functional/any_invocable.h"
+#include "cheriot/cheriot_state.h"
+#include "cheriot/riscv_cheriot_encoding_common.h"
+#include "cheriot/riscv_cheriot_rvv_fp_bin_decoder.h"
+#include "cheriot/riscv_cheriot_rvv_fp_decoder.h"
+#include "cheriot/riscv_cheriot_rvv_fp_enums.h"
+
+namespace mpact {
+namespace sim {
+namespace cheriot {
+namespace isa32_rvv_fp {
+
+using ::mpact::sim::cheriot::encoding_rvv_fp::FormatEnum;
+
+// This class provides the interface between the generated instruction decoder
+// framework (which is agnostic of the actual bit representation of
+// instructions) and the instruction representation. This class provides methods
+// to return the opcode, source operands, and destination operands for
+// instructions according to the operand fields in the encoding.
+class RiscVCheriotRVVFPEncoding : public RiscVCheriotEncodingCommon,
+ public RiscVCheriotRVVFpEncodingBase {
+ public:
+ explicit RiscVCheriotRVVFPEncoding(CheriotState *state);
+
+ // Parses an instruction and determines the opcode.
+ void ParseInstruction(uint32_t inst_word);
+
+ // RiscV32 CHERIoT has a single slot type and single entry, so the following
+ // methods ignore those parameters.
+
+ // Returns the opcode in the current instruction representation.
+ OpcodeEnum GetOpcode(SlotEnum, int) override { return opcode_; }
+
+ // Returns the instruction format in the current instruction representation.
+ FormatEnum GetFormat(SlotEnum, int) { return format_; }
+
+ // There is no predicate, so return nullptr.
+ PredicateOperandInterface *GetPredicate(SlotEnum, int, OpcodeEnum,
+ PredOpEnum) override {
+ return nullptr;
+ }
+
+ // Currently no resources modeled for RiscV CHERIoT.
+ ResourceOperandInterface *GetSimpleResourceOperand(
+ SlotEnum, int, OpcodeEnum, SimpleResourceVector &resource_vec,
+ int end) override {
+ return nullptr;
+ }
+
+ ResourceOperandInterface *GetComplexResourceOperand(
+ SlotEnum, int, OpcodeEnum, ComplexResourceEnum resource, int begin,
+ int end) override {
+ return nullptr;
+ }
+
+ // The following method returns a source operand that corresponds to the
+ // particular operand field.
+ SourceOperandInterface *GetSource(SlotEnum, int, OpcodeEnum, SourceOpEnum op,
+ int source_no) override;
+
+ // The following method returns a destination operand that corresponds to the
+ // particular operand field.
+ DestinationOperandInterface *GetDestination(SlotEnum, int, OpcodeEnum,
+ DestOpEnum op, int dest_no,
+ int latency) override;
+ // This method returns latency for any destination operand for which the
+ // latency specifier in the .isa file is '*'. Since there are none, just
+ // return 0.
+ int GetLatency(SlotEnum, int, OpcodeEnum, DestOpEnum, int) override {
+ return 0;
+ }
+
+ private:
+ using SourceOpGetterMap =
+ absl::flat_hash_map<int, absl::AnyInvocable<SourceOperandInterface *()>>;
+ using DestOpGetterMap = absl::flat_hash_map<
+ int, absl::AnyInvocable<DestinationOperandInterface *(int)>>;
+
+ SourceOpGetterMap source_op_getters_;
+ DestOpGetterMap dest_op_getters_;
+ OpcodeEnum opcode_;
+ FormatEnum format_;
+};
+
+} // namespace isa32_rvv_fp
+} // namespace cheriot
+} // namespace sim
+} // namespace mpact
+
+#endif // MPACT_CHERIOT_RISCV_CHERIOT_RVV_FP_ENCODING_H_
diff --git a/cheriot/riscv_cheriot_vector.bin_fmt b/cheriot/riscv_cheriot_vector.bin_fmt
new file mode 100644
index 0000000..8970e9e
--- /dev/null
+++ b/cheriot/riscv_cheriot_vector.bin_fmt
@@ -0,0 +1,445 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Non-floating point vector intruction encodings.
+
+format VMem[32] : Inst32Format {
+ fields:
+ unsigned nf[3];
+ unsigned mew[1];
+ unsigned mop[2];
+ unsigned vm[1];
+ unsigned rs2[5];
+ unsigned rs1[5];
+ unsigned width[3];
+ unsigned vd[5];
+ unsigned opcode[7];
+ overlays:
+ unsigned lumop[5] = rs2;
+ unsigned sumop[5] = rs2;
+ unsigned vs2[5] = rs2;
+ unsigned vs3[5] = vd;
+};
+
+format VArith[32] : Inst32Format {
+ fields:
+ unsigned func6[6];
+ unsigned vm[1];
+ unsigned vs2[5];
+ unsigned vs1[5];
+ unsigned func3[3];
+ unsigned vd[5];
+ unsigned opcode[7];
+ overlays:
+ unsigned uimm5[5] = vs1;
+ signed simm5[5] = vs1;
+ unsigned rd[5] = vd;
+ unsigned rs1[5] = vs1;
+ unsigned vd_mask[5] = vd;
+};
+
+format VConfig[32] : Inst32Format {
+ fields:
+ unsigned top12[12];
+ unsigned rs1[5];
+ unsigned func3[3];
+ unsigned rd[5];
+ unsigned opcode[7];
+ overlays:
+ signed zimm11[11] = top12[10..0];
+ unsigned func1[1] = top12[11];
+ unsigned func2[2] = top12[11..10];
+ unsigned func7[7] = top12[11..5];
+ signed zimm10[10] = top12[9..0];
+ unsigned uimm5[5] = rs1;
+ unsigned rs2[5] = top12[4..0];
+};
+
+instruction group RiscVVInst32[32] : Inst32Format {
+ //opcfg : VArith : func6 == 0bxxx'xxx, func3 == 0b111, opcode == 0b101'0111;
+ vsetvli_xn : VConfig : rs1 != 0, func1 == 0, func3 == 0b111, opcode == 0b101'0111;
+ vsetvli_nz : VConfig : rd != 0, rs1 == 0, func1 == 0, func3 == 0b111, opcode == 0b101'0111;
+ vsetvli_zz : VConfig : rd == 0, rs1 == 0, func1 == 0, func3 == 0b111, opcode == 0b101'0111;
+ vsetivli : VConfig : func2 == 0b11, func3 == 0b111, opcode == 0b101'0111;
+ vsetvl_xn : VConfig : rs1 != 0, func7 == 0b100'0000, func3 == 0b111, opcode == 0b101'0111;
+ vsetvl_nz : VConfig : rd != 0, rs1 == 0, func7 == 0b100'0000, func3 == 0b111, opcode == 0b101'0111;
+ vsetvl_zz : VConfig : rd == 0, rs1 == 0, func7 == 0b100'0000, func3 == 0b111, opcode == 0b101'0111;
+
+ // Unit stride, masked (vm=0).
+ vle8 : VMem : vm == 0, nf == 0, mew == 0, mop == 0b00, lumop == 0b00000, width == 0b000, opcode == 0b000'0111;
+ vle16 : VMem : vm == 0, nf == 0, mew == 0, mop == 0b00, lumop == 0b00000, width == 0b101, opcode == 0b000'0111;
+ vle32 : VMem : vm == 0, nf == 0, mew == 0, mop == 0b00, lumop == 0b00000, width == 0b110, opcode == 0b000'0111;
+ vle64 : VMem : vm == 0, nf == 0, mew == 0, mop == 0b00, lumop == 0b00000, width == 0b111, opcode == 0b000'0111;
+ // Unit stride, unmasked (vm=1).
+ vle8_vm1 : VMem : vm == 1, nf == 0, mew == 0, mop == 0b00, lumop == 0b00000, width == 0b000, opcode == 0b000'0111;
+ vle16_vm1 : VMem : vm == 1, nf == 0, mew == 0, mop == 0b00, lumop == 0b00000, width == 0b101, opcode == 0b000'0111;
+ vle32_vm1 : VMem : vm == 1, nf == 0, mew == 0, mop == 0b00, lumop == 0b00000, width == 0b110, opcode == 0b000'0111;
+ vle64_vm1 : VMem : vm == 1, nf == 0, mew == 0, mop == 0b00, lumop == 0b00000, width == 0b111, opcode == 0b000'0111;
+ // Mask load.
+ vlm : VMem : nf == 0, mew == 0, mop == 0b00, lumop == 0b01011, width == 0b000, opcode == 0b000'0111;
+ // Unit stride, fault first.
+ vle8ff : VMem : nf == 0, mew == 0, mop == 0b00, lumop == 0b10000, width == 0b000, opcode == 0b000'0111;
+ vle16ff : VMem : nf == 0, mew == 0, mop == 0b00, lumop == 0b10000, width == 0b101, opcode == 0b000'0111;
+ vle32ff : VMem : nf == 0, mew == 0, mop == 0b00, lumop == 0b10000, width == 0b110, opcode == 0b000'0111;
+ vle64ff : VMem : nf == 0, mew == 0, mop == 0b00, lumop == 0b10000, width == 0b111, opcode == 0b000'0111;
+ // Unit stride, whole register load.
+ vl1re8 : VMem : nf == 0, mop == 0b00, vm == 1, lumop == 0b01000, width == 0b000, opcode == 0b000'0111;
+ vl1re16 : VMem : nf == 0, mop == 0b00, vm == 1, lumop == 0b01000, width == 0b101, opcode == 0b000'0111;
+ vl1re32 : VMem : nf == 0, mop == 0b00, vm == 1, lumop == 0b01000, width == 0b110, opcode == 0b000'0111;
+ vl1re64 : VMem : nf == 0, mop == 0b00, vm == 1, lumop == 0b01000, width == 0b111, opcode == 0b000'0111;
+ vl2re8 : VMem : nf == 1, mop == 0b00, vm == 1, lumop == 0b01000, width == 0b000, opcode == 0b000'0111;
+ vl2re16 : VMem : nf == 1, mop == 0b00, vm == 1, lumop == 0b01000, width == 0b101, opcode == 0b000'0111;
+ vl2re32 : VMem : nf == 1, mop == 0b00, vm == 1, lumop == 0b01000, width == 0b110, opcode == 0b000'0111;
+ vl2re64 : VMem : nf == 1, mop == 0b00, vm == 1, lumop == 0b01000, width == 0b111, opcode == 0b000'0111;
+ vl4re8 : VMem : nf == 3, mop == 0b00, vm == 1, lumop == 0b01000, width == 0b000, opcode == 0b000'0111;
+ vl4re16 : VMem : nf == 3, mop == 0b00, vm == 1, lumop == 0b01000, width == 0b101, opcode == 0b000'0111;
+ vl4re32 : VMem : nf == 3, mop == 0b00, vm == 1, lumop == 0b01000, width == 0b110, opcode == 0b000'0111;
+ vl4re64 : VMem : nf == 3, mop == 0b00, vm == 1, lumop == 0b01000, width == 0b111, opcode == 0b000'0111;
+ vl8re8 : VMem : nf == 7, mop == 0b00, vm == 1, lumop == 0b01000, width == 0b000, opcode == 0b000'0111;
+ vl8re16 : VMem : nf == 7, mop == 0b00, vm == 1, lumop == 0b01000, width == 0b101, opcode == 0b000'0111;
+ vl8re32 : VMem : nf == 7, mop == 0b00, vm == 1, lumop == 0b01000, width == 0b110, opcode == 0b000'0111;
+ vl8re64 : VMem : nf == 7, mop == 0b00, vm == 1, lumop == 0b01000, width == 0b111, opcode == 0b000'0111;
+ // Vector load strided.
+ vlse8 : VMem : nf == 0, mew == 0, mop == 0b10, width == 0b000, opcode == 0b000'0111;
+ vlse16 : VMem : nf == 0, mew == 0, mop == 0b10, width == 0b101, opcode == 0b000'0111;
+ vlse32 : VMem : nf == 0, mew == 0, mop == 0b10, width == 0b110, opcode == 0b000'0111;
+ vlse64 : VMem : nf == 0, mew == 0, mop == 0b10, width == 0b111, opcode == 0b000'0111;
+ // Vector load indexed, unordered.
+ vluxei8 : VMem : nf == 0, mew == 0, mop == 0b01, width == 0b000, opcode == 0b000'0111;
+ vluxei16: VMem : nf == 0, mew == 0, mop == 0b01, width == 0b101, opcode == 0b000'0111;
+ vluxei32: VMem : nf == 0, mew == 0, mop == 0b01, width == 0b110, opcode == 0b000'0111;
+ vluxei64: VMem : nf == 0, mew == 0, mop == 0b01, width == 0b111, opcode == 0b000'0111;
+ // Vector load indexed, ordered.
+ vloxei8 : VMem : nf == 0, mew == 0, mop == 0b11, width == 0b000, opcode == 0b000'0111;
+ vloxei16: VMem : nf == 0, mew == 0, mop == 0b11, width == 0b101, opcode == 0b000'0111;
+ vloxei32: VMem : nf == 0, mew == 0, mop == 0b11, width == 0b110, opcode == 0b000'0111;
+ vloxei64: VMem : nf == 0, mew == 0, mop == 0b11, width == 0b111, opcode == 0b000'0111;
+ // Vector segment load, unit stride.
+ vlsege8: VMem : nf != 0, mew == 0, mop == 0b00, lumop == 0b00000, width == 0b000, opcode == 0b000'0111;
+ vlsege16: VMem : nf != 0, mew == 0, mop == 0b00, lumop == 0b00000, width == 0b101, opcode == 0b000'0111;
+ vlsege32: VMem : nf != 0, mew == 0, mop == 0b00, lumop == 0b00000, width == 0b110, opcode == 0b000'0111;
+ vlsege64: VMem : nf != 0, mew == 0, mop == 0b00, lumop == 0b00000, width == 0b111, opcode == 0b000'0111;
+ // Vector segment load, strided.
+ vlssege8: VMem : nf != 0, mew == 0, mop == 0b10, width == 0b000, opcode == 0b000'0111;
+ vlssege16: VMem : nf != 0, mew == 0, mop == 0b10, width == 0b101, opcode == 0b000'0111;
+ vlssege32: VMem : nf != 0, mew == 0, mop == 0b10, width == 0b110, opcode == 0b000'0111;
+ vlssege64: VMem : nf != 0, mew == 0, mop == 0b10, width == 0b111, opcode == 0b000'0111;
+ // Vector segment load, indexed, unordered.
+ vluxsegei8: VMem : nf != 0, mew == 0, mop == 0b01, width == 0b000, opcode == 0b000'0111;
+ vluxsegei16: VMem : nf != 0, mew == 0, mop == 0b01, width == 0b101, opcode == 0b000'0111;
+ vluxsegei32: VMem : nf != 0, mew == 0, mop == 0b01, width == 0b110, opcode == 0b000'0111;
+ vluxsegei64: VMem : nf != 0, mew == 0, mop == 0b01, width == 0b111, opcode == 0b000'0111;
+ // Vector segement load, indexed, ordered.
+ vloxsegei8: VMem : nf != 0, mew == 0, mop == 0b11, width == 0b000, opcode == 0b000'0111;
+ vloxsegei16: VMem : nf != 0, mew == 0, mop == 0b11, width == 0b101, opcode == 0b000'0111;
+ vloxsegei32: VMem : nf != 0, mew == 0, mop == 0b11, width == 0b110, opcode == 0b000'0111;
+ vloxsegei64: VMem : nf != 0, mew == 0, mop == 0b11, width == 0b111, opcode == 0b000'0111;
+
+
+ // VECTOR STORES
+
+ // Unit stride.
+ vse8 : VMem : mew == 0, mop == 0b00, sumop == 0b00000, width == 0b000, opcode == 0b010'0111;
+ vse16 : VMem : mew == 0, mop == 0b00, sumop == 0b00000, width == 0b101, opcode == 0b010'0111;
+ vse32 : VMem : mew == 0, mop == 0b00, sumop == 0b00000, width == 0b110, opcode == 0b010'0111;
+ vse64 : VMem : mew == 0, mop == 0b00, sumop == 0b00000, width == 0b111, opcode == 0b010'0111;
+ // Mask store.
+ vsm : VMem : mew == 0, mop == 0b00, sumop == 0b01011, width == 0b000, opcode == 0b010'0111;
+ // Unit stride, fault first.
+ vse8ff : VMem : mew == 0, mop == 0b00, sumop == 0b10000, width == 0b000, opcode == 0b010'0111;
+ vse16ff : VMem : mew == 0, mop == 0b00, sumop == 0b10000, width == 0b101, opcode == 0b010'0111;
+ vse32ff : VMem : mew == 0, mop == 0b00, sumop == 0b10000, width == 0b110, opcode == 0b010'0111;
+ vse64ff : VMem : mew == 0, mop == 0b00, sumop == 0b10000, width == 0b111, opcode == 0b010'0111;
+ // Unit stride, whole register store.
+ vs1re8 : VMem : nf == 0, mop == 0b00, vm == 1, sumop == 0b01000, width == 0b000, opcode == 0b010'0111;
+ vs1re16 : VMem : nf == 0, mop == 0b00, vm == 1, sumop == 0b01000, width == 0b101, opcode == 0b010'0111;
+ vs1re32 : VMem : nf == 0, mop == 0b00, vm == 1, sumop == 0b01000, width == 0b110, opcode == 0b010'0111;
+ vs1re64 : VMem : nf == 0, mop == 0b00, vm == 1, sumop == 0b01000, width == 0b111, opcode == 0b010'0111;
+ vs2re8 : VMem : nf == 1, mop == 0b00, vm == 1, sumop == 0b01000, width == 0b000, opcode == 0b010'0111;
+ vs2re16 : VMem : nf == 1, mop == 0b00, vm == 1, sumop == 0b01000, width == 0b101, opcode == 0b010'0111;
+ vs2re32 : VMem : nf == 1, mop == 0b00, vm == 1, sumop == 0b01000, width == 0b110, opcode == 0b010'0111;
+ vs2re64 : VMem : nf == 1, mop == 0b00, vm == 1, sumop == 0b01000, width == 0b111, opcode == 0b010'0111;
+ vs4re8 : VMem : nf == 3, mop == 0b00, vm == 1, sumop == 0b01000, width == 0b000, opcode == 0b010'0111;
+ vs4re16 : VMem : nf == 3, mop == 0b00, vm == 1, sumop == 0b01000, width == 0b101, opcode == 0b010'0111;
+ vs4re32 : VMem : nf == 3, mop == 0b00, vm == 1, sumop == 0b01000, width == 0b110, opcode == 0b010'0111;
+ vs4re64 : VMem : nf == 3, mop == 0b00, vm == 1, sumop == 0b01000, width == 0b111, opcode == 0b010'0111;
+ vs8re8 : VMem : nf == 7, mop == 0b00, vm == 1, sumop == 0b01000, width == 0b000, opcode == 0b010'0111;
+ vs8re16 : VMem : nf == 7, mop == 0b00, vm == 1, sumop == 0b01000, width == 0b101, opcode == 0b010'0111;
+ vs8re32 : VMem : nf == 7, mop == 0b00, vm == 1, sumop == 0b01000, width == 0b110, opcode == 0b010'0111;
+ vs8re64 : VMem : nf == 7, mop == 0b00, vm == 1, sumop == 0b01000, width == 0b111, opcode == 0b010'0111;
+ // Store strided.
+ vsse8 : VMem : mew == 0, mop == 0b10, width == 0b000, opcode == 0b010'0111;
+ vsse16 : VMem : mew == 0, mop == 0b10, width == 0b101, opcode == 0b010'0111;
+ vsse32 : VMem : mew == 0, mop == 0b10, width == 0b110, opcode == 0b010'0111;
+ vsse64 : VMem : mew == 0, mop == 0b10, width == 0b111, opcode == 0b010'0111;
+ // Store indexed, unordered.
+ vsuxei8 : VMem : mew == 0, mop == 0b01, width == 0b000, opcode == 0b010'0111;
+ vsuxei16: VMem : mew == 0, mop == 0b01, width == 0b101, opcode == 0b010'0111;
+ vsuxei32: VMem : mew == 0, mop == 0b01, width == 0b110, opcode == 0b010'0111;
+ vsuxei64: VMem : mew == 0, mop == 0b01, width == 0b111, opcode == 0b010'0111;
+ // Store indexed, ordered.
+ vsoxei8 : VMem : mew == 0, mop == 0b11, width == 0b000, opcode == 0b010'0111;
+ vsoxei16: VMem : mew == 0, mop == 0b11, width == 0b101, opcode == 0b010'0111;
+ vsoxei32: VMem : mew == 0, mop == 0b11, width == 0b110, opcode == 0b010'0111;
+ vsoxei64: VMem : mew == 0, mop == 0b11, width == 0b111, opcode == 0b010'0111;
+ // Vector segment store, unit stride.
+ vssege8: VMem : nf != 0, mew == 0, mop == 0b00, sumop == 0b00000, width == 0b000, opcode == 0b010'0111;
+ vssege16: VMem : nf != 0, mew == 0, mop == 0b00, sumop == 0b00000, width == 0b101, opcode == 0b010'0111;
+ vssege32: VMem : nf != 0, mew == 0, mop == 0b00, sumop == 0b00000, width == 0b110, opcode == 0b010'0111;
+ vssege64: VMem : nf != 0, mew == 0, mop == 0b00, sumop == 0b00000, width == 0b111, opcode == 0b010'0111;
+ // Vector segment store, strided.
+ vsssege8: VMem : nf != 0, mew == 0, mop == 0b10, width == 0b000, opcode == 0b010'0111;
+ vsssege16: VMem : nf != 0, mew == 0, mop == 0b10, width == 0b101, opcode == 0b010'0111;
+ vsssege32: VMem : nf != 0, mew == 0, mop == 0b10, width == 0b110, opcode == 0b010'0111;
+ vsssege64: VMem : nf != 0, mew == 0, mop == 0b10, width == 0b111, opcode == 0b010'0111;
+ // Vector segment store, indexed, unordered.
+ vsuxsegei8: VMem : nf != 0, mew == 0, mop == 0b01, width == 0b000, opcode == 0b010'0111;
+ vsuxsegei16: VMem : nf != 0, mew == 0, mop == 0b01, width == 0b101, opcode == 0b010'0111;
+ vsuxsegei32: VMem : nf != 0, mew == 0, mop == 0b01, width == 0b110, opcode == 0b010'0111;
+ vsuxsegei64: VMem : nf != 0, mew == 0, mop == 0b01, width == 0b111, opcode == 0b010'0111;
+ // Vector segement store, indexed, ordered.
+ vsoxsegei8: VMem : nf != 0, mew == 0, mop == 0b11, width == 0b000, opcode == 0b010'0111;
+ vsoxsegei16: VMem : nf != 0, mew == 0, mop == 0b11, width == 0b101, opcode == 0b010'0111;
+ vsoxsegei32: VMem : nf != 0, mew == 0, mop == 0b11, width == 0b110, opcode == 0b010'0111;
+ vsoxsegei64: VMem : nf != 0, mew == 0, mop == 0b11, width == 0b111, opcode == 0b010'0111;
+
+ // Integer: OPIVV, OPIVX, OPIVI
+ //opivv : VArith : func6 == 0bxxx'xxx, func3 == 0b000, opcode == 0b101'0111;
+ //opivx : VArith : func6 == 0bxxx'xxx, func3 == 0b100, opcode == 0b101'0111;
+ //opivi : VArith : func6 == 0bxxx'xxx, func3 == 0b011, opcode == 0b101'0111;
+
+ vadd_vv : VArith : func6 == 0b000'000, func3 == 0b000, opcode == 0b101'0111;
+ vadd_vx : VArith : func6 == 0b000'000, func3 == 0b100, opcode == 0b101'0111;
+ vadd_vi : VArith : func6 == 0b000'000, func3 == 0b011, opcode == 0b101'0111;
+ vsub_vv : VArith : func6 == 0b000'010, func3 == 0b000, opcode == 0b101'0111;
+ vsub_vx : VArith : func6 == 0b000'010, func3 == 0b100, opcode == 0b101'0111;
+ vrsub_vx : VArith : func6 == 0b000'011, func3 == 0b100, opcode == 0b101'0111;
+ vrsub_vi : VArith : func6 == 0b000'011, func3 == 0b011, opcode == 0b101'0111;
+ vminu_vv : VArith : func6 == 0b000'100, func3 == 0b000, opcode == 0b101'0111;
+ vminu_vx : VArith : func6 == 0b000'100, func3 == 0b100, opcode == 0b101'0111;
+ vmin_vv : VArith : func6 == 0b000'101, func3 == 0b000, opcode == 0b101'0111;
+ vmin_vx : VArith : func6 == 0b000'101, func3 == 0b100, opcode == 0b101'0111;
+ vmaxu_vv : VArith : func6 == 0b000'110, func3 == 0b000, opcode == 0b101'0111;
+ vmaxu_vx : VArith : func6 == 0b000'110, func3 == 0b100, opcode == 0b101'0111;
+ vmax_vv : VArith : func6 == 0b000'111, func3 == 0b000, opcode == 0b101'0111;
+ vmax_vx : VArith : func6 == 0b000'111, func3 == 0b100, opcode == 0b101'0111;
+ vand_vv : VArith : func6 == 0b001'001, func3 == 0b000, opcode == 0b101'0111;
+ vand_vx : VArith : func6 == 0b001'001, func3 == 0b100, opcode == 0b101'0111;
+ vand_vi : VArith : func6 == 0b001'001, func3 == 0b011, opcode == 0b101'0111;
+ vor_vv : VArith : func6 == 0b001'010, func3 == 0b000, opcode == 0b101'0111;
+ vor_vx : VArith : func6 == 0b001'010, func3 == 0b100, opcode == 0b101'0111;
+ vor_vi : VArith : func6 == 0b001'010, func3 == 0b011, opcode == 0b101'0111;
+ vxor_vv : VArith : func6 == 0b001'011, func3 == 0b000, opcode == 0b101'0111;
+ vxor_vx : VArith : func6 == 0b001'011, func3 == 0b100, opcode == 0b101'0111;
+ vxor_vi : VArith : func6 == 0b001'011, func3 == 0b011, opcode == 0b101'0111;
+ vrgather_vv : VArith : func6 == 0b001'100, func3 == 0b000, opcode == 0b101'0111;
+ vrgather_vx : VArith : func6 == 0b001'100, func3 == 0b100, opcode == 0b101'0111;
+ vrgather_vi : VArith : func6 == 0b001'100, func3 == 0b011, opcode == 0b101'0111;
+ vslideup_vx : VArith : func6 == 0b001'110, func3 == 0b100, opcode == 0b101'0111;
+ vslideup_vi : VArith : func6 == 0b001'110, func3 == 0b011, opcode == 0b101'0111;
+ vrgatherei16_vv : VArith : func6 == 0b001'110, func3 == 0b000, opcode == 0b101'0111;
+ vslidedown_vx : VArith : func6 == 0b001'111, func3 == 0b100, opcode == 0b101'0111;
+ vslidedown_vi : VArith : func6 == 0b001'111, func3 == 0b011, opcode == 0b101'0111;
+ vadc_vv : VArith : func6 == 0b010'000, vd != 0, vm == 0, func3 == 0b000, opcode == 0b101'0111;
+ vadc_vx : VArith : func6 == 0b010'000, vd != 0, vm == 0, func3 == 0b100, opcode == 0b101'0111;
+ vadc_vi : VArith : func6 == 0b010'000, vd != 0, vm == 0, func3 == 0b011, opcode == 0b101'0111;
+ vmadc_vv : VArith : func6 == 0b010'001, func3 == 0b000, opcode == 0b101'0111;
+ vmadc_vx : VArith : func6 == 0b010'001, func3 == 0b100, opcode == 0b101'0111;
+ vmadc_vi : VArith : func6 == 0b010'001, func3 == 0b011, opcode == 0b101'0111;
+ vsbc_vv : VArith : func6 == 0b010'010, vd != 0, vm == 0, func3 == 0b000, opcode == 0b101'0111;
+ vsbc_vx : VArith : func6 == 0b010'010, vd != 0, vm == 0, func3 == 0b100, opcode == 0b101'0111;
+ vmsbc_vv : VArith : func6 == 0b010'011, func3 == 0b000, opcode == 0b101'0111;
+ vmsbc_vx : VArith : func6 == 0b010'011, func3 == 0b100, opcode == 0b101'0111;
+ vmerge_vv : VArith : func6 == 0b010'111, vm == 0, func3 == 0b000, opcode == 0b101'0111;
+ vmerge_vx : VArith : func6 == 0b010'111, vm == 0, func3 == 0b100, opcode == 0b101'0111;
+ vmerge_vi : VArith : func6 == 0b010'111, vm == 0, func3 == 0b011, opcode == 0b101'0111;
+ vmv_vv : VArith : func6 == 0b010'111, vm == 1, vs2 == 0, func3 == 0b000, opcode == 0b101'0111;
+ vmv_vx : VArith : func6 == 0b010'111, vm == 1, vs2 == 0, func3 == 0b100, opcode == 0b101'0111;
+ vmv_vi : VArith : func6 == 0b010'111, vm == 1, vs2 == 0, func3 == 0b011, opcode == 0b101'0111;
+ vmseq_vv : VArith : func6 == 0b011'000, func3 == 0b000, opcode == 0b101'0111;
+ vmseq_vx : VArith : func6 == 0b011'000, func3 == 0b100, opcode == 0b101'0111;
+ vmseq_vi : VArith : func6 == 0b011'000, func3 == 0b011, opcode == 0b101'0111;
+ vmsne_vv : VArith : func6 == 0b011'001, func3 == 0b000, opcode == 0b101'0111;
+ vmsne_vx : VArith : func6 == 0b011'001, func3 == 0b100, opcode == 0b101'0111;
+ vmsne_vi : VArith : func6 == 0b011'001, func3 == 0b011, opcode == 0b101'0111;
+ vmsltu_vv : VArith : func6 == 0b011'010, func3 == 0b000, opcode == 0b101'0111;
+ vmsltu_vx : VArith : func6 == 0b011'010, func3 == 0b100, opcode == 0b101'0111;
+ vmslt_vv : VArith : func6 == 0b011'011, func3 == 0b000, opcode == 0b101'0111;
+ vmslt_vx : VArith : func6 == 0b011'011, func3 == 0b100, opcode == 0b101'0111;
+ vmsleu_vv : VArith : func6 == 0b011'100, func3 == 0b000, opcode == 0b101'0111;
+ vmsleu_vx : VArith : func6 == 0b011'100, func3 == 0b100, opcode == 0b101'0111;
+ vmsleu_vi : VArith : func6 == 0b011'100, func3 == 0b011, opcode == 0b101'0111;
+ vmsle_vv : VArith : func6 == 0b011'101, func3 == 0b000, opcode == 0b101'0111;
+ vmsle_vx : VArith : func6 == 0b011'101, func3 == 0b100, opcode == 0b101'0111;
+ vmsle_vi : VArith : func6 == 0b011'101, func3 == 0b011, opcode == 0b101'0111;
+ vmsgtu_vx : VArith : func6 == 0b011'110, func3 == 0b100, opcode == 0b101'0111;
+ vmsgtu_vi : VArith : func6 == 0b011'110, func3 == 0b011, opcode == 0b101'0111;
+ vmsgt_vx : VArith : func6 == 0b011'111, func3 == 0b100, opcode == 0b101'0111;
+ vmsgt_vi : VArith : func6 == 0b011'111, func3 == 0b011, opcode == 0b101'0111;
+ vsaddu_vv : VArith : func6 == 0b100'000, func3 == 0b000, opcode == 0b101'0111;
+ vsaddu_vx : VArith : func6 == 0b100'000, func3 == 0b100, opcode == 0b101'0111;
+ vsaddu_vi : VArith : func6 == 0b100'000, func3 == 0b011, opcode == 0b101'0111;
+ vsadd_vv : VArith : func6 == 0b100'001, func3 == 0b000, opcode == 0b101'0111;
+ vsadd_vx : VArith : func6 == 0b100'001, func3 == 0b100, opcode == 0b101'0111;
+ vsadd_vi : VArith : func6 == 0b100'001, func3 == 0b011, opcode == 0b101'0111;
+ vssubu_vv : VArith : func6 == 0b100'010, func3 == 0b000, opcode == 0b101'0111;
+ vssubu_vx : VArith : func6 == 0b100'010, func3 == 0b100, opcode == 0b101'0111;
+ vssub_vv : VArith : func6 == 0b100'011, func3 == 0b000, opcode == 0b101'0111;
+ vssub_vx : VArith : func6 == 0b100'011, func3 == 0b100, opcode == 0b101'0111;
+ vsll_vv : VArith : func6 == 0b100'101, func3 == 0b000, opcode == 0b101'0111;
+ vsll_vx : VArith : func6 == 0b100'101, func3 == 0b100, opcode == 0b101'0111;
+ vsll_vi : VArith : func6 == 0b100'101, func3 == 0b011, opcode == 0b101'0111;
+ vsmul_vv : VArith : func6 == 0b100'111, func3 == 0b000, opcode == 0b101'0111;
+ vsmul_vx : VArith : func6 == 0b100'111, func3 == 0b100, opcode == 0b101'0111;
+ vmv1r_vi : VArith : func6 == 0b100'111, uimm5 == 0, func3 == 0b011, opcode == 0b101'0111;
+ vmv2r_vi : VArith : func6 == 0b100'111, uimm5 == 1, func3 == 0b011, opcode == 0b101'0111;
+ vmv4r_vi : VArith : func6 == 0b100'111, uimm5 == 3, func3 == 0b011, opcode == 0b101'0111;
+ vmv8r_vi : VArith : func6 == 0b100'111, uimm5 == 7, func3 == 0b011, opcode == 0b101'0111;
+ vsrl_vv : VArith : func6 == 0b101'000, func3 == 0b000, opcode == 0b101'0111;
+ vsrl_vx : VArith : func6 == 0b101'000, func3 == 0b100, opcode == 0b101'0111;
+ vsrl_vi : VArith : func6 == 0b101'000, func3 == 0b011, opcode == 0b101'0111;
+ vsra_vv : VArith : func6 == 0b101'001, func3 == 0b000, opcode == 0b101'0111;
+ vsra_vx : VArith : func6 == 0b101'001, func3 == 0b100, opcode == 0b101'0111;
+ vsra_vi : VArith : func6 == 0b101'001, func3 == 0b011, opcode == 0b101'0111;
+ vssrl_vv : VArith : func6 == 0b101'010, func3 == 0b000, opcode == 0b101'0111;
+ vssrl_vx : VArith : func6 == 0b101'010, func3 == 0b100, opcode == 0b101'0111;
+ vssrl_vi : VArith : func6 == 0b101'010, func3 == 0b011, opcode == 0b101'0111;
+ vssra_vv : VArith : func6 == 0b101'011, func3 == 0b000, opcode == 0b101'0111;
+ vssra_vx : VArith : func6 == 0b101'011, func3 == 0b100, opcode == 0b101'0111;
+ vssra_vi : VArith : func6 == 0b101'011, func3 == 0b011, opcode == 0b101'0111;
+ vnsrl_vv : VArith : func6 == 0b101'100, func3 == 0b000, opcode == 0b101'0111;
+ vnsrl_vx : VArith : func6 == 0b101'100, func3 == 0b100, opcode == 0b101'0111;
+ vnsrl_vi : VArith : func6 == 0b101'100, func3 == 0b011, opcode == 0b101'0111;
+ vnsra_vv : VArith : func6 == 0b101'101, func3 == 0b000, opcode == 0b101'0111;
+ vnsra_vx : VArith : func6 == 0b101'101, func3 == 0b100, opcode == 0b101'0111;
+ vnsra_vi : VArith : func6 == 0b101'101, func3 == 0b011, opcode == 0b101'0111;
+ vnclipu_vv : VArith : func6 == 0b101'110, func3 == 0b000, opcode == 0b101'0111;
+ vnclipu_vx : VArith : func6 == 0b101'110, func3 == 0b100, opcode == 0b101'0111;
+ vnclipu_vi : VArith : func6 == 0b101'110, func3 == 0b011, opcode == 0b101'0111;
+ vnclip_vv : VArith : func6 == 0b101'111, func3 == 0b000, opcode == 0b101'0111;
+ vnclip_vx : VArith : func6 == 0b101'111, func3 == 0b100, opcode == 0b101'0111;
+ vnclip_vi : VArith : func6 == 0b101'111, func3 == 0b011, opcode == 0b101'0111;
+ vwredsumu_vv : VArith : func6 == 0b110'000, func3 == 0b000, opcode == 0b101'0111;
+ vwredsum_vv : VArith : func6 == 0b110'001, func3 == 0b000, opcode == 0b101'0111;
+
+ // Integer: OPMVV, OPMVX
+ //opmvv : VArith : func6 == 0bxxx'xxx, func3 == 0b010, opcode == 0b101'0111;
+ //opmvx : VArith : func6 == 0bxxx'xxx, func3 == 0b110, opcode == 0b101'0111;
+
+ vredsum_vv : VArith : func6 == 0b000'000, func3 == 0b010, opcode == 0b101'0111;
+ vredand_vv : VArith : func6 == 0b000'001, func3 == 0b010, opcode == 0b101'0111;
+ vredor_vv : VArith : func6 == 0b000'010, func3 == 0b010, opcode == 0b101'0111;
+ vredxor_vv : VArith : func6 == 0b000'011, func3 == 0b010, opcode == 0b101'0111;
+ vredminu_vv : VArith : func6 == 0b000'100, func3 == 0b010, opcode == 0b101'0111;
+ vredmin_vv : VArith : func6 == 0b000'101, func3 == 0b010, opcode == 0b101'0111;
+ vredmaxu_vv : VArith : func6 == 0b000'110, func3 == 0b010, opcode == 0b101'0111;
+ vredmax_vv : VArith : func6 == 0b000'111, func3 == 0b010, opcode == 0b101'0111;
+ vaaddu_vv : VArith : func6 == 0b001'000, func3 == 0b010, opcode == 0b101'0111;
+ vaaddu_vx : VArith : func6 == 0b001'000, func3 == 0b110, opcode == 0b101'0111;
+ vaadd_vv : VArith : func6 == 0b001'001, func3 == 0b010, opcode == 0b101'0111;
+ vaadd_vx : VArith : func6 == 0b001'001, func3 == 0b110, opcode == 0b101'0111;
+ vasubu_vv : VArith : func6 == 0b001'010, func3 == 0b010, opcode == 0b101'0111;
+ vasubu_vx : VArith : func6 == 0b001'010, func3 == 0b110, opcode == 0b101'0111;
+ vasub_vv : VArith : func6 == 0b001'011, func3 == 0b010, opcode == 0b101'0111;
+ vasub_vx : VArith : func6 == 0b001'011, func3 == 0b110, opcode == 0b101'0111;
+ vslide1up_vx : VArith : func6 == 0b001'110, func3 == 0b110, opcode == 0b101'0111;
+ vslide1down_vx : VArith : func6 == 0b001'111, func3 == 0b110, opcode == 0b101'0111;
+ vcompress_vv : VArith : func6 == 0b010'111, func3 == 0b010, opcode == 0b101'0111;
+ vmandnot_vv : VArith : func6 == 0b011'000, func3 == 0b010, opcode == 0b101'0111;
+ vmand_vv : VArith : func6 == 0b011'001, func3 == 0b010, opcode == 0b101'0111;
+ vmor_vv : VArith : func6 == 0b011'010, func3 == 0b010, opcode == 0b101'0111;
+ vmxor_vv : VArith : func6 == 0b011'011, func3 == 0b010, opcode == 0b101'0111;
+ vmornot_vv : VArith : func6 == 0b011'100, func3 == 0b010, opcode == 0b101'0111;
+ vmnand_vv : VArith : func6 == 0b011'101, func3 == 0b010, opcode == 0b101'0111;
+ vmnor_vv : VArith : func6 == 0b011'110, func3 == 0b010, opcode == 0b101'0111;
+ vmxnor_vv : VArith : func6 == 0b011'111, func3 == 0b010, opcode == 0b101'0111;
+
+ vdivu_vv : VArith : func6 == 0b100'000, func3 == 0b010, opcode == 0b101'0111;
+ vdivu_vx : VArith : func6 == 0b100'000, func3 == 0b110, opcode == 0b101'0111;
+ vdiv_vv : VArith : func6 == 0b100'001, func3 == 0b010, opcode == 0b101'0111;
+ vdiv_vx : VArith : func6 == 0b100'001, func3 == 0b110, opcode == 0b101'0111;
+ vremu_vv : VArith : func6 == 0b100'010, func3 == 0b010, opcode == 0b101'0111;
+ vremu_vx : VArith : func6 == 0b100'010, func3 == 0b110, opcode == 0b101'0111;
+ vrem_vv : VArith : func6 == 0b100'011, func3 == 0b010, opcode == 0b101'0111;
+ vrem_vx : VArith : func6 == 0b100'011, func3 == 0b110, opcode == 0b101'0111;
+ vmulhu_vv : VArith : func6 == 0b100'100, func3 == 0b010, opcode == 0b101'0111;
+ vmulhu_vx : VArith : func6 == 0b100'100, func3 == 0b110, opcode == 0b101'0111;
+ vmul_vv : VArith : func6 == 0b100'101, func3 == 0b010, opcode == 0b101'0111;
+ vmul_vx : VArith : func6 == 0b100'101, func3 == 0b110, opcode == 0b101'0111;
+ vmulhsu_vv : VArith : func6 == 0b100'110, func3 == 0b010, opcode == 0b101'0111;
+ vmulhsu_vx : VArith : func6 == 0b100'110, func3 == 0b110, opcode == 0b101'0111;
+ vmulh_vv : VArith : func6 == 0b100'111, func3 == 0b010, opcode == 0b101'0111;
+ vmulh_vx : VArith : func6 == 0b100'111, func3 == 0b110, opcode == 0b101'0111;
+ vmadd_vv : VArith : func6 == 0b101'001, func3 == 0b010, opcode == 0b101'0111;
+ vmadd_vx : VArith : func6 == 0b101'001, func3 == 0b110, opcode == 0b101'0111;
+ vnmsub_vv : VArith : func6 == 0b101'011, func3 == 0b010, opcode == 0b101'0111;
+ vnmsub_vx : VArith : func6 == 0b101'011, func3 == 0b110, opcode == 0b101'0111;
+ vmacc_vv : VArith : func6 == 0b101'101, func3 == 0b010, opcode == 0b101'0111;
+ vmacc_vx : VArith : func6 == 0b101'101, func3 == 0b110, opcode == 0b101'0111;
+ vnmsac_vv : VArith : func6 == 0b101'111, func3 == 0b010, opcode == 0b101'0111;
+ vnmsac_vx : VArith : func6 == 0b101'111, func3 == 0b110, opcode == 0b101'0111;
+ vwaddu_vv : VArith : func6 == 0b110'000, func3 == 0b010, opcode == 0b101'0111;
+ vwaddu_vx : VArith : func6 == 0b110'000, func3 == 0b110, opcode == 0b101'0111;
+ vwadd_vv : VArith : func6 == 0b110'001, func3 == 0b010, opcode == 0b101'0111;
+ vwadd_vx : VArith : func6 == 0b110'001, func3 == 0b110, opcode == 0b101'0111;
+ vwsubu_vv : VArith : func6 == 0b110'010, func3 == 0b010, opcode == 0b101'0111;
+ vwsubu_vx : VArith : func6 == 0b110'010, func3 == 0b110, opcode == 0b101'0111;
+ vwsub_vv : VArith : func6 == 0b110'011, func3 == 0b010, opcode == 0b101'0111;
+ vwsub_vx : VArith : func6 == 0b110'011, func3 == 0b110, opcode == 0b101'0111;
+ vwaddu_w_vv : VArith : func6 == 0b110'100, func3 == 0b010, opcode == 0b101'0111;
+ vwaddu_w_vx : VArith : func6 == 0b110'100, func3 == 0b110, opcode == 0b101'0111;
+ vwadd_w_vv : VArith : func6 == 0b110'101, func3 == 0b010, opcode == 0b101'0111;
+ vwadd_w_vx : VArith : func6 == 0b110'101, func3 == 0b110, opcode == 0b101'0111;
+ vwsubu_w_vv : VArith : func6 == 0b110'110, func3 == 0b010, opcode == 0b101'0111;
+ vwsubu_w_vx : VArith : func6 == 0b110'110, func3 == 0b110, opcode == 0b101'0111;
+ vwsub_w_vv : VArith : func6 == 0b110'111, func3 == 0b010, opcode == 0b101'0111;
+ vwsub_w_vx : VArith : func6 == 0b110'111, func3 == 0b110, opcode == 0b101'0111;
+ vwmulu_vv : VArith : func6 == 0b111'000, func3 == 0b010, opcode == 0b101'0111;
+ vwmulu_vx : VArith : func6 == 0b111'000, func3 == 0b110, opcode == 0b101'0111;
+ vwmulsu_vv : VArith : func6 == 0b111'010, func3 == 0b010, opcode == 0b101'0111;
+ vwmulsu_vx : VArith : func6 == 0b111'010, func3 == 0b110, opcode == 0b101'0111;
+ vwmul_vv : VArith : func6 == 0b111'011, func3 == 0b010, opcode == 0b101'0111;
+ vwmul_vx : VArith : func6 == 0b111'011, func3 == 0b110, opcode == 0b101'0111;
+ vwmaccu_vv : VArith : func6 == 0b111'100, func3 == 0b010, opcode == 0b101'0111;
+ vwmaccu_vx : VArith : func6 == 0b111'100, func3 == 0b110, opcode == 0b101'0111;
+ vwmacc_vv : VArith : func6 == 0b111'101, func3 == 0b010, opcode == 0b101'0111;
+ vwmacc_vx : VArith : func6 == 0b111'101, func3 == 0b110, opcode == 0b101'0111;
+ vwmaccus_vv : VArith : func6 == 0b111'110, func3 == 0b010, opcode == 0b101'0111;
+ vwmaccus_vx : VArith : func6 == 0b111'110, func3 == 0b110, opcode == 0b101'0111;
+ vwmaccsu_vv : VArith : func6 == 0b111'111, func3 == 0b010, opcode == 0b101'0111;
+ vwmaccsu_vx : VArith : func6 == 0b111'111, func3 == 0b110, opcode == 0b101'0111;
+
+ // VWXUNARY0 vv: VArith : func6 == 0b010'000, func3 == 0b010, opcode == 0b101'0111;
+ vmv_x_s : VArith : func6 == 0b010'000, vs1 == 0b00000, func3 == 0b010, opcode == 0b101'0111;
+ vcpop : VArith : func6 == 0b010'000, vs1 == 0b10000, func3 == 0b010, opcode == 0b101'0111;
+ vfirst : VArith : func6 == 0b010'000, vs1 == 0b10001, func3 == 0b010, opcode == 0b101'0111;
+
+ // VRXUNARY0 vx: VArith : func6 == 0b010'000, func3 == 0b110, opcode == 0b101'0111;
+ vmv_s_x : VArith : func6 == 0b010'000, vs2 == 0, func3 == 0b110, opcode == 0b101'0111;
+
+ // VXUNARY0 vv : VArith : func6 == 0b010'010, func3 == 0b010, opcode == 0b101'0111;
+ vzext_vf8: VArith : func6 == 0b010'010, vs1 == 0b00010, func3 == 0b010, opcode == 0b101'0111;
+ vsext_vf8: VArith : func6 == 0b010'010, vs1 == 0b00011, func3 == 0b010, opcode == 0b101'0111;
+ vzext_vf4: VArith : func6 == 0b010'010, vs1 == 0b00100, func3 == 0b010, opcode == 0b101'0111;
+ vsext_vf4: VArith : func6 == 0b010'010, vs1 == 0b00101, func3 == 0b010, opcode == 0b101'0111;
+ vzext_vf2: VArith : func6 == 0b010'010, vs1 == 0b00110, func3 == 0b010, opcode == 0b101'0111;
+ vsext_vf2: VArith : func6 == 0b010'010, vs1 == 0b00111, func3 == 0b010, opcode == 0b101'0111;
+
+ // VMUNARY vv : VArith : func6 == 0b010'100, func3 == 0b010, opcode == 0b101'0111;
+ vmsbf : VArith : func6 == 0b010'100, vs1 == 0b00001, func3 == 0b010, opcode == 0b101'0111;
+ vmsof : VArith : func6 == 0b010'100, vs1 == 0b00010, func3 == 0b010, opcode == 0b101'0111;
+ vmsif : VArith : func6 == 0b010'100, vs1 == 0b00011, func3 == 0b010, opcode == 0b101'0111;
+ viota : VArith : func6 == 0b010'100, vs1 == 0b10000, func3 == 0b010, opcode == 0b101'0111;
+ vid : VArith : func6 == 0b010'100, vs1 == 0b10001, func3 == 0b010, opcode == 0b101'0111;
+};
diff --git a/cheriot/riscv_cheriot_vector.isa b/cheriot/riscv_cheriot_vector.isa
new file mode 100644
index 0000000..a4c00d8
--- /dev/null
+++ b/cheriot/riscv_cheriot_vector.isa
@@ -0,0 +1,1094 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// This file defines the non-floating point vector instruction definitions.
+
+// First disasm field is 18 char wide and left justified.
+disasm widths = {-18};
+
+slot riscv_cheriot_vector {
+ includes {
+ #include "cheriot/riscv_cheriot_vector_memory_instructions.h"
+ #include "cheriot/riscv_cheriot_vector_opi_instructions.h"
+ #include "cheriot/riscv_cheriot_vector_opm_instructions.h"
+ #include "cheriot/riscv_cheriot_vector_permute_instructions.h"
+ #include "cheriot/riscv_cheriot_vector_reduction_instructions.h"
+ #include "cheriot/riscv_cheriot_vector_unary_instructions.h"
+ #include "absl/functional/bind_front.h"
+ }
+ default size = 4;
+ default latency = 0;
+ default opcode =
+ disasm: "Unimplemented instruction at 0x%(@:08x)",
+ semfunc: "&RV32VUnimplementedInstruction";
+ opcodes {
+ // Configuration.
+ vsetvli_xn{: rs1, zimm11: rd},
+ disasm: "vsetvli","%rd,", "%rs1, %zimm11",
+ semfunc: "absl::bind_front(&Vsetvl, /*rd_zero*/ false, /*rs1_zero*/ false)";
+ vsetvli_nz{: rs1, zimm11: rd},
+ disasm: "vsetvli", "%rd, %rs1, %zimm11",
+ semfunc: "absl::bind_front(&Vsetvl, /*rd_zero*/false, /*rs1_zero*/ true)";
+ vsetvli_zz{: rs1, zimm11: rd},
+ disasm: "vsetvli", "%rd, %rs1, %zimm11",
+ semfunc: "absl::bind_front(&Vsetvl, /*rd_zero*/true, /*rs1_zero*/ true)";
+ vsetivli{: uimm5, zimm10: rd},
+ disasm: "vsetivli %uimm5, %zimm10",
+ semfunc: "absl::bind_front(&Vsetvl, /*rd_zero*/false, /*rs1_zero*/ false)";
+ vsetvl_xn{: rs1, rs2: rd},
+ disasm: "vsetvl", "%rd, %rs1, %rs2",
+ semfunc: "absl::bind_front(&Vsetvl, /*rd_zero*/false, /*rs1_zero*/ false)";
+ vsetvl_nz{: rs1, rs2: rd},
+ disasm: "vsetvl", "%rd, %rs1, %rs2",
+ semfunc: "absl::bind_front(&Vsetvl, /*rd_zero*/false, /*rs1_zero*/ true)";
+ vsetvl_zz{: rs1, rs2: rd},
+ disasm: "vsetvl", "%rd, %rs1, %rs2",
+ semfunc: "absl::bind_front(&Vsetvl, /*rd_zero*/true, /*rs1_zero*/ true)";
+
+ // VECTOR LOADS
+
+ // Unit stride loads, masked (vm=0)
+ vle8{(: rs1, vmask :), (: : vd )},
+ disasm: "vle8.v", "%vd, (%rs1), %vmask",
+ semfunc: "absl::bind_front(&VlUnitStrided, /*element_width*/ 1)", "&VlChild";
+ vle16{(: rs1, vmask :), (: : vd )},
+ disasm: "vle16.v", "%vd, (%rs1), %vmask",
+ semfunc: "absl::bind_front(&VlUnitStrided, /*element_width*/ 2)", "&VlChild";
+ vle32{(: rs1, vmask :), ( : : vd) },
+ disasm: "vle32.v", "%vd, (%rs1), %vmask",
+ semfunc: "absl::bind_front(&VlUnitStrided, /*element_width*/ 4)", "&VlChild";
+ vle64{(: rs1, vmask :), ( : : vd) },
+ disasm: "vle64.v", "%vd, (%rs1), %vmask",
+ semfunc: "absl::bind_front(&VlUnitStrided, /*element_width*/ 8)", "&VlChild";
+
+ // Unit stride loads, unmasked (vm=1)
+ vle8_vm1{(: rs1, vmask_true :), (: : vd )},
+ disasm: "vle8.v", "%vd, (%rs1)",
+ semfunc: "absl::bind_front(&VlUnitStrided, /*element_width*/ 1)", "&VlChild";
+ vle16_vm1{(: rs1, vmask_true :), (: : vd )},
+ disasm: "vle16.v", "%vd, (%rs1)",
+ semfunc: "absl::bind_front(&VlUnitStrided, /*element_width*/ 2)", "&VlChild";
+ vle32_vm1{(: rs1, vmask_true :), ( : : vd) },
+ disasm: "vle32.v", "%vd, (%rs1)",
+ semfunc: "absl::bind_front(&VlUnitStrided, /*element_width*/ 4)", "&VlChild";
+ vle64_vm1{(: rs1, vmask_true :), ( : : vd) },
+ disasm: "vle64.v", "%vd, (%rs1)",
+ semfunc: "absl::bind_front(&VlUnitStrided, /*element_width*/ 8)", "&VlChild";
+
+ // Vector strided loads
+ vlse8{(: rs1, rs2, vmask :), (: : vd)},
+ disasm: "vlse8.v", "%vd, (%rs1), %rs2, %vmask",
+ semfunc: "absl::bind_front(&VlStrided, /*element_width*/ 1)", "&VlChild";
+ vlse16{(: rs1, rs2, vmask :), (: : vd)},
+ disasm: "vlse16.v", "%vd, (%rs1), %rs2, %vmask",
+ semfunc: "absl::bind_front(&VlStrided, /*element_width*/ 2)", "&VlChild";
+ vlse32{(: rs1, rs2, vmask :), (: : vd)},
+ disasm: "vlse32.v", "%vd, (%rs1), %rs2, %vmask",
+ semfunc: "absl::bind_front(&VlStrided, /*element_width*/ 4)", "&VlChild";
+ vlse64{(: rs1, rs2, vmask :), (: : vd)},
+ disasm: "vlse64.v", "%vd, (%rs1), %rs2, %vmask",
+ semfunc: "absl::bind_front(&VlStrided, /*element_width*/ 8)", "&VlChild";
+
+ // Vector mask load
+ vlm{(: rs1 :), (: : vd)},
+ disasm: "vlm.v", "%vd, (%rs1)",
+ semfunc: "&Vlm", "&VlChild";
+
+ // Unit stride vector load, fault first
+ vle8ff{(: rs1, vmask:), (: : vd)},
+ disasm: "vle8ff.v", "%vd, (%rs1), %vmask",
+ semfunc: "absl::bind_front(&VlUnitStrided, /*element_width*/ 1)", "&VlChild";
+ vle16ff{(: rs1, vmask:), (: : vd)},
+ disasm: "vle16ff.v", "%vd, (%rs1), %vmask",
+ semfunc: "absl::bind_front(&VlUnitStrided, /*element_width*/ 2)", "&VlChild";
+ vle32ff{(: rs1, vmask:), (: : vd)},
+ disasm: "vle32ff.v", "%vd, (%rs1), %vmask",
+ semfunc: "absl::bind_front(&VlUnitStrided, /*element_width*/ 4)", "&VlChild";
+ vle64ff{(: rs1, vmask:), (: : vd)},
+ disasm: "vle64ff.v", "%vd, (%rs1), %vmask",
+ semfunc: "absl::bind_front(&VlUnitStrided, /*element_width*/ 8)", "&VlChild";
+
+ // Vector register load
+ vl1re8{(: rs1 :), (: : vd)},
+ disasm: "vl1re8.v", "%vd, (%rs1)",
+ semfunc: "absl::bind_front(&VlRegister, /*num_regs*/ 1, /*element_width*/ 1)", "&VlChild";
+ vl1re16{(: rs1 :), (: : vd)},
+ disasm: "vl1re16.v", "%vd, (%rs1)",
+ semfunc: "absl::bind_front(&VlRegister, /*num_regs*/ 1, /*element_width*/ 2)", "&VlChild";
+ vl1re32{(: rs1 :), (: : vd)},
+ disasm: "vl1re32.v", "%vd, (%rs1)",
+ semfunc: "absl::bind_front(&VlRegister, /*num_regs*/ 1, /*element_width*/ 4)", "&VlChild";
+ vl1re64{(: rs1 :), (: : vd)},
+ disasm: "vl1re64.v", "%vd, (%rs1)",
+ semfunc: "absl::bind_front(&VlRegister, /*num_regs*/ 1, /*element_width*/ 8)", "&VlChild";
+ vl2re8{(: rs1 :), (: : vd)},
+ disasm: "vl2re8.v", "%vd, (%rs1)",
+ semfunc: "absl::bind_front(&VlRegister, /*num_regs*/ 2, /*element_width*/ 1)", "&VlChild";
+ vl2re16{(: rs1 :), (: : vd)},
+ disasm: "vl2re16.v", "%vd, (%rs1)",
+ semfunc: "absl::bind_front(&VlRegister, /*num_regs*/ 2, /*element_width*/ 2)", "&VlChild";
+ vl2re32{(: rs1 :), (: : vd)},
+ disasm: "vl2re32.v", "%vd, (%rs1)",
+ semfunc: "absl::bind_front(&VlRegister, /*num_regs*/ 2, /*element_width*/ 4)", "&VlChild";
+ vl2re64{(: rs1 :), (: : vd)},
+ disasm: "vl2re64.v", "%vd, (%rs1)",
+ semfunc: "absl::bind_front(&VlRegister, /*num_regs*/ 2, /*element_width*/ 8)", "&VlChild";
+ vl4re8{(: rs1 :), (: : vd)},
+ disasm: "vl4re8.v", "%vd, (%rs1)",
+ semfunc: "absl::bind_front(&VlRegister, /*num_regs*/ 4, /*element_width*/ 1)", "&VlChild";
+ vl4re16{(: rs1 :), (: : vd)},
+ disasm: "vl4re16.v", "%vd, (%rs1)",
+ semfunc: "absl::bind_front(&VlRegister, /*num_regs*/ 4, /*element_width*/ 2)", "&VlChild";
+ vl4re32{(: rs1 :), (: : vd)},
+ disasm: "vl4re32.v", "%vd, (%rs1)",
+ semfunc: "absl::bind_front(&VlRegister, /*num_regs*/ 4, /*element_width*/ 4)", "&VlChild";
+ vl4re64{(: rs1 :), (: : vd)},
+ disasm: "vl4re64.v", "%vd, (%rs1)",
+ semfunc: "absl::bind_front(&VlRegister, /*num_regs*/ 4, /*element_width*/ 8)", "&VlChild";
+ vl8re8{(: rs1 :), (: : vd)},
+ disasm: "vl8re8.v", "%vd, (%rs1)",
+ semfunc: "absl::bind_front(&VlRegister, /*num_regs*/ 8, /*element_width*/ 1)", "&VlChild";
+ vl8re16{(: rs1 :), (: : vd)},
+ disasm: "vl8re16.v", "%vd, (%rs1)",
+ semfunc: "absl::bind_front(&VlRegister, /*num_regs*/ 8, /*element_width*/ 2)", "&VlChild";
+ vl8re32{(: rs1 :), (: : vd)},
+ disasm: "vl8re32.v", "%vd, (%rs1)",
+ semfunc: "absl::bind_front(&VlRegister, /*num_regs*/ 8, /*element_width*/ 4)", "&VlChild";
+ vl8re64{(: rs1 :), (: : vd)},
+ disasm: "vl8re64.v", "%vd, (%rs1)",
+ semfunc: "absl::bind_front(&VlRegister, /*num_regs*/ 8, /*element_width*/ 8)", "&VlChild";
+
+ // Vector load, indexed, unordered.
+ vluxei8{(: rs1, vs2, vmask:), (: : vd)},
+ disasm: "vluxei8.v", "%vd, (%rs1), %vs2, %vmask",
+ semfunc: "absl::bind_front(&VlIndexed, /*index_width*/ 1)", "&VlChild";
+ vluxei16{(: rs1, vs2, vmask:), (: : vd)},
+ disasm: "vluxei16.v", "%vd, (%rs1), %vs2, %vmask",
+ semfunc: "absl::bind_front(&VlIndexed, /*index_width*/ 2)", "&VlChild";
+ vluxei32{(: rs1, vs2, vmask:), (: : vd)},
+ disasm: "vluxei32.v", "%vd, (%rs1), %vs2, %vmask",
+ semfunc: "absl::bind_front(&VlIndexed, /*index_width*/ 4)", "&VlChild";
+ vluxei64{(: rs1, vs2, vmask:), (: : vd)},
+ disasm: "vluxei64.v", "%vd, (%rs1), %vs2, %vmask",
+ semfunc: "absl::bind_front(&VlIndexed, /*index_width*/ 8)", "&VlChild";
+
+ // Vector load, indexed, ordered.
+ vloxei8{(: rs1, vs2, vmask:), (: : vd)},
+ disasm: "vloxei8.v", "%vd, (%rs1), %vs2, %vmask",
+ semfunc: "absl::bind_front(&VlIndexed, /*index_width*/ 1)", "&VlChild";
+ vloxei16{(: rs1, vs2, vmask:), (: : vd)},
+ disasm: "vloxei16.v", "%vd, (%rs1), %vs2, %vmask",
+ semfunc: "absl::bind_front(&VlIndexed, /*index_width*/ 2)", "&VlChild";
+ vloxei32{(: rs1, vs2, vmask:), (: : vd)},
+ disasm: "vloxei32.v", "%vd, (%rs1), %vs2, %vmask",
+ semfunc: "absl::bind_front(&VlIndexed, /*index_width*/ 4)", "&VlChild";
+ vloxei64{(: rs1, vs2, vmask:), (: : vd)},
+ disasm: "vloxei64.v", "%vd, (%rs1), %vs2, %vmask",
+ semfunc: "absl::bind_front(&VlIndexed, /*index_width*/ 8)", "&VlChild";
+
+ // Vector unit-stride segment load
+ vlsege8{(: rs1, vmask, nf:), (: nf : vd)},
+ disasm: "vlseg%nf\\e.v", "%vd, (%rs1), %vmask",
+ semfunc: "absl::bind_front(&VlSegment, /*element_width*/ 1)",
+ "absl::bind_front(&VlSegmentChild, /*element_width*/ 1)";
+ vlsege16{(: rs1, vmask, nf:), (: nf : vd)},
+ disasm: "vlseg%nf\\e.v", "%vd, (%rs1), %vmask",
+ semfunc: "absl::bind_front(&VlSegment, /*element_width*/ 2)",
+ "absl::bind_front(&VlSegmentChild, /*element_width*/ 2)";
+ vlsege32{(: rs1, vmask, nf:), (: nf : vd)},
+ disasm: "vlseg%nf\\e.v", "%vd, (%rs1), %vmask",
+ semfunc: "absl::bind_front(&VlSegment, /*element_width*/ 4)",
+ "absl::bind_front(&VlSegmentChild, /*element_width*/ 4)";
+ vlsege64{(: rs1, vmask, nf:), (: nf : vd)},
+ disasm: "vlseg%nf\\e.v", "%vd, (%rs1), %vmask",
+ semfunc: "absl::bind_front(&VlSegment, /*element_width*/ 8)",
+ "absl::bind_front(&VlSegmentChild, /*element_width*/ 8)";
+
+ // Vector strided segment load.
+ vlssege8{(: rs1, rs2, vmask, nf: ), (: nf : vd)},
+ disasm: "vlssg%nf\\e8.v", "%vd, (%rs1), %rs2, %vmask",
+ semfunc: "absl::bind_front(&VlSegmentStrided, /*element_width*/ 1)",
+ "absl::bind_front(&VlSegmentChild, /*element_width*/ 1)";
+ vlssege16{(: rs1, rs2, vmask, nf: ), (: nf : vd)},/*element_width*/
+ disasm: "vlssg%nf\\e16.v", "%vd, (%rs1), %rs2, %vmask",
+ semfunc: "absl::bind_front(&VlSegmentStrided, /*element_width*/ 2)",
+ "absl::bind_front(&VlSegmentChild, /*element_width*/ 2)";
+ vlssege32{(: rs1, rs2, vmask, nf: ), (: nf : vd)},
+ disasm: "vlssg%nf\\e32.v", "%vd, (%rs1), %rs2, %vmask",
+ semfunc: "absl::bind_front(&VlSegmentStrided, /*element_width*/ 4)",
+ "absl::bind_front(&VlSegmentChild, /*element_width*/ 4)";
+ vlssege64{(: rs1, rs2, vmask, nf: ), (: nf : vd)},
+ disasm: "vlssg%nf\\e64.v", "%vd, (%rs1), %rs2, %vmask",
+ semfunc: "absl::bind_front(&VlSegmentStrided, /*element_width*/ 8)",
+ "absl::bind_front(&VlSegmentChild, /*element_width*/ 8)";
+
+ // Vector indexed segment load unordered.
+ vluxsegei8{(: rs1, vs2, vmask, nf :), (: nf : vd)},
+ disasm: "vluxseg%nf\\ei1.v", "%vd, (%rs1), %vs2, %vmask",
+ semfunc: "absl::bind_front(&VlSegmentIndexed, /*index_width*/ 1)",
+ "absl::bind_front(&VlSegmentChild, /*element_width*/ 1)";
+ vluxsegei16{(: rs1, vs2, vmask, nf :), (: nf : vd)},
+ disasm: "vluxseg%nf\\ei2.v", "%vd, (%rs1), %vs2, %vmask",
+ semfunc: "absl::bind_front(&VlSegmentIndexed, /*index_width*/ 2)",
+ "absl::bind_front(&VlSegmentChild, /*element_width*/ 2)";
+ vluxsegei32{(: rs1, vs2, vmask, nf :), (: nf : vd)},
+ disasm: "vluxseg%nf\\ei4.v", "%vd, (%rs1), %vs2, %vmask",
+ semfunc: "absl::bind_front(&VlSegmentIndexed, /*index_width*/ 4)",
+ "absl::bind_front(&VlSegmentChild, /*element_width*/ 4)";
+ vluxsegei64{(: rs1, vs2, vmask, nf :), (: nf : vd)},
+ disasm: "vluxseg%nf\\ei8.v", "%vd, (%rs1), %vs2, %vmask",
+ semfunc: "absl::bind_front(&VlSegmentIndexed, /*index_width*/ 8)",
+ "absl::bind_front(&VlSegmentChild, /*element_width*/ 8)";
+
+ // Vector indexed segment load ordered.
+
+ vloxsegei8{(: rs1, vs2, vmask, nf :), (: nf : vd)},
+ disasm: "vluxseg%nf\\ei1.v", "%vd, (%rs1), %vs2, %vmask",
+ semfunc: "absl::bind_front(&VlSegmentIndexed, /*index_width*/ 1)",
+ "absl::bind_front(&VlSegmentChild, /*element_width*/ 1)";
+ vloxsegei16{(: rs1, vs2, vmask, nf :), (: nf : vd)},
+ disasm: "vluxseg%nf\\ei2.v", "%vd, (%rs1), %vs2, %vmask",
+ semfunc: "absl::bind_front(&VlSegmentIndexed, /*index_width*/ 2)",
+ "absl::bind_front(&VlSegmentChild, /*element_width*/ 2)";
+ vloxsegei32{(: rs1, vs2, vmask, nf :), (: nf : vd)},
+ disasm: "vluxseg%nf\\ei4.v", "%vd, (%rs1), %vs2, %vmask",
+ semfunc: "absl::bind_front(&VlSegmentIndexed, /*index_width*/ 4)",
+ "absl::bind_front(&VlSegmentChild, /*element_width*/ 4)";
+ vloxsegei64{(: rs1, vs2, vmask, nf :), (: nf : vd)},
+ disasm: "vluxseg%nf\\ei8.v", "%vd, (%rs1), %vs2, %vmask",
+ semfunc: "absl::bind_front(&VlSegmentIndexed, /*index_width*/ 8)",
+ "absl::bind_front(&VlSegmentChild, /*element_width*/ 8)";
+
+ // VECTOR STORES
+
+ // Vector store, unit stride.
+ vse8{: vs3, rs1, const1, vmask : },
+ disasm: "vse8.v", "%vs3, (%rs1), %vmask",
+ semfunc: "absl::bind_front(&VsStrided, /*element_width*/ 1)";
+ vse16{: vs3, rs1, const1, vmask : },
+ disasm: "vse16.v", "%vs3, (%rs1), %vmask",
+ semfunc: "absl::bind_front(&VsStrided, /*element_width*/ 2)";
+ vse32{: vs3, rs1, const1, vmask : },
+ disasm: "vse32.v", "%vs3, (%rs1), %vmask",
+ semfunc: "absl::bind_front(&VsStrided, /*element_width*/ 4)";
+ vse64{: vs3, rs1, const1, vmask : },
+ disasm: "vse64.v", "%vs3, (%rs1), %vmask",
+ semfunc: "absl::bind_front(&VsStrided, /*element_width*/ 8)";
+
+ // Vector store mask
+ vsm{: vs3, rs1, const1, vmask_true:},
+ disasm: "vsm",
+ semfunc: "absl::bind_front(&Vsm)";
+
+ // Vector store, unit stride, fault first.
+ vse8ff{: vs3, rs1, const1, vmask:},
+ disasm: "vse8ff.v", "%vs3, (%rs1), %vmask",
+ semfunc: "absl::bind_front(&VsStrided, /*element_width*/ 1)";
+ vse16ff{: vs3, rs1, const1, vmask:},
+ disasm: "vse16ff.v", "%vs3, (%rs1), %vmask",
+ semfunc: "absl::bind_front(&VsStrided, /*element_width*/ 2)";
+ vse32ff{: vs3, rs1, const1, vmask:},
+ disasm: "vse32ff.v", "%vs3, (%rs1), %vmask",
+ semfunc: "absl::bind_front(&VsStrided, /*element_width*/ 4)";
+ vse64ff{: vs3, rs1, const1, vmask:},
+ disasm: "vse64ff.v", "%vs3, (%rs1), %vmask",
+ semfunc: "absl::bind_front(&VsStrided, /*element_width*/ 8)";
+
+ // Vector store register.
+ vs1re8{(: vs3, rs1 :)},
+ disasm: "vs1re8.v", "%vs3, (%rs1)",
+ semfunc: "absl::bind_front(&VsRegister, /*num_regs*/ 1)";
+ vs1re16{(: vs3, rs1 :)},
+ disasm: "vs1re16.v", "%vs3, (%rs1)",
+ semfunc: "absl::bind_front(&VsRegister, /*num_regs*/ 1)";
+ vs1re32{(: vs3, rs1 :)},
+ disasm: "vs1re32.v", "%vs3, (%rs1)",
+ semfunc: "absl::bind_front(&VsRegister, /*num_regs*/ 1)";
+ vs1re64{(: vs3, rs1 :)},
+ disasm: "vs1re64.v", "%vs3, (%rs1)",
+ semfunc: "absl::bind_front(&VsRegister, /*num_regs*/ 1)";
+ vs2re8{(: vs3, rs1 :)},
+ disasm: "vs2re8.v", "%vs3, (%rs1)",
+ semfunc: "absl::bind_front(&VsRegister, /*num_regs*/ 2)";
+ vs2re16{(: vs3, rs1 :)},
+ disasm: "vs2re16.v", "%vs3, (%rs1)",
+ semfunc: "absl::bind_front(&VsRegister, /*num_regs*/ 2)";
+ vs2re32{(: vs3, rs1 :)},
+ disasm: "vs2re32.v", "%vs3, (%rs1)",
+ semfunc: "absl::bind_front(&VsRegister, /*num_regs*/ 2)";
+ vs2re64{(: vs3, rs1 :)},
+ disasm: "vs2re64.v", "%vs3, (%rs1)",
+ semfunc: "absl::bind_front(&VsRegister, /*num_regs*/ 2)";
+ vs4re8{(: vs3, rs1 :)},
+ disasm: "vs4re8.v", "%vs3, (%rs1)",
+ semfunc: "absl::bind_front(&VsRegister, /*num_regs*/ 4)";
+ vs4re16{(: vs3, rs1 :)},
+ disasm: "vs4re16.v", "%vs3, (%rs1)",
+ semfunc: "absl::bind_front(&VsRegister, /*num_regs*/ 4)";
+ vs4re32{(: vs3, rs1 :)},
+ disasm: "vs4re32.v", "%vs3, (%rs1)",
+ semfunc: "absl::bind_front(&VsRegister, /*num_regs*/ 4)";
+ vs4re64{(: vs3, rs1 :)},
+ disasm: "vs4re64.v", "%vs3, (%rs1)",
+ semfunc: "absl::bind_front(&VsRegister, /*num_regs*/ 4)";
+ vs8re8{(: vs3, rs1 :)},
+ disasm: "vs8re8.v", "%vs3, (%rs1)",
+ semfunc: "absl::bind_front(&VsRegister, /*num_regs*/8)";
+ vs8re16{(: vs3, rs1 :)},
+ disasm: "vs8re16.v", "%vs3, (%rs1)",
+ semfunc: "absl::bind_front(&VsRegister, /*num_regs*/8)";
+ vs8re32{(: vs3, rs1 :)},
+ disasm: "vs8re32.v", "%vs3, (%rs1)",
+ semfunc: "absl::bind_front(&VsRegister, /*num_regs*/8)";
+ vs8re64{(: vs3, rs1 :)},
+ disasm: "vs8re64.v", "%vs3, (%rs1)",
+ semfunc: "absl::bind_front(&VsRegister, /*num_regs*/8)";
+
+ // Vector store, strided.
+ vsse8{: vs3, rs1, rs2, vmask : },
+ disasm: "vsse8.v", "%vs3, (%rs1), %rs2, %vmask",
+ semfunc: "absl::bind_front(&VsStrided, /*element_width*/ 1)";
+ vsse16{: vs3, rs1, rs2, vmask : },
+ disasm: "vsse16.v", "%vs3, (%rs1), %rs2, %vmask",
+ semfunc: "absl::bind_front(&VsStrided, /*element_width*/ 2)";
+ vsse32{: vs3, rs1, rs2, vmask : },
+ disasm: "vsse32.v", "%vs3, (%rs1), %rs2, %vmask",
+ semfunc: "absl::bind_front(&VsStrided, /*element_width*/ 4)";
+ vsse64{: vs3, rs1, rs2, vmask : },
+ disasm: "vsse64.v", "%vs3, (%rs1), %rs2, %vmask",
+ semfunc: "absl::bind_front(&VsStrided, /*element_width*/ 8)";
+
+ // Vector store, indexed, unordered.
+ vsuxei8{: vs3, rs1, vs2, vmask: },
+ disasm: "vsuxei8", "%vs3, (%rs1), %vs2, %vmask",
+ semfunc: "absl::bind_front(&VsIndexed, /*index_width*/ 1)";
+ vsuxei16{: vs3, rs1, vs2, vmask:},
+ disasm: "vsuxei16", "%vs3, (%rs1), %vs2, %vmask",
+ semfunc: "absl::bind_front(&VsIndexed, /*index_width*/ 2)";
+ vsuxei32{: vs3, rs1, vs2, vmask:},
+ disasm: "vsuxei32", "%vs3, (%rs1), %vs2, %vmask",
+ semfunc: "absl::bind_front(&VsIndexed, /*index_width*/ 4)";
+ vsuxei64{: vs3, rs1, vs2, vmask:},
+ disasm: "vsuxei64", "%vs3, (%rs1), %vs2, %vmask",
+ semfunc: "absl::bind_front(&VsIndexed, /*index_width*/ 8)";
+
+ // Vector store, indexed, unordered
+ vsoxei8{: vs3, rs1, vs2, vmask:},
+ disasm: "vsoxei8", "%vs3, (%rs1), %vs2, %vmask",
+ semfunc: "absl::bind_front(&VsIndexed, /*index_width*/ 1)";
+ vsoxei16{: vs3, rs1, vs2, vmask:},
+ disasm: "vsoxei16", "%vs3, (%rs1), %vs2, %vmask",
+ semfunc: "absl::bind_front(&VsIndexed, /*index_width*/ 2)";
+ vsoxei32{: vs3, rs1, vs2, vmask:},
+ disasm: "vsoxei32", "%vs3, (%rs1), %vs2, %vmask",
+ semfunc: "absl::bind_front(&VsIndexed, /*index_width*/ 4)";
+ vsoxei64{: vs3, rs1, vs2, vmask:},
+ disasm: "vsoxei64", "%vs3, (%rs1), %vs2, %vmask",
+ semfunc: "absl::bind_front(&VsIndexed, /*index_width*/ 8)";
+
+ // Vector unit-stride segment store.
+ vssege8{(: vs3, rs1, vmask, nf:)},
+ disasm: "vsseg%nf\\e.v", "%vs3, (%rs1), %vmask",
+ semfunc: "absl::bind_front(&VsSegment, /*element_width*/ 1)";
+ vssege16{(: vs3, rs1, vmask, nf:)},
+ disasm: "vsseg%nf\\e.v", "%vs3, (%rs1), %vmask",
+ semfunc: "absl::bind_front(&VsSegment, /*element_width*/ 2)";
+ vssege32{(: vs3, rs1, vmask, nf:)},
+ disasm: "vsseg%nf\\e.v", "%vs3, (%rs1), %vmask",
+ semfunc: "absl::bind_front(&VsSegment, /*element_width*/ 4)";
+ vssege64{(: vs3, rs1, vmask, nf:)},
+ disasm: "vsseg%nf\\e.v", "%vs3, (%rs1), %vmask",
+ semfunc: "absl::bind_front(&VsSegment, /*element_width*/ 8)";
+
+ // Vector strided segment store.
+ vsssege8{(: vs3, rs1, rs2, vmask, nf: )},
+ disasm: "vssseg%nf\\e8.v", "%vs3, (%rs1), %rs2, %vmask",
+ semfunc: "absl::bind_front(&VsSegmentStrided, /*element_width*/ 1)";
+ vsssege16{(: vs3, rs1, rs2, vmask, nf: )},
+ disasm: "vssseg%nf\\e16.v", "%vs3, (%rs1), %rs2, %vmask",
+ semfunc: "absl::bind_front(&VsSegmentStrided, /*element_width*/ 2)";
+ vsssege32{(: vs3, rs1, rs2, vmask, nf: )},
+ disasm: "vssseg%nf\\e32.v", "%vs3, (%rs1), %rs2, %vmask",
+ semfunc: "absl::bind_front(&VsSegmentStrided, /*element_width*/ 4)";
+ vsssege64{(: vs3, rs1, rs2, vmask, nf: )},
+ disasm: "vssseg%nf\\e64.v", "%vs3, (%rs1), %rs2, %vmask",
+ semfunc: "absl::bind_front(&VsSegmentStrided, /*element_width*/ 8)";
+
+ // Vector indexed segment store unordered.
+ vsuxsegei8{(: vs3, rs1, vs2, vmask, nf :)},
+ disasm: "vsuxseg%nf\\ei1.v", "%vs3, (%rs1), %vs2, %vmask",
+ semfunc: "absl::bind_front(&VsSegmentStrided, /*element_width*/ 1)";
+ vsuxsegei16{(: vs3, rs1, vs2, vmask, nf :)},
+ disasm: "vsuxseg%nf\\ei2.v", "%vs3, (%rs1), %vs2, %vmask",
+ semfunc: "absl::bind_front(&VsSegmentStrided, /*element_width*/ 2)";
+ vsuxsegei32{(: vs3, rs1, vs2, vmask, nf :)},
+ disasm: "vsuxseg%nf\\ei4.v", "%vs3, (%rs1), %vs2, %vmask",
+ semfunc: "absl::bind_front(&VsSegmentStrided, /*element_width*/ 4)";
+ vsuxsegei64{(: vs3, rs1, vs2, vmask, nf :)},
+ disasm: "vsuxseg%nf\\ei8.v", "%vs3, (%rs1), %vs2, %vmask",
+ semfunc: "absl::bind_front(&VsSegmentStrided, /*element_width*/ 8)";
+
+ // Vector indexed segment store ordered.
+ vsoxsegei8{(: vs3, rs1, vs2, vmask, nf :)},
+ disasm: "vsuxseg%nf\\ei1.v", "%vs3, (%rs1), %vs2, %vmask",
+ semfunc: "absl::bind_front(&VsSegmentIndexed, /*index_width*/ 1)";
+ vsoxsegei16{(: vs3, rs1, vs2, vmask, nf :)},
+ disasm: "vsuxseg%nf\\ei2.v", "%vs3, (%rs1), %vs2, %vmask",
+ semfunc: "absl::bind_front(&VsSegmentIndexed, /*index_width*/ 2)";
+ vsoxsegei32{(: vs3, rs1, vs2, vmask, nf :)},
+ disasm: "vsuxseg%nf\\ei4.v", "%vs3, (%rs1), %vs2, %vmask",
+ semfunc: "absl::bind_front(&VsSegmentIndexed, /*index_width*/ 4)";
+ vsoxsegei64{(: vs3, rs1, vs2, vmask, nf :)},
+ disasm: "vsuxseg%nf\\ei8.v", "%vs3, (%rs1), %vs2, %vmask",
+ semfunc: "absl::bind_front(&VsSegmentIndexed, /*index_width*/ 8)";
+
+ // Integer OPIVV, OPIVX, OPIVI.
+ vadd_vv{: vs2, vs1, vmask : vd},
+ disasm: "vadd.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vadd";
+ vadd_vx{: vs2, rs1, vmask : vd},
+ disasm: "vadd.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vadd";
+ vadd_vi{: vs2, simm5, vmask : vd},
+ disasm: "vadd.vi", "%vd, %simm5, %vmask",
+ semfunc: "&Vadd";
+ vsub_vv{: vs2, vs1, vmask : vd},
+ disasm: "vsub.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vsub";
+ vsub_vx{: vs2, rs1, vmask : vd},
+ disasm: "vsub.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vsub";
+ vrsub_vx{: vs2, rs1, vmask : vd},
+ disasm: "vrsub.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vrsub";
+ vrsub_vi{: vs2, simm5, vmask, vd},
+ disasm: "vrsub.vi", "%vd, %simm5, %vmask",
+ semfunc: "&Vrsub";
+ vminu_vv{: vs2, vs1, vmask : vd},
+ disasm: "vminu.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vminu";
+ vminu_vx{: vs2, rs1, vmask : vd},
+ disasm: "vminu.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vminu";
+ vmin_vv{: vs2, vs1, vmask : vd},
+ disasm: "vmin.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vmin";
+ vmin_vx{: vs2, rs1, vmask : vd},
+ disasm: "vmin.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vmin";
+ vmaxu_vv{: vs2, vs1, vmask : vd},
+ disasm: "vmax.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vmaxu";
+ vmaxu_vx{: vs2, rs1, vmask : vd},
+ disasm: "vmax.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vmaxu";
+ vmax_vv{: vs2, vs1, vmask : vd},
+ disasm: "vmax.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vmax";
+ vmax_vx{: vs2, rs1, vmask : vd},
+ disasm: "vmax.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vmax";
+ vand_vv{: vs2, vs1, vmask : vd},
+ disasm: "vand.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vand";
+ vand_vx{: vs2, rs1, vmask : vd},
+ disasm: "vand.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vand";
+ vand_vi{: vs2, simm5, vmask : vd},
+ disasm: "vand.vi", "%vd, %simm5, %vmask",
+ semfunc: "&Vand";
+ vor_vv{: vs2, vs1, vmask : vd},
+ disasm: "vor.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vor";
+ vor_vx{: vs2, rs1, vmask : vd},
+ disasm: "vor.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vor";
+ vor_vi{: vs2, simm5, vmask : vd},
+ disasm: "vor.vi", "%vd, %simm5, %vmask",
+ semfunc: "&Vor";
+ vxor_vv{: vs2, vs1, vmask : vd},
+ disasm: "vxor.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vxor";
+ vxor_vx{: vs2, rs1, vmask : vd},
+ disasm: "vxor.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vxor";
+ vxor_vi{: vs2, simm5, vmask : vd},
+ disasm: "vxor.vi", "%vd, %simm5, %vmask",
+ semfunc: "&Vxor";
+ vrgather_vv{: vs2, vs1, vmask: vd},
+ disasm: "vrgather.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vrgather";
+ vrgather_vx{: vs2, rs1, vmask: vd},
+ disasm: "vrgather.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vrgather";
+ vrgather_vi{: vs2, uimm5, vmask: vd},
+ disasm: "vrgather.vi", "%vd, %uimm5, %vmask",
+ semfunc: "&Vrgather";
+ vrgatherei16_vv{: vs2, vs1, vmask: vd},
+ disasm: "vrgatherei16.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vrgatherei16";
+ vslideup_vx{: vs2, rs1, vmask: vd},
+ disasm: "vslideup.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vslideup";
+ vslideup_vi{: vs2, uimm5, vmask: vd},
+ disasm: "vslideup.vi", "%vd, %vs2, %uimm5, %vmask",
+ semfunc: "&Vslideup";
+ vslidedown_vx{: vs2, rs1, vmask: vd},
+ disasm: "vslidedown.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vslidedown";
+ vslidedown_vi{: vs2, uimm5, vmask: vd},
+ disasm: "vslidedown.vi", "%vd, %vs2, %uimm5, %vmask",
+ semfunc: "&Vslidedown";
+ vadc_vv{: vs2, vs1, vmask: vd},
+ disasm: "vadc.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vadc";
+ vadc_vx{: vs2, rs1, vmask: vd},
+ disasm: "vadc.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vadc";
+ vadc_vi{: vs2, simm5, vmask: vd},
+ disasm: "vadc.vi", "%vd, %vs2, %simm5, %vmask",
+ semfunc: "&Vadc";
+ vmadc_vv{: vs2, vs1, vmask, vm: vd},
+ disasm: "vmadc.vv", "%vd, %vs2, %vs1, %vmask, %vmask",
+ semfunc: "&Vmadc";
+ vmadc_vx{: vs2, rs1, vmask, vm: vd},
+ disasm: "vmadc.vx", "%vd, %vs2, %rs1, %vmask, %vmask",
+ semfunc: "&Vmadc";
+ vmadc_vi{: vs2, simm5, vmask, vm: vd},
+ disasm: "vmadc.vi", "%vd, %vs2, %simm5, %vmask, %vmask",
+ semfunc: "&Vmadc";
+ vsbc_vv{: vs2, vs1, vmask: vd},
+ disasm: "vsbc.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vsbc";
+ vsbc_vx{: vs2, rs1, vmask: vd},
+ disasm: "vsbc.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vsbc";
+ vmsbc_vv{: vs2, vs1, vmask, vm: vd},
+ disasm: "vmsbc.vv", "%vd, %vs2, %vs1, %vmask, %vmask",
+ semfunc: "&Vmsbc";
+ vmsbc_vx{: vs2, rs1, vmask, vm: vd},
+ disasm: "vmsbc.vx", "%vd, %vs2, %rs1, %vmask, %vmask",
+ semfunc: "&Vmsbc";
+ vmerge_vv{: vs2, vs1, vmask: vd},
+ disasm: "vmerge.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vmerge";
+ vmerge_vx{: vs2, rs1, vmask: vd},
+ disasm: "vmerge.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vmerge";
+ vmerge_vi{: vs2, simm5, vmask: vd},
+ disasm: "vmerge.vi", "%vd, %vs2, %simm5, %vmask",
+ semfunc: "&Vmerge";
+ vmv_vv{: vs2, vs1, vmask_true: vd},
+ disasm: "vmv.vv", "%vd, %vs1",
+ semfunc: "&Vmerge";
+ vmv_vx{: vs2, rs1, vmask_true: vd},
+ disasm: "vmv.vx", "%vd, %rs1",
+ semfunc: "&Vmerge";
+ vmv_vi{: vs2, simm5, vmask_true: vd},
+ disasm: "vmv.vi", "%vd, %simm5",
+ semfunc: "&Vmerge";
+ vmseq_vv{: vs2, vs1, vmask: vd},
+ disasm: "vmseq.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vmseq";
+ vmseq_vx{: vs2, rs1, vmask: vd},
+ disasm: "vmseq.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vmseq";
+ vmseq_vi{: vs2, simm5, vmask: vd},
+ disasm: "vmseq.vi", "%vd, %vs2, %simm5, %vmask",
+ semfunc: "&Vmseq";
+ vmsne_vv{: vs2, vs1, vmask: vd},
+ disasm: "vmsne.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vmsne";
+ vmsne_vx{: vs2, rs1, vmask: vd},
+ disasm: "vmsne.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vmsne";
+ vmsne_vi{: vs2, simm5, vmask: vd},
+ disasm: "vmsne.vi", "%vd, %vs2, %simm5, %vmask",
+ semfunc: "&Vmsne";
+ vmsltu_vv{: vs2, vs1, vmask: vd},
+ disasm: "vmsltu.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vmsltu";
+ vmsltu_vx{: vs2, rs1, vmask: vd},
+ disasm: "vmsltu.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vmsltu";
+ vmslt_vv{: vs2, vs1, vmask: vd},
+ disasm: "vmslt.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vmslt";
+ vmslt_vx{: vs2, rs1, vmask: vd},
+ disasm: "vmslt.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vmslt";
+ vmsleu_vv{: vs2, vs1, vmask: vd},
+ disasm: "vmsleu.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vmsleu";
+ vmsleu_vx{: vs2, rs1, vmask: vd},
+ disasm: "vmsleu.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vmsleu";
+ vmsleu_vi{: vs2, simm5, vmask: vd},
+ disasm: "vmsleu.vi", "%vd, %vs2, %simm5, %vmask",
+ semfunc: "&Vmsleu";
+ vmsle_vv{: vs2, vs1, vmask: vd},
+ disasm: "vmsle.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vmsle";
+ vmsle_vx{: vs2, rs1, vmask: vd},
+ disasm: "vmsle.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vmsle";
+ vmsle_vi{: vs2, simm5, vmask: vd},
+ disasm: "vmsle.vi", "%vd, %vs2, %simm5, %vmask",
+ semfunc: "&Vmsle";
+ vmsgtu_vx{: vs2, rs1, vmask: vd},
+ disasm: "vmsgtu.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vmsgtu";
+ vmsgtu_vi{: vs2, simm5, vmask: vd},
+ disasm: "vmsgtu.vi", "%vd, %vs2, %simm5, %vmask",
+ semfunc: "&Vmsgtu";
+ vmsgt_vx{: vs2, rs1, vmask: vd},
+ disasm: "vmsgt.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vmsgt";
+ vmsgt_vi{: vs2, simm5, vmask: vd},
+ disasm: "vmsgt.vi", "%vd, %vs2, %simm5, %vmask",
+ semfunc: "&Vmsgt";
+ vsaddu_vv{: vs2, vs1, vmask: vd},
+ disasm: "vsaddu.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vsaddu";
+ vsaddu_vx{: vs2, rs1, vmask: vd},
+ disasm: "vsaddu.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vsaddu";
+ vsaddu_vi{: vs2, simm5, vmask: vd},
+ disasm: "vsaddu.vi", "%vd, %vs2, %simm5, %vmask",
+ semfunc: "&Vsaddu";
+ vsadd_vv{: vs2, vs1, vmask: vd},
+ disasm: "vsadd.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vsadd";
+ vsadd_vx{: vs2, rs1, vmask: vd},
+ disasm: "vsadd.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vsadd";
+ vsadd_vi{: vs2, simm5, vmask: vd},
+ disasm: "vsadd.vi", "%vd, %vs2, %simm5, %vmask",
+ semfunc: "&Vsadd";
+ vssubu_vv{: vs2, vs1, vmask: vd},
+ disasm: "vssubu.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vssubu";
+ vssubu_vx{: vs2, rs1, vmask: vd},
+ disasm: "vssubu.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vssubu";
+ vssub_vv{: vs2, vs1, vmask: vd},
+ disasm: "vssub.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vssub";
+ vssub_vx{: vs2, rs1, vmask: vd},
+ disasm: "vssub.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vssub";
+ vsll_vv{: vs2, vs1, vmask : vd},
+ disasm: "vsll.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vsll";
+ vsll_vx{: vs2, rs1, vmask : vd},
+ disasm: "vsll.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vsll";
+ vsll_vi{: vs2, simm5, vmask: vd},
+ disasm: "vsll.vi", "%vd, %simm5, %vmask",
+ semfunc: "&Vsll";
+ vsmul_vv{: vs2, vs1, vmask : vd},
+ disasm: "vsmul.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vsmul";
+ vsmul_vx{: vs2, rs1, vmask : vd},
+ disasm: "vsmul.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vsmul";
+ vmv1r_vi{: vs2 : vd},
+ disasm: "vmv1r.vi", "%vd, %vs2",
+ semfunc: "absl::bind_front(&Vmvr, 1)";
+ vmv2r_vi{: vs2 : vd},
+ disasm: "vmv2r.vi", "%vd, %vs2",
+ semfunc: "absl::bind_front(&Vmvr, 2)";
+ vmv4r_vi{: vs2 : vd},
+ disasm: "vmv4r.vi", "%vd, %vs2",
+ semfunc: "absl::bind_front(&Vmvr, 4)";
+ vmv8r_vi{: vs2 : vd},
+ disasm: "vmv8r.vi", "%vd, %vs2",
+ semfunc: "absl::bind_front(&Vmvr, 8)";
+ vsrl_vv{: vs2, vs1, vmask : vd},
+ disasm: "vsrl.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vsrl";
+ vsrl_vx{: vs2, rs1, vmask : vd},
+ disasm: "vsrl.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vsrl";
+ vsrl_vi{: vs2, simm5, vmask: vd},
+ disasm: "vsrl.vi", "%vd, %simm5, %vmask",
+ semfunc: "&Vsrl";
+ vsra_vv{: vs2, vs1, vmask : vd},
+ disasm: "vsra.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vsra";
+ vsra_vx{: vs2, rs1, vmask : vd},
+ disasm: "vsra.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vsra";
+ vsra_vi{: vs2, simm5, vmask: vd},
+ disasm: "vsra.vi", "%vd, %simm5, %vmask",
+ semfunc: "&Vsra";
+ vssrl_vv{: vs2, vs1, vmask: vd},
+ disasm: "vssrl.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vssrl";
+ vssrl_vx{: vs2, rs1, vmask: vd},
+ disasm: "vssrl.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vssrl";
+ vssrl_vi{: vs2, uimm5, vmask: vd},
+ disasm: "vssrl.vi", "%vd, %vs2, %uimm5, %vmask",
+ semfunc: "&Vssrl";
+ vssra_vv{: vs2, vs1, vmask: vd},
+ disasm: "vssra.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vssra";
+ vssra_vx{: vs2, rs1, vmask: vd},
+ disasm: "vssra.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vssra";
+ vssra_vi{: vs2, uimm5, vmask: vd},
+ disasm: "vssra.vi", "%vd, %vs2, %uimm5, %vmask",
+ semfunc: "&Vssra";
+ vnsrl_vv{: vs2, vs1, vmask : vd},
+ disasm: "vnsrl.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vnsrl";
+ vnsrl_vx{: vs2, rs1, vmask : vd},
+ disasm: "vnsrl.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vnsrl";
+ vnsrl_vi{: vs2, uimm5, vmask : vd},
+ disasm: "vnsrl.vi", "%vd, %vs2, %uimm5, %vmask",
+ semfunc: "&Vnsrl";
+ vnsra_vv{: vs2, vs1, vmask : vd},
+ disasm: "vnsra.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vnsra";
+ vnsra_vx{: vs2, rs1, vmask : vd},
+ disasm: "vnsra.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vnsra";
+ vnsra_vi{: vs2, uimm5, vmask : vd},
+ disasm: "vnsra.vi", "%vd, %vs2, %uimm5, %vmask",
+ semfunc: "&Vnsra";
+ vnclipu_vv{: vs2, vs1, vmask : vd},
+ disasm: "vnclipu_vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vnclipu";
+ vnclipu_vx{: vs2, rs1, vmask : vd},
+ disasm: "vnclipu_vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vnclipu";
+ vnclipu_vi{: vs2, uimm5, vmask : vd},
+ disasm: "vnclipu_vi", "%vd, %vs2, %uimm5, %vmask",
+ semfunc: "&Vnclipu";
+ vnclip_vv{: vs2, vs1, vmask : vd},
+ disasm: "vnclip_vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vnclip";
+ vnclip_vx{: vs2, rs1, vmask : vd},
+ disasm: "vnclip_vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vnclip";
+ vnclip_vi{: vs2, uimm5, vmask : vd},
+ disasm: "vnclip_vi", "%vd, %vs2, %uimm5, %vmask",
+ semfunc: "&Vnclip";
+ vwredsumu_vv{: vs2, vs1, vmask: vd},
+ disasm: "vwredsumu.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vwredsumu";
+ vwredsum_vv{: vs2, vs1, vmask: vd},
+ disasm: "vwredsum.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vwredsum";
+
+ // Integer OPMVV, OPMVX.
+ vredsum_vv{: vs2, vs1, vmask: vd},
+ disasm: "vredsum.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vredsum";
+ vredand_vv{: vs2, vs1, vmask: vd},
+ disasm: "vredand.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vredand";
+ vredor_vv{: vs2, vs1, vmask: vd},
+ disasm: "vredor.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vredor";
+ vredxor_vv{: vs2, vs1, vmask: vd},
+ disasm: "vredxor.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vredxor";
+ vredminu_vv{: vs2, vs1, vmask: vd},
+ disasm: "vredminu.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vredminu";
+ vredmin_vv{: vs2, vs1, vmask: vd},
+ disasm: "vredmin.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vredmin";
+ vredmaxu_vv{: vs2, vs1, vmask: vd},
+ disasm: "vredmaxu.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vredmaxu";
+ vredmax_vv{: vs2, vs1, vmask: vd},
+ disasm: "vredmax.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vredmax";
+ vaaddu_vv{: vs2, vs1, vmask: vd},
+ disasm: "vaaddu.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vaaddu";
+ vaaddu_vx{: vs2, rs1, vmask: vd},
+ disasm: "vaaddu.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vaaddu";
+ vaadd_vv{: vs2, vs1, vmask: vd},
+ disasm: "vaadd.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vaadd";
+ vaadd_vx{: vs2, rs1, vmask: vd},
+ disasm: "vaadd.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vaadd";
+ vasubu_vv{: vs2, vs1, vmask: vd},
+ disasm: "vasubu.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vasubu";
+ vasubu_vx{: vs2, rs1, vmask: vd},
+ disasm: "vasubu.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vasubu";
+ vasub_vv{: vs2, vs1, vmask: vd},
+ disasm: "vasub.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vasub";
+ vasub_vx{: vs2, rs1, vmask: vd},
+ disasm: "vasub.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vasub";
+ vslide1up_vx{: vs2, rs1, vmask: vd},
+ disasm: "vslide1up.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vslide1up";
+ vslide1down_vx{: vs2, rs1, vmask: vd},
+ disasm: "vslide1down.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vslide1down";
+ vcompress_vv{: vs2, vs1: vd},
+ disasm: "vcompress.vv", "%vd, %vs2, %vs1",
+ semfunc: "&Vcompress";
+ vmandnot_vv{: vs2, vs1: vd},
+ disasm: "vwmandnot.vv", "%vd, %vs2, %vs1",
+ semfunc: "&Vmandnot";
+ vmand_vv{: vs2, vs1: vd},
+ disasm: "vmand.vv", "%vd, %vs2, %vs1",
+ semfunc: "&Vmand";
+ vmor_vv{: vs2, vs1: vd},
+ disasm: "vmor.vv", "%vd, %vs2, %vs1",
+ semfunc: "&Vmor";
+ vmxor_vv{: vs2, vs1: vd},
+ disasm: "vmxor.vv", "%vd, %vs2, %vs1",
+ semfunc: "&Vmxor";
+ vmornot_vv{: vs2, vs1: vd},
+ disasm: "vmornot.vv", "%vd, %vs2, %vs1",
+ semfunc: "&Vmornot";
+ vmnand_vv{: vs2, vs1: vd},
+ disasm: "vmnand.vv", "%vd, %vs2, %vs1",
+ semfunc: "&Vmnand";
+ vmnor_vv{: vs2, vs1: vd},
+ disasm: "vmnor.vv", "%vd, %vs2, %vs1",
+ semfunc: "&Vmnor";
+ vmxnor_vv{: vs2, vs1: vd},
+ disasm: "vmxnor.vv", "%vd, %vs2, %vs1",
+ semfunc: "&Vmxnor";
+ vdivu_vv{: vs2, vs1, vmask: vd},
+ disasm: "vdivu.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vdivu";
+ vdivu_vx{: vs2, rs1, vmask: vd},
+ disasm: "vdivu.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vdivu";
+ vdiv_vv{: vs2, vs1, vmask: vd},
+ disasm: "vdiv.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vdiv";
+ vdiv_vx{: vs2, rs1, vmask: vd},
+ disasm: "vdiv.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vdiv";
+ vremu_vv{: vs2, vs1, vmask: vd},
+ disasm: "vremu.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vremu";
+ vremu_vx{: vs2, rs1, vmask: vd},
+ disasm: "vremu.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vremu";
+ vrem_vv{: vs2, vs1, vmask: vd},
+ disasm: "vrem.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vrem";
+ vrem_vx{: vs2, rs1, vmask: vd},
+ disasm: "vrem.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vrem";
+ vmulhu_vv{: vs2, vs1, vmask: vd},
+ disasm: "vmulhu.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vmulhu";
+ vmulhu_vx{: vs2, rs1, vmask: vd},
+ disasm: "vmulhu.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vmulhu";
+ vmul_vv{: vs2, vs1, vmask: vd},
+ disasm: "vmul.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vmul";
+ vmul_vx{: vs2, rs1, vmask: vd},
+ disasm: "vmul.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vmul";
+ vmulhsu_vv{: vs2, vs1, vmask: vd},
+ disasm: "vmulhsu.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vmulhsu";
+ vmulhsu_vx{: vs2, rs1, vmask: vd},
+ disasm: "vmulhsu.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vmulhsu";
+ vmulh_vv{: vs2, vs1, vmask: vd},
+ disasm: "vmulh.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vmulh";
+ vmulh_vx{: vs2, rs1, vmask: vd},
+ disasm: "vmulh.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vmulh";
+ vmadd_vv{: vs2, vs1, vd, vmask: vd},
+ disasm: "vmadd.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vmadd";
+ vmadd_vx{: vs2, rs1, vd, vmask: vd},
+ disasm: "vmadd.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vmadd";
+ vnmsub_vv{: vs2, vs1, vd, vmask: vd},
+ disasm: "vnmsub.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vnmsub";
+ vnmsub_vx{: vs2, rs1, vd, vmask: vd},
+ disasm: "vnmsub.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vnmsub";
+ vmacc_vv{: vs2, vs1, vd, vmask: vd},
+ disasm: "vmacc.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vmacc";
+ vmacc_vx{: vs2, rs1, vd, vmask: vd},
+ disasm: "vmacc.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vmacc";
+ vnmsac_vv{: vs2, vs1, vd, vmask: vd},
+ disasm: "vnmsac.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vnmsac";
+ vnmsac_vx{: vs2, rs1, vd, vmask: vd},
+ disasm: "vnmsac.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vnmsac";
+ vwaddu_vv{: vs2, vs1, vmask : vd},
+ disasm: "vwaddu.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vwaddu";
+ vwaddu_vx{: vs2, rs1, vmask : vd},
+ disasm: "vwaddu.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vwaddu";
+ vwadd_vv{: vs2, vs1, vmask : vd},
+ disasm: "vwadd_vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vwadd";
+ vwadd_vx{: vs2, rs1, vmask : vd},
+ disasm: "vwadd.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vwadd";
+ vwsubu_vv{: vs2, vs1, vmask : vd},
+ disasm: "vwsubu.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vwsubu";
+ vwsubu_vx{: vs2, rs1, vmask : vd},
+ disasm: "vwsubu.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vwsubu";
+ vwsub_vv{: vs2, vs1, vmask : vd},
+ disasm: "vwsub.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vwsub";
+ vwsub_vx{: vs2, rs1, vmask : vd},
+ disasm: "vwsub.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vwsub";
+ vwaddu_w_vv{: vs2, vs1, vmask : vd},
+ disasm: "vwaddu.wv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vwadduw";
+ vwaddu_w_vx{: vs2, rs1, vmask : vd},
+ disasm: "vwaddu.wx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vwadduw";
+ vwadd_w_vv{: vs2, vs1, vmask : vd},
+ disasm: "vwadd.wv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vwaddw";
+ vwadd_w_vx{: vs2, rs1, vmask : vd},
+ disasm: "vwadd.wx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vwaddw";
+ vwsubu_w_vv{: vs2, vs1, vmask : vd},
+ disasm: "vwsubu.wv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vwsubuw";
+ vwsubu_w_vx{: vs2, rs1, vmask : vd},
+ disasm: "vwsubu.wx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vwsubuw";
+ vwsub_w_vv{: vs2, vs1, vmask : vd},
+ disasm: "vwsub.wv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vwsubw";
+ vwsub_w_vx{: vs2, rs1, vmask : vd},
+ disasm: "vwsub.wx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vwsubw";
+ vwmulu_vv{: vs2, vs1, vmask: vd},
+ disasm: "vwmulu.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vwmulu";
+ vwmulu_vx{: vs2, rs1, vmask: vd},
+ disasm: "vwmulu.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vwmulu";
+ vwmulsu_vv{: vs2, vs1, vmask: vd},
+ disasm: "vwmulsu.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vwmulsu";
+ vwmulsu_vx{: vs2, rs1, vmask: vd},
+ disasm: "vwmulsu.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vwmulsu";
+ vwmul_vv{: vs2, vs1, vmask: vd},
+ disasm: "vwmul.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vwmul";
+ vwmul_vx{: vs2, rs1, vmask: vd},
+ disasm: "vwmul.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vwmul";
+ vwmaccu_vv{: vs2, vs1, vd, vmask: vd},
+ disasm: "vwmaccu.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vwmaccu";
+ vwmaccu_vx{: vs2, rs1, vd, vmask: vd},
+ disasm: "vwmaccu.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vwmaccu";
+ vwmacc_vv{: vs2, vs1, vd, vmask: vd},
+ disasm: "vwmacc.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vwmacc";
+ vwmacc_vx{: vs2, rs1, vd, vmask: vd},
+ disasm: "vwmacc.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vwmacc";
+ vwmaccus_vv{: vs2, vs1, vd, vmask: vd},
+ disasm: "vwmaccus.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vwmaccus";
+ vwmaccus_vx{: vs2, rs1, vd, vmask: vd},
+ disasm: "vwmaccus.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vwmaccus";
+ vwmaccsu_vv{: vs2, vs1, vd, vmask: vd},
+ disasm: "vwmaccsu.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vwmaccsu";
+ vwmaccsu_vx{: vs2, rs1, vd, vmask: vd},
+ disasm: "vwmaccsu.vx", "%vd, %vs2, %rs1, %vmask",
+ semfunc: "&Vwmaccsu";
+
+
+ // VWXUNARY0
+ vmv_x_s{: vs2 : rd},
+ disasm: "vmv.x.s", "%rd, %vs2",
+ semfunc: "&VmvToScalar";
+ vcpop{: vs2, vmask: rd},
+ disasm: "vcpop", "%rd, %vs2, %vmask",
+ semfunc: "&Vcpop";
+ vfirst{: vs2, vmask: rd},
+ disasm: "vfirst", "%rd, %vs2, %vmask",
+ semfunc: "&Vfirst";
+ // VRXUNARY0
+ vmv_s_x{: rs1 : vd},
+ disasm: "vmv.s.x", "%vd, %rs1",
+ semfunc: "&VmvFromScalar";
+ // VXUNARY0
+ vzext_vf8{: vs2, vmask: vd},
+ disasm: "vzext.vf8", "%vd, %vs2, %vmask",
+ semfunc: "&Vzext8";
+ vsext_vf8{: vs2, vmask: vd},
+ disasm: "vsext.vf8", "%vd, %vs2, %vmask",
+ semfunc: "&Vsext8";
+ vzext_vf4{: vs2, vmask: vd},
+ disasm: "vzext.vf4", "%vd, %vs2, %vmask",
+ semfunc: "&Vzext4";
+ vsext_vf4{: vs2, vmask: vd},
+ disasm: "vsext.vf4", "%vd, %vs2, %vmask",
+ semfunc: "&Vsext4";
+ vzext_vf2{: vs2, vmask: vd},
+ disasm: "vzext.vf2", "%vd, %vs2, %vmask",
+ semfunc: "&Vzext2";
+ vsext_vf2{: vs2, vmask: vd},
+ disasm: "vsext.vf2", "%vd, %vs2, %vmask",
+ semfunc: "&Vsext2";
+ // VMUNARY0
+ vmsbf{:vs2, vmask: vd},
+ disasm: "vmsbf.m", "%vd, %vs2, %vmask",
+ semfunc: "&Vmsbf";
+ vmsof{:vs2, vmask: vd},
+ disasm: "vmsof.m", "%vd, %vs2, %vmask",
+ semfunc: "&Vmsof";
+ vmsif{:vs2, vmask: vd},
+ disasm: "vmsif.m", "%vd, %vs2, %vmask",
+ semfunc: "&Vmsif";
+ viota{:vs2, vmask: vd},
+ disasm: "viota.m", "%vd, %vs2, %vmask",
+ semfunc: "&Viota";
+ vid{: vmask: vd},
+ disasm: "vid.v", "%vd, %vmask",
+ semfunc: "&Vid";
+ }
+}
+
diff --git a/cheriot/riscv_cheriot_vector_fp.bin_fmt b/cheriot/riscv_cheriot_vector_fp.bin_fmt
new file mode 100644
index 0000000..d8354ad
--- /dev/null
+++ b/cheriot/riscv_cheriot_vector_fp.bin_fmt
@@ -0,0 +1,133 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Cheriot RiscV vector floating point instruction encodings.
+
+instruction group RiscVVFPInst32[32] : Inst32Format {
+ // FP: OPFVV, OPFVF
+ //opfvv : VArith : func6 == 0bxxx'xxx, func3 == 0b001, opcode == 0b101'0111;
+ //opfvf : VArith : func6 == 0bxxx'xxx, func3 == 0b101, opcode == 0b101'0111;
+
+ vfadd_vv : VArith : func6 == 0b000'000, func3 == 0b001, opcode == 0b101'0111;
+ vfadd_vf : VArith : func6 == 0b000'000, func3 == 0b101, opcode == 0b101'0111;
+ vfredusum_vv : VArith : func6 == 0b000'001, func3 == 0b001, opcode == 0b101'0111;
+ vfsub_vv : VArith : func6 == 0b000'010, func3 == 0b001, opcode == 0b101'0111;
+ vfsub_vf : VArith : func6 == 0b000'010, func3 == 0b101, opcode == 0b101'0111;
+ vfredosum_vv : VArith : func6 == 0b000'011, func3 == 0b001, opcode == 0b101'0111;
+ vfmin_vv : VArith : func6 == 0b000'100, func3 == 0b001, opcode == 0b101'0111;
+ vfmin_vf : VArith : func6 == 0b000'100, func3 == 0b101, opcode == 0b101'0111;
+ vfredmin_vv : VArith : func6 == 0b000'101, func3 == 0b001, opcode == 0b101'0111;
+ vfmax_vv : VArith : func6 == 0b000'110, func3 == 0b001, opcode == 0b101'0111;
+ vfmax_vf : VArith : func6 == 0b000'110, func3 == 0b101, opcode == 0b101'0111;
+ vfredmax_vv : VArith : func6 == 0b000'111, func3 == 0b001, opcode == 0b101'0111;
+ vfsgnj_vv : VArith : func6 == 0b001'000, func3 == 0b001, opcode == 0b101'0111;
+ vfsgnj_vf : VArith : func6 == 0b001'000, func3 == 0b101, opcode == 0b101'0111;
+ vfsgnjn_vv : VArith : func6 == 0b001'001, func3 == 0b001, opcode == 0b101'0111;
+ vfsgnjn_vf : VArith : func6 == 0b001'001, func3 == 0b101, opcode == 0b101'0111;
+ vfsgnjx_vv : VArith : func6 == 0b001'010, func3 == 0b001, opcode == 0b101'0111;
+ vfsgnjx_vf : VArith : func6 == 0b001'010, func3 == 0b101, opcode == 0b101'0111;
+ vfslide1up_vf : VArith : func6 == 0b001'110, func3 == 0b101, opcode == 0b101'0111;
+ vfslide1down_vf : VArith : func6 == 0b001'111, func3 == 0b101, opcode == 0b101'0111;
+ vfmv_vf : VArith : func6 == 0b010'111, vm == 1, vs2 == 0, func3 == 0b101, opcode == 0b101'0111;
+ vfmerge_vf : VArith : func6 == 0b010'111, vm == 0, func3 == 0b101, opcode == 0b101'0111;
+ vmfeq_vv : VArith : func6 == 0b011'000, func3 == 0b001, opcode == 0b101'0111;
+ vmfeq_vf : VArith : func6 == 0b011'000, func3 == 0b101, opcode == 0b101'0111;
+ vmfle_vv : VArith : func6 == 0b011'001, func3 == 0b001, opcode == 0b101'0111;
+ vmfle_vf : VArith : func6 == 0b011'001, func3 == 0b101, opcode == 0b101'0111;
+ vmflt_vv : VArith : func6 == 0b011'011, func3 == 0b001, opcode == 0b101'0111;
+ vmflt_vf : VArith : func6 == 0b011'011, func3 == 0b101, opcode == 0b101'0111;
+ vmfne_vv : VArith : func6 == 0b011'100, func3 == 0b001, opcode == 0b101'0111;
+ vmfne_vf : VArith : func6 == 0b011'100, func3 == 0b101, opcode == 0b101'0111;
+ vmfgt_vf : VArith : func6 == 0b011'101, func3 == 0b101, opcode == 0b101'0111;
+ vmfge_vf : VArith : func6 == 0b011'111, func3 == 0b101, opcode == 0b101'0111;
+ vfdiv_vv : VArith : func6 == 0b100'000, func3 == 0b001, opcode == 0b101'0111;
+ vfdiv_vf : VArith : func6 == 0b100'000, func3 == 0b101, opcode == 0b101'0111;
+ vfrdiv_vf : VArith : func6 == 0b100'001, func3 == 0b101, opcode == 0b101'0111;
+ vfmul_vv : VArith : func6 == 0b100'100, func3 == 0b001, opcode == 0b101'0111;
+ vfmul_vf : VArith : func6 == 0b100'100, func3 == 0b101, opcode == 0b101'0111;
+ vfrsub_vf : VArith : func6 == 0b100'111, func3 == 0b101, opcode == 0b101'0111;
+ vfmadd_vv : VArith : func6 == 0b101'000, func3 == 0b001, opcode == 0b101'0111;
+ vfmadd_vf : VArith : func6 == 0b101'000, func3 == 0b101, opcode == 0b101'0111;
+ vfnmadd_vv : VArith : func6 == 0b101'001, func3 == 0b001, opcode == 0b101'0111;
+ vfnmadd_vf : VArith : func6 == 0b101'001, func3 == 0b101, opcode == 0b101'0111;
+ vfmsub_vv : VArith : func6 == 0b101'010, func3 == 0b001, opcode == 0b101'0111;
+ vfmsub_vf : VArith : func6 == 0b101'010, func3 == 0b101, opcode == 0b101'0111;
+ vfnmsub_vv : VArith : func6 == 0b101'011, func3 == 0b001, opcode == 0b101'0111;
+ vfnmsub_vf : VArith : func6 == 0b101'011, func3 == 0b101, opcode == 0b101'0111;
+ vfmacc_vv : VArith : func6 == 0b101'100, func3 == 0b001, opcode == 0b101'0111;
+ vfmacc_vf : VArith : func6 == 0b101'100, func3 == 0b101, opcode == 0b101'0111;
+ vfnmacc_vv : VArith : func6 == 0b101'101, func3 == 0b001, opcode == 0b101'0111;
+ vfnmacc_vf : VArith : func6 == 0b101'101, func3 == 0b101, opcode == 0b101'0111;
+ vfmsac_vv : VArith : func6 == 0b101'110, func3 == 0b001, opcode == 0b101'0111;
+ vfmsac_vf : VArith : func6 == 0b101'110, func3 == 0b101, opcode == 0b101'0111;
+ vfnmsac_vv : VArith : func6 == 0b101'111, func3 == 0b001, opcode == 0b101'0111;
+ vfnmsac_vf : VArith : func6 == 0b101'111, func3 == 0b101, opcode == 0b101'0111;
+ vfwadd_vv : VArith : func6 == 0b110'000, func3 == 0b001, opcode == 0b101'0111;
+ vfwadd_vf : VArith : func6 == 0b110'000, func3 == 0b101, opcode == 0b101'0111;
+ vfwredusum_vv : VArith : func6 == 0b110'001, func3 == 0b001, opcode == 0b101'0111;
+ vfwsub_vv : VArith : func6 == 0b110'010, func3 == 0b001, opcode == 0b101'0111;
+ vfwsub_vf : VArith : func6 == 0b110'010, func3 == 0b101, opcode == 0b101'0111;
+ vfwredosum_vv : VArith : func6 == 0b110'011, func3 == 0b001, opcode == 0b101'0111;
+ vfwadd_w_vv : VArith : func6 == 0b110'100, func3 == 0b001, opcode == 0b101'0111;
+ vfwadd_w_vf : VArith : func6 == 0b110'100, func3 == 0b101, opcode == 0b101'0111;
+ vfwsub_w_vv : VArith : func6 == 0b110'110, func3 == 0b001, opcode == 0b101'0111;
+ vfwsub_w_vf : VArith : func6 == 0b110'110, func3 == 0b101, opcode == 0b101'0111;
+ vfwmul_vv : VArith : func6 == 0b111'000, func3 == 0b001, opcode == 0b101'0111;
+ vfwmul_vf : VArith : func6 == 0b111'000, func3 == 0b101, opcode == 0b101'0111;
+ vfwmacc_vv : VArith : func6 == 0b111'100, func3 == 0b001, opcode == 0b101'0111;
+ vfwmacc_vf : VArith : func6 == 0b111'100, func3 == 0b101, opcode == 0b101'0111;
+ vfwnmacc_vv : VArith : func6 == 0b111'101, func3 == 0b001, opcode == 0b101'0111;
+ vfwnmacc_vf : VArith : func6 == 0b111'101, func3 == 0b101, opcode == 0b101'0111;
+ vfwmsac_vv : VArith : func6 == 0b111'110, func3 == 0b001, opcode == 0b101'0111;
+ vfwmsac_vf : VArith : func6 == 0b111'110, func3 == 0b101, opcode == 0b101'0111;
+ vfwnmsac_vv : VArith : func6 == 0b111'111, func3 == 0b001, opcode == 0b101'0111;
+ vfwnmsac_vf : VArith : func6 == 0b111'111, func3 == 0b101, opcode == 0b101'0111;
+
+ // VWFUNARY0 vv: VArith : func6 == 0b010'000, func3 == 0b001, opcode == 0b101'0111;
+ vfmv_f_s : VArith : func6 == 0b010'000, vs1 == 0, func3 == 0b001, opcode == 0b101'0111;
+
+ // VRFUNARY0 vf: VArith : func6 == 0b010'000, func3 == 0b101, opcode == 0b101'0111;
+ vfmv_s_f : VArith : func6 == 0b010'000, vs2 == 0, func3 == 0b101, opcode == 0b101'0111;
+
+ // VFUNARY0 vv: VArith : func6 == 0b010'010, func3 == 0b001, opcode == 0b101'0111;
+ vfcvt_xu_f_v : VArith : func6 == 0b010'010, vs1 == 0b00000, func3 == 0b001, opcode == 0b101'0111;
+ vfcvt_x_f_v : VArith : func6 == 0b010'010, vs1 == 0b00001, func3 == 0b001, opcode == 0b101'0111;
+ vfcvt_f_xu_v : VArith : func6 == 0b010'010, vs1 == 0b00010, func3 == 0b001, opcode == 0b101'0111;
+ vfcvt_f_x_v : VArith : func6 == 0b010'010, vs1 == 0b00011, func3 == 0b001, opcode == 0b101'0111;
+ vfcvt_rtz_xu_f_v : VArith : func6 == 0b010'010, vs1 == 0b00110, func3 == 0b001, opcode == 0b101'0111;
+ vfcvt_rtz_x_f_v : VArith : func6 == 0b010'010, vs1 == 0b00111, func3 == 0b001, opcode == 0b101'0111;
+
+ vfwcvt_xu_f_v : VArith : func6 == 0b010'010, vs1 == 0b01000, func3 == 0b001, opcode == 0b101'0111;
+ vfwcvt_x_f_v : VArith : func6 == 0b010'010, vs1 == 0b01001, func3 == 0b001, opcode == 0b101'0111;
+ vfwcvt_f_xu_v : VArith : func6 == 0b010'010, vs1 == 0b01010, func3 == 0b001, opcode == 0b101'0111;
+ vfwcvt_f_x_v : VArith : func6 == 0b010'010, vs1 == 0b01011, func3 == 0b001, opcode == 0b101'0111;
+ vfwcvt_f_f_v : VArith : func6 == 0b010'010, vs1 == 0b01100, func3 == 0b001, opcode == 0b101'0111;
+ vfwcvt_rtz_xu_f_v: VArith : func6 == 0b010'010, vs1 == 0b01110, func3 == 0b001, opcode == 0b101'0111;
+ vfwcvt_rtz_x_f_v : VArith : func6 == 0b010'010, vs1 == 0b01111, func3 == 0b001, opcode == 0b101'0111;
+
+ vfncvt_xu_f_w : VArith : func6 == 0b010'010, vs1 == 0b10000, func3 == 0b001, opcode == 0b101'0111;
+ vfncvt_x_f_w : VArith : func6 == 0b010'010, vs1 == 0b10001, func3 == 0b001, opcode == 0b101'0111;
+ vfncvt_f_xu_w : VArith : func6 == 0b010'010, vs1 == 0b10010, func3 == 0b001, opcode == 0b101'0111;
+ vfncvt_f_x_w : VArith : func6 == 0b010'010, vs1 == 0b10011, func3 == 0b001, opcode == 0b101'0111;
+ vfncvt_f_f_w : VArith : func6 == 0b010'010, vs1 == 0b10100, func3 == 0b001, opcode == 0b101'0111;
+ vfncvt_rod_f_f_w : VArith : func6 == 0b010'010, vs1 == 0b10101, func3 == 0b001, opcode == 0b101'0111;
+ vfncvt_rtz_xu_f_w: VArith : func6 == 0b010'010, vs1 == 0b10110, func3 == 0b001, opcode == 0b101'0111;
+ vfncvt_rtz_x_f_w : VArith : func6 == 0b010'010, vs1 == 0b10111, func3 == 0b001, opcode == 0b101'0111;
+
+ // VFUNARY1 vv: VArith : func6 == 0b010'011, func3 == 0b001, opcode == 0b101'0111;
+ vfsqrt_v : VArith : func6 == 0b010'011, vs1 == 0b00000, func3 == 0b001, opcode == 0b101'0111;
+ vfrsqrt7_v : VArith : func6 == 0b010'011, vs1 == 0b00100, func3 == 0b001, opcode == 0b101'0111;
+ vfrec7_v : VArith : func6 == 0b010'011, vs1 == 0b00101, func3 == 0b001, opcode == 0b101'0111;
+ vfclass_v : VArith : func6 == 0b010'011, vs1 == 0b10000, func3 == 0b001, opcode == 0b101'0111;
+};
diff --git a/cheriot/riscv_cheriot_vector_fp.isa b/cheriot/riscv_cheriot_vector_fp.isa
new file mode 100644
index 0000000..867df70
--- /dev/null
+++ b/cheriot/riscv_cheriot_vector_fp.isa
@@ -0,0 +1,342 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Definitions of RiscV vector floating point instructions.
+
+// First disasm field is 18 char wide and left justified.
+disasm widths = {-18};
+
+slot riscv_cheriot_vector_fp {
+ includes {
+ #include "cheriot/riscv_cheriot_vector_fp_compare_instructions.h"
+ #include "cheriot/riscv_cheriot_vector_fp_instructions.h"
+ #include "cheriot/riscv_cheriot_vector_fp_reduction_instructions.h"
+ #include "cheriot/riscv_cheriot_vector_fp_unary_instructions.h"
+ #include "absl/functional/bind_front.h"
+ }
+ default size = 4;
+ default latency = 0;
+ default opcode =
+ disasm: "Unimplemented instruction at 0x%(@:08x)",
+ semfunc: "&RV32VUnimplementedInstruction";
+ opcodes {
+ // Floating point, OPFVV, OPFVF.
+ vfadd_vv{: vs2, vs1, vmask : vd},
+ disasm: "vfadd.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vfadd";
+ vfadd_vf{: vs2, fs1, vmask : vd},
+ disasm: "vfadd.vf", "%vd, %vs2, %fs1, %vmask",
+ semfunc: "&Vfadd";
+ vfredusum_vv{: vs2, vs1, vmask : vd},
+ disasm: "vfredusum.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vfredosum";
+ vfsub_vv{: vs2, vs1, vmask : vd},
+ disasm: "vfsub.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vfsub";
+ vfsub_vf{: vs2, fs1, vmask : vd},
+ disasm: "vfsub.vf", "%vd, %vs2, %fs1, %vmask",
+ semfunc: "&Vfsub";
+ vfredosum_vv{: vs2, vs1, vmask : vd},
+ disasm: "vfredosum.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vfredosum";
+ vfmin_vv{: vs2, vs1, vmask: vd, fflags},
+ disasm: "vfmin.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vfmin";
+ vfmin_vf{: vs2, fs1, vmask : vd, fflags},
+ disasm: "vfmin.vf", "%vd, %vs2, %fs1, %vmask",
+ semfunc: "&Vfmin";
+ vfredmin_vv{: vs2, vs1, vmask: vd},
+ disasm: "vfredmin.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vfredmin";
+ vfmax_vv{: vs2, vs1, vmask: vd, fflags},
+ disasm: "vfmax.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vfmax";
+ vfmax_vf{: vs2, fs1, vmask : vd, fflags},
+ disasm: "vfmax.vf", "%vd, %vs2, %fs1, %vmask",
+ semfunc: "&Vfmax";
+ vfredmax_vv{: vs2, vs1, vmask: vd},
+ disasm: "vfredmax.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vfredmax";
+ vfsgnj_vv{: vs2, vs1, vmask: vd},
+ disasm: "vfsgnj.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vfsgnj";
+ vfsgnj_vf{: vs2, fs1, vmask : vd},
+ disasm: "vfsgnj.vf", "%vd, %vs2, %fs1, %vmask",
+ semfunc: "&Vfsgnj";
+ vfsgnjn_vv{: vs2, vs1, vmask: vd},
+ disasm: "vfsgnjn.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vfsgnjn";
+ vfsgnjn_vf{: vs2, fs1, vmask : vd},
+ disasm: "vfsgnjn.vf", "%vd, %vs2, %fs1, %vmask",
+ semfunc: "&Vfsgnjn";
+ vfsgnjx_vv{: vs2, vs1, vmask: vd},
+ disasm: "vfsgnjx.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vfsgnjx";
+ vfsgnjx_vf{: vs2, fs1, vmask : vd},
+ disasm: "vfsgnjx.vf", "%vd, %vs2, %fs1, %vmask",
+ semfunc: "&Vfsgnjx";
+ vfslide1up_vf{: vs2, fs1, vmask : vd},
+ disasm: "vfslide1up.vf", "%vd, %vs2, %fs1, %vmask",
+ semfunc: "&Vfslide1up";
+ vfslide1down_vf{: vs2, fs1, vmask : vd},
+ disasm: "vfslide1down.vf", "%vd, %vs2, %fs1, %vmask",
+ semfunc: "&Vfslide1down";
+ vfmv_vf{: fs1, vmask : vd},
+ disasm: "vfmv.vf", "%vd, %fs1, %vmask",
+ semfunc: "&Vfmvvf";
+ vfmerge_vf{: vs2, vs1, vmask : vd},
+ disasm: "vfmerge.vf", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vfmerge";
+ vmfeq_vv{: vs2, vs1, vmask : vd},
+ disasm: "vmfeq.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vmfeq";
+ vmfeq_vf{: vs2, fs1, vmask : vd},
+ disasm: "vmfeq.vf", "%vd, %vs2, %fs1, %vmask",
+ semfunc: "&Vmfeq";
+ vmfle_vv{: vs2, vs1, vmask : vd},
+ disasm: "vmfle.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vmfle";
+ vmfle_vf{: vs2, fs1, vmask : vd},
+ disasm: "vmfle.vf", "%vd, %vs2, %fs1, %vmask",
+ semfunc: "&Vmfle";
+ vmflt_vv{: vs2, vs1, vmask : vd},
+ disasm: "vmflt.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vmflt";
+ vmflt_vf{: vs2, fs1, vmask : vd},
+ disasm: "vmflt.vf", "%vd, %vs2, %fs1, %vmask",
+ semfunc: "&Vmflt";
+ vmfne_vv{: vs2, vs1, vmask : vd},
+ disasm: "vmfne.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vmfne";
+ vmfne_vf{: vs2, fs1, vmask : vd},
+ disasm: "vmfne.vf", "%vd, %vs2, %fs1, %vmask",
+ semfunc: "&Vmfne";
+ vmfgt_vf{: vs2, fs1, vmask : vd},
+ disasm: "vmfgt.vf", "%vd, %vs2, %fs1, %vmask",
+ semfunc: "&Vmfgt";
+ vmfge_vf{: vs2, fs1, vmask : vd},
+ disasm: "vmfge.vf", "%vd, %vs2, %fs1, %vmask",
+ semfunc: "&Vmfge";
+ vfdiv_vv{: vs2, vs1, vmask : vd},
+ disasm: "vfdiv.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vfdiv";
+ vfdiv_vf{: vs2, fs1, vmask : vd},
+ disasm: "vfdiv.vf", "%vd, %vs2, %fs1, %vmask",
+ semfunc: "&Vfdiv";
+ vfrdiv_vf{: vs2, fs1, vmask : vd},
+ disasm: "vfrdiv.vf", "%vd, %vs2, %fs1, %vmask",
+ semfunc: "&Vfrdiv";
+ vfmul_vv{: vs2, vs1, vmask : vd},
+ disasm: "vfmul.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vfmul";
+ vfmul_vf{: vs2, fs1, vmask : vd},
+ disasm: "vfmul.vf", "%vd, %vs2, %fs1, %vmask",
+ semfunc: "&Vfmul";
+ vfrsub_vf{: vs2, fs1, vmask : vd},
+ disasm: "vfrsub.vf", "%vd, %vs2, %fs1, %vmask",
+ semfunc: "&Vfrsub";
+ vfmadd_vv{: vs2, vs1, vd, vmask: vd},
+ disasm: "vfmadd.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vfmadd";
+ vfmadd_vf{: vs2, fs1, vd, vmask: vd},
+ disasm: "vfmadd.vf", "%vd, %vs2, %fs1, %vmask",
+ semfunc: "&Vfmadd";
+ vfnmadd_vv{: vs2, vs1, vd, vmask: vd},
+ disasm: "vfnmadd.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vfnmadd";
+ vfnmadd_vf{: vs2, fs1, vd, vmask: vd},
+ disasm: "vfnmadd.vf", "%vd, %vs2, %fs1, %vmask",
+ semfunc: "&Vfnmadd";
+ vfmsub_vv{: vs2, vs1, vd, vmask: vd},
+ disasm: "vfmsub.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vfmsub";
+ vfmsub_vf{: vs2, fs1, vd, vmask: vd},
+ disasm: "vfmsub.v", "%vd, %vs2, %fs1, %vmask",
+ semfunc: "&Vfmsub";
+ vfnmsub_vv{: vs2, vs1, vd, vmask: vd},
+ disasm: "vfnmsub.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vfnmsub";
+ vfnmsub_vf{: vs2, fs1, vd, vmask: vd},
+ disasm: "vfnmsub.vf", "%vd, %vs2, %fs1, %vmask",
+ semfunc: "&Vfnmsub";
+ vfmacc_vv{: vs2, vs1, vd, vmask: vd},
+ disasm: "vfmacc.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vfmacc";
+ vfmacc_vf{: vs2, fs1, vd, vmask: vd},
+ disasm: "vfmacc.vf", "%vd, %vs2, %fs1, %vmask",
+ semfunc: "&Vfmacc";
+ vfnmacc_vv{: vs2, vs1, vd, vmask: vd},
+ disasm: "vfnmacc.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vfnmacc";
+ vfnmacc_vf{: vs2, fs1, vd, vmask: vd},
+ disasm: "vfnmacc.vf", "%vd, %vs2, %fs1, %vmask",
+ semfunc: "&Vfnmacc";
+ vfmsac_vv{: vs2, vs1, vd, vmask: vd},
+ disasm: "vfmsac.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vfmsac";
+ vfmsac_vf{: vs2, fs1, vd, vmask: vd},
+ disasm: "vfmsac.vf", "%vd, %vs2, %fs1, %vmask",
+ semfunc: "&Vfmsac";
+ vfnmsac_vv{: vs2, vs1, vd, vmask: vd},
+ disasm: "vfnmsac.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vfnmsac";
+ vfnmsac_vf{: vs2, fs1, vd, vmask: vd},
+ disasm: "vfnmsac.vf", "%vd, %vs2, %fs1, %vmask",
+ semfunc: "&Vfnmsac";
+ vfwadd_vv{: vs2, vs1, vmask: vd},
+ disasm: "vfwadd.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vfwadd";
+ vfwadd_vf{: vs2, fs1, vmask: vd},
+ disasm: "vfwadd.vf", "%vd, %vs2, %fs1, %vmask",
+ semfunc: "&Vfwadd";
+ vfwredusum_vv{: vs2, vs1, vmask : vd},
+ disasm: "vfwredusum.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vfwredosum";
+ vfwsub_vv{: vs2, vs1, vmask: vd},
+ disasm: "vfwsub.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vfwsub";
+ vfwsub_vf{: vs2, fs1, vmask: vd},
+ disasm: "vfwsub.vf", "%vd, %vs2, %fs1, %vmask",
+ semfunc: "&Vfwsub";
+ vfwredosum_vv{: vs2, vs1, vmask : vd},
+ disasm: "vfwredosum.vv", "%vd, %vs2, %vs1, %vmask";
+ vfwadd_w_vv{: vs2, vs1, vmask: vd},
+ disasm: "vfwadd.w.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vfwadd";
+ vfwadd_w_vf{: vs2, fs1, vmask : vd},
+ disasm: "vfwadd.w.vf", "%vd, %vs2, %fs1, %vmask",
+ semfunc: "&Vfwadd";
+ vfwsub_w_vv{: vs2, vs1, vmask: vd},
+ disasm: "vfwsub.w.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vfwsub";
+ vfwsub_w_vf{: vs2, fs1, vmask : vd},
+ disasm: "vfwsub.w.vf", "%vd, %vs2, %fs1, %vmask",
+ semfunc: "&Vfwsub";
+ vfwmul_vv{: vs2, vs1, vmask: vd},
+ disasm: "vfwmul.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vfwmul";
+ vfwmul_vf{: vs2, fs1, vmask : vd},
+ disasm: "vfwmul.vf", "%vd, %vs2, %fs1, %vmask",
+ semfunc: "&Vfwmul";
+ vfwmacc_vv{: vs2, vs1, vd, vmask: vd},
+ disasm: "vfwmacc.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vfwmacc";
+ vfwmacc_vf{: vs2, fs1, vd, vmask: vd},
+ disasm: "vfwmacc.vf", "%vd, %vs2, %fs1, %vmask",
+ semfunc: "&Vfwmacc";
+ vfwnmacc_vv{: vs2, vs1, vd, vmask: vd},
+ disasm: "vfwnmacc.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vfwnmacc";
+ vfwnmacc_vf{: vs2, fs1, vd, vmask: vd},
+ disasm: "vfwnmacc.vf", "%vd, %vs2, %fs1, %vmask",
+ semfunc: "&Vfwnmacc";
+ vfwmsac_vv{: vs2, vs1, vd, vmask: vd},
+ disasm: "vfwmsac.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vfwmsac";
+ vfwmsac_vf{: vs2, fs1, vd, vmask: vd},
+ disasm: "vfwmsac.vf", "%vd, %vs2, %fs1, %vmask",
+ semfunc: "&Vfwmsac";
+ vfwnmsac_vv{: vs2, vs1, vd, vmask: vd},
+ disasm: "vfwnmsac.vv", "%vd, %vs2, %vs1, %vmask",
+ semfunc: "&Vfwnmsac";
+ vfwnmsac_vf{: vs2, fs1, vd, vmask: vd},
+ disasm: "vfwnmsac.vf", "%vd, %vs2, %fs1, %vmask",
+ semfunc: "&Vfwnmsac";
+ // VWFUNARY0
+ vfmv_f_s{: vs2 : fd},
+ disasm: "vfmv.f.s", "%fd, %vs2",
+ semfunc: "&Vfmvfs";
+ // VRFUNARY0
+ vfmv_s_f{: fs1 : vd},
+ disasm: "vfmv.s.f", "%vd, %fs1",
+ semfunc: "&Vfmvsf";
+ // VFUNARY0
+ vfcvt_xu_f_v{: vs2, vmask: vd, fflags},
+ disasm: "vfcvt.xu.f.v", "%vd, %vs2, %vmask",
+ semfunc: "&Vfcvtxufv";
+ vfcvt_x_f_v{: vs2, vmask: vd, fflags},
+ disasm: "vfcvt.x.f.v", "%vd, %vs2, %vmask",
+ semfunc: "&Vfcvtxfv";
+ vfcvt_f_xu_v{: vs2, vmask: vd},
+ disasm: "vfcvt.xu.v", "%vd, %vs2, %vmask",
+ semfunc: "&Vfcvtfxuv";
+ vfcvt_f_x_v{: vs2, vmask: vd},
+ disasm: "vfcvt.x.v", "%vd, %vs2, %vmask",
+ semfunc: "&Vfcvtfxv";
+ vfcvt_rtz_xu_f_v{: vs2, vmask: vd, fflags},
+ disasm: "vfcvt.rtz.xu.f.v", "%vd, %vs2, %vmask",
+ semfunc: "&Vfcvtrtzxufv";
+ vfcvt_rtz_x_f_v{: vs2, vmask: vd, fflags},
+ disasm: "vfcvt.rtz.x.f.v", "%vd, %vs2, %vmask",
+ semfunc: "&Vfcvtrtzxfv";
+ vfwcvt_xu_f_v{: vs2, vmask: vd, fflags},
+ disasm: "vfwcvt.xu.f.v", "%vd, %vs2, %vmask",
+ semfunc: "&Vfwcvtxufv";
+ vfwcvt_x_f_v{: vs2, vmask: vd, fflags},
+ disasm: "vfwcvt.x.f.v", "%vd, %vs2, %vmask",
+ semfunc: "&Vfwcvtxfv";
+ vfwcvt_f_xu_v{: vs2, vmask: vd},
+ disasm: "vfwcvt.f.xu.v", "%vd, %vs2, %vmask",
+ semfunc: "&Vfwcvtfxuv";
+ vfwcvt_f_x_v{: vs2, vmask: vd},
+ disasm: "vfwcvt.f.x.v", "%vd, %vs2, %vmask",
+ semfunc: "&Vfwcvtfxv";
+ vfwcvt_f_f_v{: vs2, vmask: vd},
+ disasm: "vfwcvt.f.f.v", "%vd, %vs2, %vmask",
+ semfunc: "&Vfwcvtffv";
+ vfwcvt_rtz_xu_f_v{: vs2, vmask: vd, fflags},
+ disasm: "vfwcvt.rtz.xu.f.v", "%vd, %vs2, %vmask",
+ semfunc: "&Vfwcvtrtzxufv";
+ vfwcvt_rtz_x_f_v{: vs2, vmask: vd, fflags},
+ disasm: "vfwcvt.rtz.x.f.v", "%vd, %vs2, %vmask",
+ semfunc: "&Vfwcvtrtzxfv";
+ vfncvt_xu_f_w{: vs2, vmask: vd, fflags},
+ disasm: "vfncvt.xu.f.w", "%vd, %vs2, %vmask",
+ semfunc: "&Vfncvtxufw";
+ vfncvt_x_f_w{: vs2, vmask: vd, fflags},
+ disasm: "vfncvt.x.f.w", "%vd, %vs2, %vmask",
+ semfunc: "&Vfncvtxfw";
+ vfncvt_f_xu_w{: vs2, vmask: vd},
+ disasm: "vfncvt.f.xu.w", "%vd, %vs2, %vmask",
+ semfunc: "&Vfncvtfxuw";
+ vfncvt_f_x_w{: vs2, vmask: vd},
+ disasm: "vfncvt.f.x.w", "%vd, %vs2, %vmask",
+ semfunc: "&Vfncvtfxw";
+ vfncvt_f_f_w{: vs2, vmask: vd},
+ disasm: "vfncvt.f.f.w", "%vd, %vs2, %vmask",
+ semfunc: "&Vfncvtffw";
+ vfncvt_rod_f_f_w{: vs2, vmask: vd},
+ disasm: "vfncvt.rod.f.f.w", "%vd, %vs2, %vmask",
+ semfunc: "&Vfncvtrodffw";
+ vfncvt_rtz_xu_f_w{: vs2, vmask: vd, fflags},
+ disasm: "vfncvt.rtz.xu.f.w", "%vd, %vs2, %vmask",
+ semfunc: "&Vfncvtrtzxufw";
+ vfncvt_rtz_x_f_w{: vs2, vmask: vd, fflags},
+ disasm: "vfncvt.rtz.x.f.w", "%vd, %vs2, %vmask",
+ semfunc: "&Vfncvtrtzxfw";
+ // VFUNARY1
+ vfsqrt_v{: vs2, vmask: vd, fflags},
+ disasm: "vfsqrt.v", "%vd, %vs2, %vmask",
+ semfunc: "&Vfsqrtv";
+ vfrsqrt7_v{: vs2, vmask: vd, fflags},
+ disasm: "vfrsqrt7.v", "%vd, %vs2, %vmask",
+ semfunc: "&Vfrsqrt7v";
+ vfrec7_v{: vs2, vmask: vd},
+ disasm: "vfrec7.v", "%vd, %vs2, %vmask",
+ semfunc: "&Vfrec7v";
+ vfclass_v{: vs2, vmask: vd},
+ disasm: "vfclass.v", "%vd, %vs2, %vmask",
+ semfunc: "&Vfclassv";
+ }
+}
\ No newline at end of file
diff --git a/cheriot/riscv_cheriot_vector_fp_compare_instructions.cc b/cheriot/riscv_cheriot_vector_fp_compare_instructions.cc
new file mode 100644
index 0000000..2747d7b
--- /dev/null
+++ b/cheriot/riscv_cheriot_vector_fp_compare_instructions.cc
@@ -0,0 +1,148 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cheriot/riscv_cheriot_vector_fp_compare_instructions.h"
+
+#include "absl/log/log.h"
+#include "cheriot/cheriot_state.h"
+#include "cheriot/cheriot_vector_state.h"
+#include "cheriot/riscv_cheriot_vector_instruction_helpers.h"
+
+namespace mpact {
+namespace sim {
+namespace cheriot {
+
+// Vector floating point compare equal.
+void Vmfeq(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 4:
+ return RiscVBinaryVectorMaskOp<float, float>(
+ rv_vector, inst,
+ [](float vs2, float vs1) -> bool { return vs2 == vs1; });
+ case 8:
+ return RiscVBinaryVectorMaskOp<double, double>(
+ rv_vector, inst,
+ [](double vs2, double vs1) -> bool { return vs2 == vs1; });
+ default:
+ LOG(ERROR) << "Vmfeq: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Vector floating point compare less than or equal.
+void Vmfle(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 4:
+ return RiscVBinaryVectorMaskOp<float, float>(
+ rv_vector, inst,
+ [](float vs2, float vs1) -> bool { return vs2 <= vs1; });
+ case 8:
+ return RiscVBinaryVectorMaskOp<double, double>(
+ rv_vector, inst,
+ [](double vs2, double vs1) -> bool { return vs2 <= vs1; });
+ default:
+ LOG(ERROR) << "Vmfle: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Vector floating compare less than.
+void Vmflt(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 4:
+ return RiscVBinaryVectorMaskOp<float, float>(
+ rv_vector, inst,
+ [](float vs2, float vs1) -> bool { return vs2 < vs1; });
+ case 8:
+ return RiscVBinaryVectorMaskOp<double, double>(
+ rv_vector, inst,
+ [](double vs2, double vs1) -> bool { return vs2 < vs1; });
+ default:
+ LOG(ERROR) << "Vmflt: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Vector floating point compare not equal.
+void Vmfne(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 4:
+ return RiscVBinaryVectorMaskOp<float, float>(
+ rv_vector, inst,
+ [](float vs2, float vs1) -> bool { return vs2 != vs1; });
+ case 8:
+ return RiscVBinaryVectorMaskOp<double, double>(
+ rv_vector, inst,
+ [](double vs2, double vs1) -> bool { return vs2 != vs1; });
+ default:
+ LOG(ERROR) << "Vmfne: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Vector floating point compare greater than.
+void Vmfgt(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 4:
+ return RiscVBinaryVectorMaskOp<float, float>(
+ rv_vector, inst,
+ [](float vs2, float vs1) -> bool { return vs2 > vs1; });
+ case 8:
+ return RiscVBinaryVectorMaskOp<double, double>(
+ rv_vector, inst,
+ [](double vs2, double vs1) -> bool { return vs2 > vs1; });
+ default:
+ LOG(ERROR) << "Vmfgt: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Vector floating point compare greater than or equal.
+void Vmfge(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 4:
+ return RiscVBinaryVectorMaskOp<float, float>(
+ rv_vector, inst,
+ [](float vs2, float vs1) -> bool { return vs2 >= vs1; });
+ case 8:
+ return RiscVBinaryVectorMaskOp<double, double>(
+ rv_vector, inst,
+ [](double vs2, double vs1) -> bool { return vs2 >= vs1; });
+ default:
+ LOG(ERROR) << "Vmfge: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+} // namespace cheriot
+} // namespace sim
+} // namespace mpact
diff --git a/cheriot/riscv_cheriot_vector_fp_compare_instructions.h b/cheriot/riscv_cheriot_vector_fp_compare_instructions.h
new file mode 100644
index 0000000..9b5f83e
--- /dev/null
+++ b/cheriot/riscv_cheriot_vector_fp_compare_instructions.h
@@ -0,0 +1,45 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef MPACT_CHERIOT_RISCV_CHERIOT_VECTOR_FP_COMPARE_INSTRUCTIONS_H_
+#define MPACT_CHERIOT_RISCV_CHERIOT_VECTOR_FP_COMPARE_INSTRUCTIONS_H_
+
+#include "mpact/sim/generic/instruction.h"
+
+// This file declares the vector floating point compare instructions.
+
+namespace mpact {
+namespace sim {
+namespace cheriot {
+
+using Instruction = ::mpact::sim::generic::Instruction;
+
+// Each of these instructions take 3 source operands, and one destination
+// operand. Source operand 0 is a vector register group, source operand 1 is
+// either a vector register group or a scalar floating point register (Vmfgt and
+// Vmfge only take the scalar register), source operand 2 is the vector mask
+// register. Destination operand 0 is a vector register treated as the
+// destination mask register.
+void Vmfeq(const Instruction *inst);
+void Vmfle(const Instruction *inst);
+void Vmflt(const Instruction *inst);
+void Vmfne(const Instruction *inst);
+void Vmfgt(const Instruction *inst);
+void Vmfge(const Instruction *inst);
+
+} // namespace cheriot
+} // namespace sim
+} // namespace mpact
+
+#endif // MPACT_CHERIOT_RISCV_CHERIOT_VECTOR_FP_COMPARE_INSTRUCTIONS_H_
diff --git a/cheriot/riscv_cheriot_vector_fp_instructions.cc b/cheriot/riscv_cheriot_vector_fp_instructions.cc
new file mode 100644
index 0000000..e4d92fa
--- /dev/null
+++ b/cheriot/riscv_cheriot_vector_fp_instructions.cc
@@ -0,0 +1,846 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cheriot/riscv_cheriot_vector_fp_instructions.h"
+
+#include <cmath>
+#include <cstdint>
+#include <functional>
+#include <tuple>
+
+#include "absl/log/log.h"
+#include "cheriot/cheriot_state.h"
+#include "cheriot/riscv_cheriot_vector_instruction_helpers.h"
+#include "mpact/sim/generic/type_helpers.h"
+#include "riscv//riscv_fp_host.h"
+#include "riscv//riscv_fp_info.h"
+#include "riscv//riscv_fp_state.h"
+#include "riscv//riscv_vector_state.h"
+
+namespace mpact {
+namespace sim {
+namespace cheriot {
+
+using ::mpact::sim::generic::FPTypeInfo;
+using ::mpact::sim::riscv::FPExceptions;
+using ::mpact::sim::riscv::ScopedFPStatus;
+
+// Floating point add.
+void Vfadd(const Instruction *inst) {
+ auto *rv_fp = static_cast<CheriotState *>(inst->state())->rv_fp();
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ if (!rv_fp->rounding_mode_valid()) {
+ LOG(ERROR) << "Invalid rounding mode";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int sew = rv_vector->selected_element_width();
+ ScopedFPStatus set_fpstatus(rv_fp->host_fp_interface());
+ switch (sew) {
+ case 4:
+ return RiscVBinaryVectorOp<float, float, float>(
+ rv_vector, inst,
+ [](float vs2, float vs1) -> float { return vs2 + vs1; });
+ case 8:
+ return RiscVBinaryVectorOp<double, double, double>(
+ rv_vector, inst,
+ [](double vs2, double vs1) -> double { return vs2 + vs1; });
+ default:
+ LOG(ERROR) << "Vfadd: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Floating point subtract.
+void Vfsub(const Instruction *inst) {
+ auto *rv_fp = static_cast<CheriotState *>(inst->state())->rv_fp();
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ if (!rv_fp->rounding_mode_valid()) {
+ LOG(ERROR) << "Invalid rounding mode";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int sew = rv_vector->selected_element_width();
+ ScopedFPStatus set_fpstatus(rv_fp->host_fp_interface());
+ switch (sew) {
+ case 4:
+ return RiscVBinaryVectorOp<float, float, float>(
+ rv_vector, inst,
+ [](float vs2, float vs1) -> float { return vs2 - vs1; });
+ case 8:
+ return RiscVBinaryVectorOp<double, double, double>(
+ rv_vector, inst,
+ [](double vs2, double vs1) -> double { return vs2 - vs1; });
+ default:
+ LOG(ERROR) << "Vfsub: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Reverse floating point subtract (rs1 - vs2).
+void Vfrsub(const Instruction *inst) {
+ auto *rv_fp = static_cast<CheriotState *>(inst->state())->rv_fp();
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ if (!rv_fp->rounding_mode_valid()) {
+ LOG(ERROR) << "Invalid rounding mode";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int sew = rv_vector->selected_element_width();
+ ScopedFPStatus set_fpstatus(rv_fp->host_fp_interface());
+ switch (sew) {
+ case 4:
+ return RiscVBinaryVectorOp<float, float, float>(
+ rv_vector, inst,
+ [](float vs2, float vs1) -> float { return vs1 - vs2; });
+ case 8:
+ return RiscVBinaryVectorOp<double, double, double>(
+ rv_vector, inst,
+ [](double vs2, double vs1) -> double { return vs1 - vs2; });
+ default:
+ LOG(ERROR) << "Vfrsub: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Widening floating point add.
+void Vfwadd(const Instruction *inst) {
+ auto *rv_fp = static_cast<CheriotState *>(inst->state())->rv_fp();
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ if (!rv_fp->rounding_mode_valid()) {
+ LOG(ERROR) << "Invalid rounding mode";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int sew = rv_vector->selected_element_width();
+ ScopedFPStatus set_fpstatus(rv_fp->host_fp_interface());
+ switch (sew) {
+ case 4:
+ return RiscVBinaryVectorOp<double, float, float>(
+ rv_vector, inst, [](float vs2, float vs1) -> double {
+ double vs2_d = static_cast<double>(vs2);
+ double vs1_d = static_cast<double>(vs1);
+ return (vs2_d + vs1_d);
+ });
+ default:
+ LOG(ERROR) << "Vfwadd: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Widening floating point subtract.
+void Vfwsub(const Instruction *inst) {
+ auto *rv_fp = static_cast<CheriotState *>(inst->state())->rv_fp();
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ if (!rv_fp->rounding_mode_valid()) {
+ LOG(ERROR) << "Invalid rounding mode";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int sew = rv_vector->selected_element_width();
+ ScopedFPStatus set_fpstatus(rv_fp->host_fp_interface());
+ switch (sew) {
+ case 4:
+ return RiscVBinaryVectorOp<double, float, float>(
+ rv_vector, inst, [](float vs2, float vs1) -> double {
+ double vs2_d = static_cast<double>(vs2);
+ double vs1_d = static_cast<double>(vs1);
+ return (vs2_d - vs1_d);
+ });
+ default:
+ LOG(ERROR) << "Vfwsub: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Widening floating point add with wide operand (vs2).
+void Vfwaddw(const Instruction *inst) {
+ auto *rv_fp = static_cast<CheriotState *>(inst->state())->rv_fp();
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ if (!rv_fp->rounding_mode_valid()) {
+ LOG(ERROR) << "Invalid rounding mode";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int sew = rv_vector->selected_element_width();
+ ScopedFPStatus set_fpstatus(rv_fp->host_fp_interface());
+ switch (sew) {
+ case 4:
+ return RiscVBinaryVectorOp<double, double, float>(
+ rv_vector, inst, [](double vs2_d, float vs1) -> double {
+ double vs1_d = static_cast<double>(vs1);
+ return (vs2_d + vs1_d);
+ });
+ default:
+ LOG(ERROR) << "Vfwaddw: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Widening floating point subtract with wide operand (vs2).
+void Vfwsubw(const Instruction *inst) {
+ auto *rv_fp = static_cast<CheriotState *>(inst->state())->rv_fp();
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ if (!rv_fp->rounding_mode_valid()) {
+ LOG(ERROR) << "Invalid rounding mode";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int sew = rv_vector->selected_element_width();
+ ScopedFPStatus set_fpstatus(rv_fp->host_fp_interface());
+ switch (sew) {
+ case 4:
+ return RiscVBinaryVectorOp<double, double, float>(
+ rv_vector, inst, [](double vs2_d, float vs1) -> double {
+ double vs1_d = static_cast<double>(vs1);
+ return (vs2_d - vs1_d);
+ });
+ default:
+ LOG(ERROR) << "Vfwsubw: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Floating point multiply.
+void Vfmul(const Instruction *inst) {
+ auto *rv_fp = static_cast<CheriotState *>(inst->state())->rv_fp();
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ if (!rv_fp->rounding_mode_valid()) {
+ LOG(ERROR) << "Invalid rounding mode";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int sew = rv_vector->selected_element_width();
+ ScopedFPStatus set_fpstatus(rv_fp->host_fp_interface());
+ switch (sew) {
+ case 4:
+ return RiscVBinaryVectorOp<float, float, float>(
+ rv_vector, inst,
+ [](float vs2, float vs1) -> float { return vs2 * vs1; });
+ case 8:
+ return RiscVBinaryVectorOp<double, double, double>(
+ rv_vector, inst,
+ [](double vs2, double vs1) -> double { return vs2 * vs1; });
+ default:
+ LOG(ERROR) << "Vfmul: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Floating point division vs2/vs1;
+void Vfdiv(const Instruction *inst) {
+ auto *rv_fp = static_cast<CheriotState *>(inst->state())->rv_fp();
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ if (!rv_fp->rounding_mode_valid()) {
+ LOG(ERROR) << "Invalid rounding mode";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int sew = rv_vector->selected_element_width();
+ ScopedFPStatus set_fpstatus(rv_fp->host_fp_interface());
+ switch (sew) {
+ case 4:
+ return RiscVBinaryVectorOp<float, float, float>(
+ rv_vector, inst,
+ [](float vs2, float vs1) -> float { return vs2 / vs1; });
+ case 8:
+ return RiscVBinaryVectorOp<double, double, double>(
+ rv_vector, inst,
+ [](double vs2, double vs1) -> double { return vs2 / vs1; });
+ default:
+ LOG(ERROR) << "Vfdiv: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Floating point reverse division vs1/vs2.
+void Vfrdiv(const Instruction *inst) {
+ auto *rv_fp = static_cast<CheriotState *>(inst->state())->rv_fp();
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ if (!rv_fp->rounding_mode_valid()) {
+ LOG(ERROR) << "Invalid rounding mode";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int sew = rv_vector->selected_element_width();
+ ScopedFPStatus set_fpstatus(rv_fp->host_fp_interface());
+ switch (sew) {
+ case 4:
+ return RiscVBinaryVectorOp<float, float, float>(
+ rv_vector, inst,
+ [](float vs2, float vs1) -> float { return vs1 / vs2; });
+ case 8:
+ return RiscVBinaryVectorOp<double, double, double>(
+ rv_vector, inst,
+ [](double vs2, double vs1) -> double { return vs1 / vs2; });
+ default:
+ LOG(ERROR) << "Vfrdiv: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Widening floating point multiply.
+void Vfwmul(const Instruction *inst) {
+ auto *rv_fp = static_cast<CheriotState *>(inst->state())->rv_fp();
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ if (!rv_fp->rounding_mode_valid()) {
+ LOG(ERROR) << "Invalid rounding mode";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int sew = rv_vector->selected_element_width();
+ ScopedFPStatus set_fpstatus(rv_fp->host_fp_interface());
+ switch (sew) {
+ case 4:
+ return RiscVBinaryVectorOp<double, float, float>(
+ rv_vector, inst, [](float vs2, float vs1) -> double {
+ double vs2_d = static_cast<double>(vs2);
+ double vs1_d = static_cast<double>(vs1);
+ return (vs2_d * vs1_d);
+ });
+ default:
+ LOG(ERROR) << "Vfwadd: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Floating point multiply and add vs2.
+void Vfmadd(const Instruction *inst) {
+ auto *rv_fp = static_cast<CheriotState *>(inst->state())->rv_fp();
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ if (!rv_fp->rounding_mode_valid()) {
+ LOG(ERROR) << "Invalid rounding mode";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int sew = rv_vector->selected_element_width();
+ ScopedFPStatus set_fpstatus(rv_fp->host_fp_interface());
+ switch (sew) {
+ case 4:
+ return RiscVTernaryVectorOp<float, float, float>(
+ rv_vector, inst, [](float vs2, float vs1, float vd) -> float {
+ return std::fma(vs1, vd, vs2);
+ });
+ case 8:
+ return RiscVTernaryVectorOp<double, double, double>(
+ rv_vector, inst, [](double vs2, double vs1, double vd) -> double {
+ return std::fma(vs1, vd, vs2);
+ });
+ default:
+ LOG(ERROR) << "Vfmadd: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Negated floating point multiply and add vs2.
+void Vfnmadd(const Instruction *inst) {
+ auto *rv_fp = static_cast<CheriotState *>(inst->state())->rv_fp();
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ if (!rv_fp->rounding_mode_valid()) {
+ LOG(ERROR) << "Invalid rounding mode";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int sew = rv_vector->selected_element_width();
+ ScopedFPStatus set_fpstatus(rv_fp->host_fp_interface());
+ switch (sew) {
+ case 4:
+ return RiscVTernaryVectorOp<float, float, float>(
+ rv_vector, inst, [](float vs2, float vs1, float vd) -> float {
+ return std::fma(-vs1, vd, -vs2);
+ });
+ case 8:
+ return RiscVTernaryVectorOp<double, double, double>(
+ rv_vector, inst, [](double vs2, double vs1, double vd) -> double {
+ return std::fma(-vs1, vd, -vs2);
+ });
+ default:
+ LOG(ERROR) << "Vfnmadd: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Floating point multiply and subtract vs2.
+void Vfmsub(const Instruction *inst) {
+ auto *rv_fp = static_cast<CheriotState *>(inst->state())->rv_fp();
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ if (!rv_fp->rounding_mode_valid()) {
+ LOG(ERROR) << "Invalid rounding mode";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int sew = rv_vector->selected_element_width();
+ ScopedFPStatus set_fpstatus(rv_fp->host_fp_interface());
+ switch (sew) {
+ case 4:
+ return RiscVTernaryVectorOp<float, float, float>(
+ rv_vector, inst, [](float vs2, float vs1, float vd) -> float {
+ return std::fma(vs1, vd, -vs2);
+ });
+ case 8:
+ return RiscVTernaryVectorOp<double, double, double>(
+ rv_vector, inst, [](double vs2, double vs1, double vd) -> double {
+ return std::fma(vs1, vd, -vs2);
+ });
+ default:
+ LOG(ERROR) << "Vfmsub: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Negated floating point multiply and subtract vs2.
+void Vfnmsub(const Instruction *inst) {
+ auto *rv_fp = static_cast<CheriotState *>(inst->state())->rv_fp();
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ if (!rv_fp->rounding_mode_valid()) {
+ LOG(ERROR) << "Invalid rounding mode";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int sew = rv_vector->selected_element_width();
+ ScopedFPStatus set_fpstatus(rv_fp->host_fp_interface());
+ switch (sew) {
+ case 4:
+ return RiscVTernaryVectorOp<float, float, float>(
+ rv_vector, inst, [](float vs2, float vs1, float vd) -> float {
+ return std::fma(-vs1, vd, vs2);
+ });
+ case 8:
+ return RiscVTernaryVectorOp<double, double, double>(
+ rv_vector, inst, [](double vs2, double vs1, double vd) -> double {
+ return std::fma(-vs1, vd, vs2);
+ });
+ default:
+ LOG(ERROR) << "Vfnmsub: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Floating point multiply and accumulate vd.
+void Vfmacc(const Instruction *inst) {
+ auto *rv_fp = static_cast<CheriotState *>(inst->state())->rv_fp();
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ if (!rv_fp->rounding_mode_valid()) {
+ LOG(ERROR) << "Invalid rounding mode";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int sew = rv_vector->selected_element_width();
+ ScopedFPStatus set_fpstatus(rv_fp->host_fp_interface());
+ switch (sew) {
+ case 4:
+ return RiscVTernaryVectorOp<float, float, float>(
+ rv_vector, inst, [](float vs2, float vs1, float vd) -> float {
+ return std::fma(vs1, vs2, vd);
+ });
+ case 8:
+ return RiscVTernaryVectorOp<double, double, double>(
+ rv_vector, inst, [](double vs2, double vs1, double vd) -> double {
+ return std::fma(vs1, vs2, vd);
+ });
+ default:
+ LOG(ERROR) << "Vfmacc: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Negated floating point multiply and accumulate vd.
+void Vfnmacc(const Instruction *inst) {
+ auto *rv_fp = static_cast<CheriotState *>(inst->state())->rv_fp();
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ if (!rv_fp->rounding_mode_valid()) {
+ LOG(ERROR) << "Invalid rounding mode";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int sew = rv_vector->selected_element_width();
+ ScopedFPStatus set_fpstatus(rv_fp->host_fp_interface());
+ switch (sew) {
+ case 4:
+ return RiscVTernaryVectorOp<float, float, float>(
+ rv_vector, inst, [](float vs2, float vs1, float vd) -> float {
+ return std::fma(-vs1, vs2, -vd);
+ });
+ case 8:
+ return RiscVTernaryVectorOp<double, double, double>(
+ rv_vector, inst, [](double vs2, double vs1, double vd) -> double {
+ return std::fma(-vs1, vs2, -vd);
+ });
+ default:
+ LOG(ERROR) << "Vfnmacc: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Floating point multiply and subtract vd.
+void Vfmsac(const Instruction *inst) {
+ auto *rv_fp = static_cast<CheriotState *>(inst->state())->rv_fp();
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ if (!rv_fp->rounding_mode_valid()) {
+ LOG(ERROR) << "Invalid rounding mode";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int sew = rv_vector->selected_element_width();
+ ScopedFPStatus set_fpstatus(rv_fp->host_fp_interface());
+ switch (sew) {
+ case 4:
+ return RiscVTernaryVectorOp<float, float, float>(
+ rv_vector, inst, [](float vs2, float vs1, float vd) -> float {
+ return std::fma(vs1, vs2, -vd);
+ });
+ case 8:
+ return RiscVTernaryVectorOp<double, double, double>(
+ rv_vector, inst, [](double vs2, double vs1, double vd) -> double {
+ return std::fma(vs1, vs2, -vd);
+ });
+ default:
+ LOG(ERROR) << "Vfmsac: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Negated floating point multiply and subtract vd.
+void Vfnmsac(const Instruction *inst) {
+ auto *rv_fp = static_cast<CheriotState *>(inst->state())->rv_fp();
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ if (!rv_fp->rounding_mode_valid()) {
+ LOG(ERROR) << "Invalid rounding mode";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int sew = rv_vector->selected_element_width();
+ ScopedFPStatus set_fpstatus(rv_fp->host_fp_interface());
+ switch (sew) {
+ case 4:
+ return RiscVTernaryVectorOp<float, float, float>(
+ rv_vector, inst, [](float vs2, float vs1, float vd) -> float {
+ return std::fma(-vs1, vs2, vd);
+ });
+ case 8:
+ return RiscVTernaryVectorOp<double, double, double>(
+ rv_vector, inst, [](double vs2, double vs1, double vd) -> double {
+ return std::fma(-vs1, vs2, vd);
+ });
+ default:
+ LOG(ERROR) << "Vfnmsac: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Widening floating point multiply and accumulate vd.
+void Vfwmacc(const Instruction *inst) {
+ auto *rv_fp = static_cast<CheriotState *>(inst->state())->rv_fp();
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ if (!rv_fp->rounding_mode_valid()) {
+ LOG(ERROR) << "Invalid rounding mode";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int sew = rv_vector->selected_element_width();
+ ScopedFPStatus set_fpstatus(rv_fp->host_fp_interface());
+ switch (sew) {
+ case 4:
+ return RiscVTernaryVectorOp<double, float, float>(
+ rv_vector, inst, [](float vs2, float vs1, double vd) -> double {
+ double vs1_d = vs1;
+ double vs2_d = vs2;
+ return ((vs1_d * vs2_d) + vd);
+ });
+ default:
+ LOG(ERROR) << "Vfwmacc: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Widening negated floating point multiply and accumulate vd.
+void Vfwnmacc(const Instruction *inst) {
+ auto *rv_fp = static_cast<CheriotState *>(inst->state())->rv_fp();
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ if (!rv_fp->rounding_mode_valid()) {
+ LOG(ERROR) << "Invalid rounding mode";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int sew = rv_vector->selected_element_width();
+ ScopedFPStatus set_fpstatus(rv_fp->host_fp_interface());
+ switch (sew) {
+ case 4:
+ return RiscVTernaryVectorOp<double, float, float>(
+ rv_vector, inst, [](float vs2, float vs1, double vd) -> double {
+ double vs1_d = vs1;
+ double vs2_d = vs2;
+ return (-(vs1_d * vs2_d)) - vd;
+ });
+ default:
+ LOG(ERROR) << "Vfwnmacc: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Widening floating point multiply and subtract vd.
+void Vfwmsac(const Instruction *inst) {
+ auto *rv_fp = static_cast<CheriotState *>(inst->state())->rv_fp();
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ if (!rv_fp->rounding_mode_valid()) {
+ LOG(ERROR) << "Invalid rounding mode";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int sew = rv_vector->selected_element_width();
+ ScopedFPStatus set_fpstatus(rv_fp->host_fp_interface());
+ switch (sew) {
+ case 4:
+ return RiscVTernaryVectorOp<double, float, float>(
+ rv_vector, inst, [](float vs2, float vs1, double vd) -> double {
+ double vs1_d = vs1;
+ double vs2_d = vs2;
+ return ((vs1_d * vs2_d) - vd);
+ });
+ default:
+ LOG(ERROR) << "Vfwmsac: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Widening negated floating point multiply and subtract vd.
+void Vfwnmsac(const Instruction *inst) {
+ auto *rv_fp = static_cast<CheriotState *>(inst->state())->rv_fp();
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ if (!rv_fp->rounding_mode_valid()) {
+ LOG(ERROR) << "Invalid rounding mode";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int sew = rv_vector->selected_element_width();
+ ScopedFPStatus set_fpstatus(rv_fp->host_fp_interface());
+ switch (sew) {
+ case 4:
+ return RiscVTernaryVectorOp<double, float, float>(
+ rv_vector, inst, [](float vs2, float vs1, double vd) -> double {
+ double vs1_d = vs1;
+ double vs2_d = vs2;
+ return (-(vs1_d * vs2_d)) + vd;
+ });
+ default:
+ LOG(ERROR) << "Vfwnmsac: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Change the sign of vs2 to the sign of vs1.
+void Vfsgnj(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 4:
+ return RiscVBinaryVectorOp<uint32_t, uint32_t, uint32_t>(
+ rv_vector, inst, [](uint32_t vs2, uint32_t vs1) -> uint32_t {
+ return (vs2 & 0x7fff'ffff) | (vs1 & 0x8000'0000);
+ });
+ case 8:
+ return RiscVBinaryVectorOp<uint64_t, uint64_t, uint64_t>(
+ rv_vector, inst, [](uint64_t vs2, uint64_t vs1) -> uint64_t {
+ return (vs2 & 0x7fff'ffff'ffff'ffff) |
+ (vs1 & 0x8000'0000'0000'0000);
+ });
+ default:
+ LOG(ERROR) << "Vfsgnj: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Change the sign of vs2 to the negation of the sign of vs1.
+void Vfsgnjn(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 4:
+ return RiscVBinaryVectorOp<uint32_t, uint32_t, uint32_t>(
+ rv_vector, inst, [](uint32_t vs2, uint32_t vs1) -> uint32_t {
+ return (vs2 & 0x7fff'ffff) | (~vs1 & 0x8000'0000);
+ });
+ case 8:
+ return RiscVBinaryVectorOp<uint64_t, uint64_t, uint64_t>(
+ rv_vector, inst, [](uint64_t vs2, uint64_t vs1) -> uint64_t {
+ return (vs2 & 0x7fff'ffff'ffff'ffff) |
+ (~vs1 & 0x8000'0000'0000'0000);
+ });
+ default:
+ LOG(ERROR) << "Vfsgnjn: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Change the sign of vs2 to the xor of the sign of the two operands.
+void Vfsgnjx(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 4:
+ return RiscVBinaryVectorOp<uint32_t, uint32_t, uint32_t>(
+ rv_vector, inst, [](uint32_t vs2, uint32_t vs1) -> uint32_t {
+ return (vs2 & 0x7fff'ffff) | ((vs1 ^ vs2) & 0x8000'0000);
+ });
+ case 8:
+ return RiscVBinaryVectorOp<uint64_t, uint64_t, uint64_t>(
+ rv_vector, inst, [](uint64_t vs2, uint64_t vs1) -> uint64_t {
+ return (vs2 & 0x7fff'ffff'ffff'ffff) ^
+ ((vs1 ^ vs2) & 0x8000'0000'0000'0000);
+ });
+ default:
+ LOG(ERROR) << "Vfsgnjx: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Templated helper function for vfmin and vfmax instructions.
+template <typename T>
+inline std::tuple<T, uint32_t> MaxMinHelper(T vs2, T vs1,
+ std::function<T(T, T)> operation) {
+ // If either operand is a signaling NaN or if both operands are NaNs, then
+ // return a canonical (non-signaling) NaN.
+ uint32_t flag = 0;
+ if (FPTypeInfo<T>::IsSNaN(vs1) || FPTypeInfo<T>::IsSNaN(vs2)) {
+ flag = static_cast<uint32_t>(FPExceptions::kInvalidOp);
+ }
+ if (FPTypeInfo<T>::IsNaN(vs2) && FPTypeInfo<T>::IsNaN(vs1)) {
+ auto c_nan = FPTypeInfo<T>::kCanonicalNaN;
+ return std::make_tuple(*reinterpret_cast<T *>(&c_nan), flag);
+ }
+ // If either operand is a NaN return the other.
+ if (FPTypeInfo<T>::IsNaN(vs2)) return std::tie(vs1, flag);
+ if (FPTypeInfo<T>::IsNaN(vs1)) return std::tie(vs2, flag);
+ // Return the min/max of the two operands.
+ if ((vs2 == 0.0) && (vs1 == 0.0)) {
+ T tmp2 = std::signbit(vs2) ? -1.0 : 1;
+ T tmp1 = std::signbit(vs1) ? -1.0 : 1;
+ return std::make_tuple(operation(tmp2, tmp1) == tmp2 ? vs2 : vs1, 0);
+ }
+ return std::make_tuple(operation(vs2, vs1), flag);
+}
+
+// Vector floating point min.
+void Vfmin(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 4:
+ return RiscVBinaryVectorOpWithFflags<float, float, float>(
+ rv_vector, inst,
+ [](float vs2, float vs1) -> std::tuple<float, uint32_t> {
+ using T = float;
+ return MaxMinHelper<T>(vs2, vs1, [](T vs2, T vs1) -> T {
+ return (vs1 < vs2) ? vs1 : vs2;
+ });
+ });
+ case 8:
+ return RiscVBinaryVectorOpWithFflags<double, double, double>(
+ rv_vector, inst,
+ [](double vs2, double vs1) -> std::tuple<double, uint32_t> {
+ using T = double;
+ return MaxMinHelper<T>(vs2, vs1, [](T vs2, T vs1) -> T {
+ return (vs1 < vs2) ? vs1 : vs2;
+ });
+ });
+ default:
+ LOG(ERROR) << "Vfmin: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Vector floating point max.
+void Vfmax(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 4:
+ return RiscVBinaryVectorOpWithFflags<float, float, float>(
+ rv_vector, inst,
+ [](float vs2, float vs1) -> std::tuple<float, uint32_t> {
+ using T = float;
+ return MaxMinHelper<T>(vs2, vs1, [](T vs2, T vs1) -> T {
+ return (vs1 > vs2) ? vs1 : vs2;
+ });
+ });
+ case 8:
+ return RiscVBinaryVectorOpWithFflags<double, double, double>(
+ rv_vector, inst,
+ [](double vs2, double vs1) -> std::tuple<double, uint32_t> {
+ using T = double;
+ return MaxMinHelper<T>(vs2, vs1, [](T vs2, T vs1) -> T {
+ return (vs1 > vs2) ? vs1 : vs2;
+ });
+ });
+ default:
+ LOG(ERROR) << "Vfmax: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Vector fp merge.
+void Vfmerge(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 4:
+ return RiscVMaskBinaryVectorOp<uint32_t, uint32_t, uint32_t>(
+ rv_vector, inst,
+ [](uint32_t vs2, uint32_t vs1, bool mask) -> uint32_t {
+ return mask ? vs1 : vs2;
+ });
+ case 8:
+ return RiscVMaskBinaryVectorOp<uint64_t, uint64_t, uint64_t>(
+ rv_vector, inst,
+ [](uint64_t vs2, uint64_t vs1, bool mask) -> uint64_t {
+ return mask ? vs1 : vs2;
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Vfmerge: Illegal sew (" << sew << ")";
+ return;
+ }
+}
+
+} // namespace cheriot
+} // namespace sim
+} // namespace mpact
diff --git a/cheriot/riscv_cheriot_vector_fp_instructions.h b/cheriot/riscv_cheriot_vector_fp_instructions.h
new file mode 100644
index 0000000..46d56b5
--- /dev/null
+++ b/cheriot/riscv_cheriot_vector_fp_instructions.h
@@ -0,0 +1,85 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef MPACT_CHERIOT_RISCV_CHERIOT_VECTOR_FP_INSTRUCTIONS_H_
+#define MPACT_CHERIOT_RISCV_CHERIOT_VECTOR_FP_INSTRUCTIONS_H_
+
+#include "mpact/sim/generic/instruction.h"
+
+// This file declares the main binary and ternary floating point instruction
+// semantic functions.
+
+namespace mpact {
+namespace sim {
+namespace cheriot {
+
+using Instruction = ::mpact::sim::generic::Instruction;
+
+// Vector floating point arithmetic instructions. Each of these instructions
+// take three source operands and one destination operand. Source 0 is a vector
+// register group, source 1 is either a vector register group or a scalar
+// register, and source 2 is the mask register. Destination 0 is a vector
+// register group.
+void Vfadd(const Instruction *inst);
+void Vfsub(const Instruction *inst);
+void Vfrsub(const Instruction *inst);
+void Vfwadd(const Instruction *inst);
+void Vfwsub(const Instruction *inst);
+void Vfwaddw(const Instruction *inst);
+void Vfwsubw(const Instruction *inst);
+void Vfmul(const Instruction *inst);
+void Vfdiv(const Instruction *inst);
+void Vfrdiv(const Instruction *inst);
+void Vfwmul(const Instruction *inst);
+
+// Vector floating point multiply and add/subtract instructions. Each of these
+// instructions take four source operands and one destination operand. Source 0
+// is a vector register group, source 1 is either a vector register group or a
+// scalar register, source 2 is a vector register group, and source 3 is the
+// mask register. Destination 0 is a vector register group.
+void Vfmadd(const Instruction *inst);
+void Vfnmadd(const Instruction *inst);
+void Vfmsub(const Instruction *inst);
+void Vfnmsub(const Instruction *inst);
+void Vfmacc(const Instruction *inst);
+void Vfnmacc(const Instruction *inst);
+void Vfmsac(const Instruction *inst);
+void Vfnmsac(const Instruction *inst);
+void Vfwmacc(const Instruction *inst);
+void Vfwnmacc(const Instruction *inst);
+void Vfwmsac(const Instruction *inst);
+void Vfwnmsac(const Instruction *inst);
+
+// Vector floating point sign modification instructions. Each of these
+// instructions take three source operands and one destination operand. Source 0
+// is a vector register group, source 1 is either a vector register group or a
+// scalar register, and source 2 is the mask register. Destination 0 is a vector
+// register group.
+void Vfsgnj(const Instruction *inst);
+void Vfsgnjn(const Instruction *inst);
+void Vfsgnjx(const Instruction *inst);
+
+// Vector selection instructions. Each of these instructions take three source
+// operands and one destination operand. Source 0 is a vector register group,
+// source 1 is either a vector register group or a scalar register, and source 2
+// is the mask register. Destination 0 is a vector register group.
+void Vfmin(const Instruction *inst);
+void Vfmax(const Instruction *inst);
+void Vfmerge(const Instruction *inst);
+
+} // namespace cheriot
+} // namespace sim
+} // namespace mpact
+
+#endif // MPACT_CHERIOT_RISCV_CHERIOT_VECTOR_FP_INSTRUCTIONS_H_
diff --git a/cheriot/riscv_cheriot_vector_fp_reduction_instructions.cc b/cheriot/riscv_cheriot_vector_fp_reduction_instructions.cc
new file mode 100644
index 0000000..cf72616
--- /dev/null
+++ b/cheriot/riscv_cheriot_vector_fp_reduction_instructions.cc
@@ -0,0 +1,182 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cheriot/riscv_cheriot_vector_fp_reduction_instructions.h"
+
+#include <functional>
+
+#include "absl/log/log.h"
+#include "cheriot/cheriot_state.h"
+#include "cheriot/riscv_cheriot_vector_instruction_helpers.h"
+#include "mpact/sim/generic/type_helpers.h"
+#include "riscv//riscv_fp_host.h"
+#include "riscv//riscv_fp_state.h"
+#include "riscv//riscv_vector_state.h"
+
+namespace mpact {
+namespace sim {
+namespace cheriot {
+
+using ::mpact::sim::generic::FPTypeInfo;
+using ::mpact::sim::riscv::ScopedFPStatus;
+
+// These reduction instructions take an accumulator and a value and returns
+// the result of the reduction operation. Each partial sum is stored to a
+// separate entry in the destination vector.
+
+// Sum reduction.
+void Vfredosum(const Instruction *inst) {
+ auto *rv_fp = static_cast<CheriotState *>(inst->state())->rv_fp();
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ if (!rv_fp->rounding_mode_valid()) {
+ LOG(ERROR) << "Invalid rounding mode";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int sew = rv_vector->selected_element_width();
+ ScopedFPStatus set_fpstatus(rv_fp->host_fp_interface());
+ switch (sew) {
+ case 4:
+ return RiscVBinaryReductionVectorOp<float, float, float>(
+ rv_vector, inst,
+ [](float acc, float vs2) -> float { return acc + vs2; });
+ return;
+ case 8:
+ return RiscVBinaryReductionVectorOp<double, double, double>(
+ rv_vector, inst,
+ [](double acc, double vs2) -> double { return acc + vs2; });
+ return;
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+void Vfwredosum(const Instruction *inst) {
+ auto *rv_fp = static_cast<CheriotState *>(inst->state())->rv_fp();
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ if (!rv_fp->rounding_mode_valid()) {
+ LOG(ERROR) << "Invalid rounding mode";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int sew = rv_vector->selected_element_width();
+ ScopedFPStatus set_fpstatus(rv_fp->host_fp_interface());
+ switch (sew) {
+ case 4:
+ return RiscVBinaryReductionVectorOp<double, float, double>(
+ rv_vector, inst, [](double acc, float vs2) -> double {
+ return acc + static_cast<double>(vs2);
+ });
+ return;
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Templated helper function for vfmin and vfmax instructions.
+template <typename T>
+inline T MaxMinHelper(T vs2, T vs1, std::function<T(T, T)> operation) {
+ // If either operand is a signaling NaN or if both operands are NaNs, then
+ // return a canonical (non-signaling) NaN.
+ if (FPTypeInfo<T>::IsSNaN(vs1) || FPTypeInfo<T>::IsSNaN(vs2) ||
+ (FPTypeInfo<T>::IsNaN(vs2) && FPTypeInfo<T>::IsNaN(vs1))) {
+ typename FPTypeInfo<T>::UIntType c_nan = FPTypeInfo<T>::kCanonicalNaN;
+ return *reinterpret_cast<T *>(&c_nan);
+ }
+ // If either operand is a NaN return the other.
+ if (FPTypeInfo<T>::IsNaN(vs2)) return vs1;
+ if (FPTypeInfo<T>::IsNaN(vs1)) return vs2;
+ // Return the min/max of the two operands.
+ return operation(vs2, vs1);
+}
+
+// FP min reduction.
+void Vfredmin(const Instruction *inst) {
+ auto *rv_fp = static_cast<CheriotState *>(inst->state())->rv_fp();
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ if (!rv_fp->rounding_mode_valid()) {
+ LOG(ERROR) << "Invalid rounding mode";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int sew = rv_vector->selected_element_width();
+ ScopedFPStatus set_fpstatus(rv_fp->host_fp_interface());
+ switch (sew) {
+ case 4:
+ return RiscVBinaryReductionVectorOp<float, float, float>(
+ rv_vector, inst, [](float acc, float vs2) -> float {
+ return MaxMinHelper<float>(acc, vs2,
+ [](float acc, float vs2) -> float {
+ return (acc > vs2) ? vs2 : acc;
+ });
+ });
+ return;
+ case 8:
+ return RiscVBinaryReductionVectorOp<double, double, double>(
+ rv_vector, inst, [](double acc, double vs2) -> double {
+ return MaxMinHelper<double>(acc, vs2,
+ [](double acc, double vs2) -> double {
+ return (acc > vs2) ? vs2 : acc;
+ });
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// FP max reduction.
+void Vfredmax(const Instruction *inst) {
+ auto *rv_fp = static_cast<CheriotState *>(inst->state())->rv_fp();
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ if (!rv_fp->rounding_mode_valid()) {
+ LOG(ERROR) << "Invalid rounding mode";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int sew = rv_vector->selected_element_width();
+ ScopedFPStatus set_fpstatus(rv_fp->host_fp_interface());
+ switch (sew) {
+ case 4:
+ return RiscVBinaryReductionVectorOp<float, float, float>(
+ rv_vector, inst, [](float acc, float vs2) -> float {
+ return MaxMinHelper<float>(acc, vs2,
+ [](float acc, float vs2) -> float {
+ return (acc < vs2) ? vs2 : acc;
+ });
+ });
+ return;
+ case 8:
+ return RiscVBinaryReductionVectorOp<double, double, double>(
+ rv_vector, inst, [](double acc, double vs2) -> double {
+ return MaxMinHelper<double>(acc, vs2,
+ [](double acc, double vs2) -> double {
+ return (acc < vs2) ? vs2 : acc;
+ });
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+} // namespace cheriot
+} // namespace sim
+} // namespace mpact
diff --git a/cheriot/riscv_cheriot_vector_fp_reduction_instructions.h b/cheriot/riscv_cheriot_vector_fp_reduction_instructions.h
new file mode 100644
index 0000000..bfb4b6c
--- /dev/null
+++ b/cheriot/riscv_cheriot_vector_fp_reduction_instructions.h
@@ -0,0 +1,42 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef MPACT_CHERIOT_RISCV_CHERIOT_VECTOR_FP_REDUCTION_INSTRUCTIONS_H_
+#define MPACT_CHERIOT_RISCV_CHERIOT_VECTOR_FP_REDUCTION_INSTRUCTIONS_H_
+
+#include "mpact/sim/generic/instruction.h"
+
+// This file declares the semantic functions for the vector floating point
+// reduction instructions.
+
+namespace mpact {
+namespace sim {
+namespace cheriot {
+
+using Instruction = ::mpact::sim::generic::Instruction;
+
+// Each of these instruction semantic functions take 3 source operands and 1
+// destination operand. Source 0 is a vector register group, source 1 is a
+// vector register, and source 2 is the vector mask register. Destination
+// operand 0 is a vector register group.
+void Vfredosum(const Instruction *inst);
+void Vfwredosum(const Instruction *inst);
+void Vfredmin(const Instruction *inst);
+void Vfredmax(const Instruction *inst);
+
+} // namespace cheriot
+} // namespace sim
+} // namespace mpact
+
+#endif // MPACT_CHERIOT_RISCV_CHERIOT_VECTOR_FP_REDUCTION_INSTRUCTIONS_H_
diff --git a/cheriot/riscv_cheriot_vector_fp_unary_instructions.cc b/cheriot/riscv_cheriot_vector_fp_unary_instructions.cc
new file mode 100644
index 0000000..06db4e3
--- /dev/null
+++ b/cheriot/riscv_cheriot_vector_fp_unary_instructions.cc
@@ -0,0 +1,968 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cheriot/riscv_cheriot_vector_fp_unary_instructions.h"
+
+#include <cmath>
+#include <cstdint>
+#include <limits>
+#include <tuple>
+
+#include "absl/log/log.h"
+#include "cheriot/cheriot_state.h"
+#include "cheriot/riscv_cheriot_instruction_helpers.h"
+#include "cheriot/riscv_cheriot_vector_instruction_helpers.h"
+#include "mpact/sim/generic/instruction.h"
+#include "mpact/sim/generic/type_helpers.h"
+#include "riscv//riscv_fp_host.h"
+#include "riscv//riscv_fp_info.h"
+#include "riscv//riscv_fp_state.h"
+#include "riscv//riscv_vector_state.h"
+
+namespace mpact {
+namespace sim {
+namespace cheriot {
+
+using ::mpact::sim::generic::FPTypeInfo;
+using ::mpact::sim::riscv::FPExceptions;
+using ::mpact::sim::riscv::ScopedFPStatus;
+
+// These tables contain the 7 bits of mantissa used by the approximated
+// reciprocal square root and reciprocal instructions.
+static const int kRecipSqrtMantissaTable[128] = {
+ 52, 51, 50, 48, 47, 46, 44, 43, 42, 41, 40, 39, 38, 36, 35,
+ 34, 33, 32, 31, 30, 30, 29, 28, 27, 26, 25, 24, 23, 23, 22,
+ 21, 20, 19, 19, 18, 17, 16, 16, 15, 14, 14, 13, 12, 12, 11,
+ 10, 10, 9, 9, 8, 7, 7, 6, 6, 5, 4, 4, 3, 3, 2,
+ 2, 1, 1, 0, 127, 125, 123, 121, 119, 118, 116, 114, 113, 111, 109,
+ 108, 106, 105, 103, 102, 100, 99, 97, 96, 95, 93, 92, 91, 20, 88,
+ 87, 86, 85, 84, 83, 82, 80, 79, 78, 77, 76, 75, 74, 73, 72,
+ 71, 70, 70, 69, 68, 67, 66, 65, 64, 63, 63, 62, 61, 60, 59,
+ 59, 58, 57, 56, 56, 55, 54, 53,
+};
+
+static const int kRecipMantissaTable[128] = {
+ 127, 125, 123, 121, 119, 117, 116, 114, 112, 110, 109, 107, 105, 104, 102,
+ 100, 99, 97, 96, 94, 93, 91, 90, 88, 87, 85, 84, 83, 81, 80,
+ 79, 77, 76, 75, 74, 72, 71, 70, 69, 68, 66, 65, 64, 63, 62,
+ 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47,
+ 46, 45, 44, 43, 42, 41, 40, 40, 39, 38, 37, 36, 35, 35, 34,
+ 33, 32, 31, 31, 30, 29, 28, 28, 27, 26, 25, 25, 24, 23, 23,
+ 22, 21, 21, 20, 19, 19, 18, 17, 17, 16, 15, 15, 14, 14, 13,
+ 12, 12, 11, 11, 10, 9, 9, 8, 8, 7, 7, 6, 5, 5, 4,
+ 4, 3, 3, 2, 2, 1, 1, 0};
+
+// Move float from scalar fp register to vector register(all elements).
+void Vfmvvf(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ const int vl = rv_vector->vector_length();
+ if (rv_vector->vstart() > 0) return;
+ if (vl == 0) return;
+
+ const int sew = rv_vector->selected_element_width();
+ auto dest_op =
+ static_cast<RV32VectorDestinationOperand *>(inst->Destination(0));
+ auto dest_db = dest_op->CopyDataBuffer();
+ switch (sew) {
+ case 4:
+ for (int i = 0; i < vl; ++i) {
+ dest_db->Set<uint32_t>(
+ i, generic::GetInstructionSource<uint32_t>(inst, 0, 0));
+ }
+ break;
+ case 8:
+ for (int i = 0; i < vl; ++i) {
+ dest_db->Set<uint64_t>(
+ i, generic::GetInstructionSource<uint64_t>(inst, 0, 0));
+ }
+ break;
+ default:
+ dest_db->DecRef();
+ LOG(ERROR) << "Vfmv.s.f: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ dest_db->Submit();
+ rv_vector->clear_vstart();
+}
+
+// Move float from vector to scalar fp register(first element).
+void Vfmvsf(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ if (rv_vector->vstart() > 0) return;
+ if (rv_vector->vector_length() == 0) return;
+ int sew = rv_vector->selected_element_width();
+ auto dest_op =
+ static_cast<RV32VectorDestinationOperand *>(inst->Destination(0));
+ auto dest_db = dest_op->CopyDataBuffer();
+ switch (sew) {
+ case 4:
+ dest_db->Set<uint32_t>(
+ 0, generic::GetInstructionSource<uint32_t>(inst, 0, 0));
+ break;
+ case 8:
+ dest_db->Set<uint64_t>(
+ 0, generic::GetInstructionSource<uint64_t>(inst, 0, 0));
+ break;
+ default:
+ dest_db->DecRef();
+ LOG(ERROR) << "Vfmv.s.f: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ dest_db->Submit();
+ rv_vector->clear_vstart();
+}
+
+// Move scalar floating point value to element 0 of vector register.
+void Vfmvfs(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ auto dest_op = inst->Destination(0);
+ auto dest_db = dest_op->AllocateDataBuffer();
+ int db_size = dest_db->size<uint8_t>();
+ switch (sew) {
+ case 4: {
+ uint64_t value = generic::GetInstructionSource<uint32_t>(inst, 0, 0);
+ if (db_size == 4) {
+ dest_db->Set<uint32_t>(0, value);
+ } else if (db_size == 8) {
+ uint64_t val64 = 0xffff'ffff'0000'0000ULL | value;
+ dest_db->Set<uint64_t>(0, val64);
+ } else {
+ LOG(ERROR) << "Unexpected databuffer size in Vfmvfs";
+ }
+ break;
+ }
+ case 8:
+ dest_db->Set<uint64_t>(
+ 0, generic::GetInstructionSource<uint64_t>(inst, 0, 0));
+ break;
+ default:
+ dest_db->DecRef();
+ LOG(ERROR) << "Vfmv.f.s: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ dest_db->Submit();
+ rv_vector->clear_vstart();
+}
+
+// Convert floating point to unsigned integer.
+void Vfcvtxufv(const Instruction *inst) {
+ auto *rv_fp = static_cast<CheriotState *>(inst->state())->rv_fp();
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ if (!rv_fp->rounding_mode_valid()) {
+ LOG(ERROR) << "Invalid rounding mode";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 4:
+ return RiscVUnaryVectorOpWithFflags<uint32_t, float>(
+ rv_vector, inst, [](float vs2) -> std::tuple<uint32_t, uint32_t> {
+ return CvtHelper<float, uint32_t>(vs2);
+ });
+ case 8:
+ return RiscVUnaryVectorOpWithFflags<uint64_t, double>(
+ rv_vector, inst, [](double vs2) -> std::tuple<uint64_t, uint32_t> {
+ return CvtHelper<double, uint64_t>(vs2);
+ });
+ default:
+ LOG(ERROR) << "Vfcvt.xu.fv: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Convert floating point to signed integer.
+void Vfcvtxfv(const Instruction *inst) {
+ auto *rv_fp = static_cast<CheriotState *>(inst->state())->rv_fp();
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ if (!rv_fp->rounding_mode_valid()) {
+ LOG(ERROR) << "Invalid rounding mode";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 4:
+ return RiscVUnaryVectorOpWithFflags<int32_t, float>(
+ rv_vector, inst, [](float vs2) -> std::tuple<int32_t, uint32_t> {
+ return CvtHelper<float, int32_t>(vs2);
+ });
+ case 8:
+ return RiscVUnaryVectorOpWithFflags<int64_t, double>(
+ rv_vector, inst, [](double vs2) -> std::tuple<int64_t, uint32_t> {
+ return CvtHelper<double, int64_t>(vs2);
+ });
+ default:
+ LOG(ERROR) << "Vfcvt.x.fv: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Convert unsigned integer to floating point.
+void Vfcvtfxuv(const Instruction *inst) {
+ auto *rv_fp = static_cast<CheriotState *>(inst->state())->rv_fp();
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ if (!rv_fp->rounding_mode_valid()) {
+ LOG(ERROR) << "Invalid rounding mode";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int sew = rv_vector->selected_element_width();
+ ScopedFPStatus set_fpstatus(rv_fp->host_fp_interface());
+ switch (sew) {
+ case 4:
+ return RiscVUnaryVectorOp<float, uint32_t>(
+ rv_vector, inst,
+ [](uint32_t vs2) -> float { return static_cast<float>(vs2); });
+ case 8:
+ return RiscVUnaryVectorOp<double, uint64_t>(
+ rv_vector, inst,
+ [](uint64_t vs2) -> double { return static_cast<double>(vs2); });
+ default:
+ LOG(ERROR) << "Vfcvt.f.xuv: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Convert signed integer to floating point.
+void Vfcvtfxv(const Instruction *inst) {
+ auto *rv_fp = static_cast<CheriotState *>(inst->state())->rv_fp();
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ if (!rv_fp->rounding_mode_valid()) {
+ LOG(ERROR) << "Invalid rounding mode";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int sew = rv_vector->selected_element_width();
+ ScopedFPStatus set_fpstatus(rv_fp->host_fp_interface());
+ switch (sew) {
+ case 4:
+ return RiscVUnaryVectorOp<float, int32_t>(
+ rv_vector, inst,
+ [](int32_t vs2) -> float { return static_cast<float>(vs2); });
+ case 8:
+ return RiscVUnaryVectorOp<double, int64_t>(
+ rv_vector, inst,
+ [](int64_t vs2) -> double { return static_cast<double>(vs2); });
+ default:
+ LOG(ERROR) << "Vfcvt.f.xv: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Convert floating point to unsigned integer with truncation.
+void Vfcvtrtzxufv(const Instruction *inst) {
+ auto *rv_state = static_cast<CheriotState *>(inst->state());
+ auto *rv_vector = rv_state->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 4:
+ return RiscVUnaryVectorOpWithFflags<uint32_t, float>(
+ rv_vector, inst, [](float vs2) -> std::tuple<uint32_t, uint32_t> {
+ return CvtHelper<float, uint32_t>(vs2);
+ });
+ case 8:
+ return RiscVUnaryVectorOpWithFflags<uint64_t, double>(
+ rv_vector, inst, [](double vs2) -> std::tuple<uint64_t, uint32_t> {
+ return CvtHelper<double, uint64_t>(vs2);
+ });
+ default:
+ LOG(ERROR) << "Vfcvt.rtz.xu.fv: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Convert floating point to signed integer with truncation.
+void Vfcvtrtzxfv(const Instruction *inst) {
+ auto *rv_state = static_cast<CheriotState *>(inst->state());
+ auto *rv_vector = rv_state->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 4:
+ return RiscVUnaryVectorOpWithFflags<int32_t, float>(
+ rv_vector, inst, [](float vs2) -> std::tuple<int32_t, uint32_t> {
+ return CvtHelper<float, int32_t>(vs2);
+ });
+ case 8:
+ return RiscVUnaryVectorOpWithFflags<int64_t, double>(
+ rv_vector, inst, [](double vs2) -> std::tuple<int64_t, uint32_t> {
+ return CvtHelper<double, int64_t>(vs2);
+ });
+ default:
+ LOG(ERROR) << "Vfcvt.rtz.x.fv: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Widening conversion of floating point to unsigned integer.
+void Vfwcvtxufv(const Instruction *inst) {
+ auto *rv_fp = static_cast<CheriotState *>(inst->state())->rv_fp();
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ if (!rv_fp->rounding_mode_valid()) {
+ LOG(ERROR) << "Invalid rounding mode";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 4:
+ return RiscVUnaryVectorOpWithFflags<uint64_t, float>(
+ rv_vector, inst, [](float vs2) -> std::tuple<uint64_t, uint32_t> {
+ return CvtHelper<float, uint64_t>(vs2);
+ });
+ default:
+ LOG(ERROR) << "Vfwcvt.xu.fv: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Widening conversion of floating point to signed integer.
+void Vfwcvtxfv(const Instruction *inst) {
+ auto *rv_fp = static_cast<CheriotState *>(inst->state())->rv_fp();
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ if (!rv_fp->rounding_mode_valid()) {
+ LOG(ERROR) << "Invalid rounding mode";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 4:
+ return RiscVUnaryVectorOpWithFflags<int64_t, float>(
+ rv_vector, inst, [](float vs2) -> std::tuple<int64_t, uint32_t> {
+ return CvtHelper<float, int64_t>(vs2);
+ });
+ default:
+ LOG(ERROR) << "Vfwcvt.x.fv: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Wideing conversion of floating point to floating point.
+void Vfwcvtffv(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 4:
+ return RiscVUnaryVectorOp<double, float>(
+ rv_vector, inst,
+ [](float vs2) -> double { return static_cast<double>(vs2); });
+ default:
+ LOG(ERROR) << "Vfwcvt.f.fv: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Widening conversion of unsigned integer to floating point.
+void Vfwcvtfxuv(const Instruction *inst) {
+ auto *rv_fp = static_cast<CheriotState *>(inst->state())->rv_fp();
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ if (!rv_fp->rounding_mode_valid()) {
+ LOG(ERROR) << "Invalid rounding mode";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int sew = rv_vector->selected_element_width();
+ ScopedFPStatus set_fpstatus(rv_fp->host_fp_interface());
+ switch (sew) {
+ case 2:
+ return RiscVUnaryVectorOp<float, uint16_t>(
+ rv_vector, inst,
+ [](uint16_t vs2) -> float { return static_cast<float>(vs2); });
+ case 4:
+ return RiscVUnaryVectorOp<double, uint32_t>(
+ rv_vector, inst,
+ [](uint32_t vs2) -> double { return static_cast<double>(vs2); });
+ default:
+ LOG(ERROR) << "Vfwcvt.f.xuv: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Widening conversion of signed integer to floating point.
+void Vfwcvtfxv(const Instruction *inst) {
+ auto *rv_fp = static_cast<CheriotState *>(inst->state())->rv_fp();
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ if (!rv_fp->rounding_mode_valid()) {
+ LOG(ERROR) << "Invalid rounding mode";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int sew = rv_vector->selected_element_width();
+ ScopedFPStatus set_fpstatus(rv_fp->host_fp_interface());
+ switch (sew) {
+ case 2:
+ return RiscVUnaryVectorOp<float, int16_t>(
+ rv_vector, inst,
+ [](int16_t vs2) -> float { return static_cast<float>(vs2); });
+ case 4:
+ return RiscVUnaryVectorOp<double, int32_t>(
+ rv_vector, inst,
+ [](int32_t vs2) -> double { return static_cast<double>(vs2); });
+ default:
+ LOG(ERROR) << "Vfwcvt.f.xuv: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Widening conversion of floating point to unsigned integer with truncation.
+void Vfwcvtrtzxufv(const Instruction *inst) {
+ auto *rv_state = static_cast<CheriotState *>(inst->state());
+ auto *rv_vector = rv_state->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 4:
+ return RiscVUnaryVectorOpWithFflags<uint64_t, float>(
+ rv_vector, inst, [](float vs2) -> std::tuple<uint64_t, uint32_t> {
+ return CvtHelper<float, uint64_t>(vs2);
+ });
+ default:
+ LOG(ERROR) << "Vwfcvt.rtz.xu.fv: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Widening conversion of floating point to signed integer with truncation.
+void Vfwcvtrtzxfv(const Instruction *inst) {
+ auto *rv_state = static_cast<CheriotState *>(inst->state());
+ auto *rv_vector = rv_state->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 4:
+ return RiscVUnaryVectorOpWithFflags<int64_t, float>(
+ rv_vector, inst, [](float vs2) -> std::tuple<int64_t, uint32_t> {
+ return CvtHelper<float, int64_t>(vs2);
+ });
+ default:
+ LOG(ERROR) << "Vwfcvt.rtz.x.fv: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Narrowing conversion of floating point to unsigned integer.
+void Vfncvtxufw(const Instruction *inst) {
+ auto *rv_fp = static_cast<CheriotState *>(inst->state())->rv_fp();
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ if (!rv_fp->rounding_mode_valid()) {
+ LOG(ERROR) << "Invalid rounding mode";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 4:
+ return RiscVUnaryVectorOpWithFflags<uint16_t, float>(
+ rv_vector, inst, [](float vs2) -> std::tuple<uint16_t, uint32_t> {
+ return CvtHelper<float, uint16_t>(vs2);
+ });
+ case 8:
+ return RiscVUnaryVectorOpWithFflags<uint32_t, double>(
+ rv_vector, inst, [](double vs2) -> std::tuple<uint32_t, uint32_t> {
+ return CvtHelper<double, uint32_t>(vs2);
+ });
+ default:
+ LOG(ERROR) << "Vfncvt.xu.fw: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Narrowing conversion of floating point to signed integer.
+void Vfncvtxfw(const Instruction *inst) {
+ auto *rv_fp = static_cast<CheriotState *>(inst->state())->rv_fp();
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ if (!rv_fp->rounding_mode_valid()) {
+ LOG(ERROR) << "Invalid rounding mode";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 4:
+ return RiscVUnaryVectorOpWithFflags<int16_t, float>(
+ rv_vector, inst, [](float vs2) -> std::tuple<int16_t, uint32_t> {
+ return CvtHelper<float, int16_t>(vs2);
+ });
+ case 8:
+ return RiscVUnaryVectorOpWithFflags<int32_t, double>(
+ rv_vector, inst, [](double vs2) -> std::tuple<int32_t, uint32_t> {
+ return CvtHelper<double, int32_t>(vs2);
+ });
+ default:
+ LOG(ERROR) << "Vfncvt.x.fw: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Narrowing conversion of floating point to floating point.
+void Vfncvtffw(const Instruction *inst) {
+ auto *rv_fp = static_cast<CheriotState *>(inst->state())->rv_fp();
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ if (!rv_fp->rounding_mode_valid()) {
+ LOG(ERROR) << "Invalid rounding mode";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int sew = rv_vector->selected_element_width();
+ ScopedFPStatus set_fpstatus(rv_fp->host_fp_interface());
+ switch (sew) {
+ case 8:
+ return RiscVUnaryVectorOp<float, double>(
+ rv_vector, inst,
+ [](double vs2) -> float { return static_cast<float>(vs2); });
+ default:
+ LOG(ERROR) << "Vfwcvt.f.fw: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Narrowing conversion of floating point to floating point rounding to odd.
+void Vfncvtrodffw(const Instruction *inst) {
+ auto *rv_fp = static_cast<CheriotState *>(inst->state())->rv_fp();
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ if (!rv_fp->rounding_mode_valid()) {
+ LOG(ERROR) << "Invalid rounding mode";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int sew = rv_vector->selected_element_width();
+ // The rounding mode is round to odd, which means that the lsb of the new
+ // mantissa is either 1 or it is the logical or of all the bits to the right
+ // in the original width mantissa.
+ switch (sew) {
+ case 8:
+ return RiscVUnaryVectorOp<float, double>(
+ rv_vector, inst, [](double vs2) -> float {
+ if (FPTypeInfo<double>::IsNaN(vs2) ||
+ FPTypeInfo<double>::IsInf(vs2)) {
+ return static_cast<float>(vs2);
+ }
+ using UIntD = typename FPTypeInfo<double>::UIntType;
+ using UIntF = typename FPTypeInfo<float>::UIntType;
+ UIntD uval = *reinterpret_cast<UIntD *>(&vs2);
+ int sig_diff =
+ FPTypeInfo<double>::kSigSize - FPTypeInfo<float>::kSigSize;
+ UIntD mask = (1ULL << sig_diff) - 1;
+ UIntF bit = (mask & uval) != 0;
+ auto res = static_cast<float>(vs2);
+ if (FPTypeInfo<float>::IsInf(res)) return res;
+ UIntF ures = *reinterpret_cast<UIntF *>(&res);
+ ures |= bit;
+ return *reinterpret_cast<float *>(&ures);
+ });
+ default:
+ LOG(ERROR) << "Vfwcvt.rod.f.fw: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Narrowing conversion of unsigned integer to floating point.
+void Vfncvtfxuw(const Instruction *inst) {
+ auto *rv_fp = static_cast<CheriotState *>(inst->state())->rv_fp();
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ if (!rv_fp->rounding_mode_valid()) {
+ LOG(ERROR) << "Invalid rounding mode";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int sew = rv_vector->selected_element_width();
+ ScopedFPStatus set_fpstatus(rv_fp->host_fp_interface());
+ switch (sew) {
+ case 8:
+ return RiscVUnaryVectorOp<float, uint64_t>(
+ rv_vector, inst,
+ [](uint64_t vs2) -> float { return static_cast<float>(vs2); });
+ default:
+ LOG(ERROR) << "Vfncvt.f.xuw: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Narrowing conversion of signed integeer to floating point.
+void Vfncvtfxw(const Instruction *inst) {
+ auto *rv_fp = static_cast<CheriotState *>(inst->state())->rv_fp();
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ if (!rv_fp->rounding_mode_valid()) {
+ LOG(ERROR) << "Invalid rounding mode";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int sew = rv_vector->selected_element_width();
+ ScopedFPStatus set_fpstatus(rv_fp->host_fp_interface());
+ switch (sew) {
+ case 8:
+ return RiscVUnaryVectorOp<float, int64_t>(
+ rv_vector, inst,
+ [](int64_t vs2) -> float { return static_cast<float>(vs2); });
+ default:
+ LOG(ERROR) << "Vfncvt.f.xw: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Narrowing conversion of floating point to unsigned integer with truncation.
+void Vfncvtrtzxufw(const Instruction *inst) {
+ auto *rv_state = static_cast<CheriotState *>(inst->state());
+ auto *rv_vector = rv_state->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 4:
+ return RiscVUnaryVectorOpWithFflags<uint16_t, float>(
+ rv_vector, inst, [](float vs2) -> std::tuple<uint16_t, uint32_t> {
+ return CvtHelper<float, uint16_t>(vs2);
+ });
+ case 8:
+ return RiscVUnaryVectorOpWithFflags<uint32_t, double>(
+ rv_vector, inst, [](double vs2) -> std::tuple<uint32_t, uint32_t> {
+ return CvtHelper<double, uint32_t>(vs2);
+ });
+ default:
+ LOG(ERROR) << "Vfcvt.rtz.xu.fw: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Narrowing conversion of floating point to signed integer with truncation.
+void Vfncvtrtzxfw(const Instruction *inst) {
+ auto *rv_state = static_cast<CheriotState *>(inst->state());
+ auto *rv_vector = rv_state->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 4:
+ return RiscVUnaryVectorOpWithFflags<int16_t, float>(
+ rv_vector, inst, [](float vs2) -> std::tuple<int16_t, uint32_t> {
+ return CvtHelper<float, int16_t>(vs2);
+ });
+ case 8:
+ return RiscVUnaryVectorOpWithFflags<int32_t, double>(
+ rv_vector, inst, [](double vs2) -> std::tuple<int32_t, uint32_t> {
+ return CvtHelper<double, int32_t>(vs2);
+ });
+ default:
+ LOG(ERROR) << "Vfcvt.rtz.xu.fw: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Templated helper function to compute square root.
+template <typename T>
+inline std::tuple<T, uint32_t> SqrtHelper(T vs2) {
+ uint32_t flags = 0;
+ T res;
+ if (FPTypeInfo<T>::IsNaN(vs2) || vs2 < 0.0) {
+ auto value = FPTypeInfo<T>::kCanonicalNaN;
+ res = *reinterpret_cast<T *>(&value);
+ flags = *FPExceptions::kInvalidOp;
+ return std::tie(res, flags);
+ }
+ if (vs2 == 0.0) return std::tie(vs2, flags);
+ res = sqrt(vs2);
+ return std::tie(res, flags);
+}
+
+// Square root.
+void Vfsqrtv(const Instruction *inst) {
+ auto *rv_fp = static_cast<CheriotState *>(inst->state())->rv_fp();
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ if (!rv_fp->rounding_mode_valid()) {
+ LOG(ERROR) << "Invalid rounding mode";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int sew = rv_vector->selected_element_width();
+ uint32_t flags = 0;
+ {
+ ScopedFPStatus set_fpstatus(rv_fp->host_fp_interface());
+ switch (sew) {
+ case 4:
+ RiscVUnaryVectorOp<float, float>(rv_vector, inst,
+ [&flags](float vs2) -> float {
+ auto [res, f] = SqrtHelper(vs2);
+ flags |= f;
+ return res;
+ });
+ break;
+ case 8:
+ RiscVUnaryVectorOp<double, double>(rv_vector, inst,
+ [&flags](double vs2) -> double {
+ auto [res, f] = SqrtHelper(vs2);
+ flags |= f;
+ return res;
+ });
+ break;
+ default:
+ LOG(ERROR) << "Vffcvt.f.xuv: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ }
+ auto *fflags = rv_fp->fflags();
+ fflags->Write(flags | fflags->AsUint32());
+}
+
+// Templated helper function to compute the Reciprocal Square Root
+// approximation for valid inputs.
+template <typename T>
+inline T RecipSqrt7(T value) {
+ using Uint = typename FPTypeInfo<T>::UIntType;
+ Uint uint_value = *reinterpret_cast<Uint *>(&value);
+ // The input value is positive. Negative values are already handled.
+ int norm_exponent =
+ (uint_value & FPTypeInfo<T>::kExpMask) >> FPTypeInfo<T>::kSigSize;
+ Uint norm_mantissa = uint_value & FPTypeInfo<T>::kSigMask;
+ if (norm_exponent == 0) { // The value is a denormal.
+ Uint mask = static_cast<Uint>(1) << (FPTypeInfo<T>::kSigSize - 1);
+ // Normalize the mantissa and exponent by shifting the mantissa left until
+ // the most significant bit is one.
+ while ((norm_mantissa & mask) == 0) {
+ norm_exponent--;
+ norm_mantissa <<= 1;
+ }
+ // Shift it left once more - so it becomes the "implied" bit, and not used
+ // in the lookup below.
+ norm_mantissa <<= 1;
+ }
+ int index = (norm_exponent & 0b1) << 6 |
+ ((norm_mantissa >> (FPTypeInfo<T>::kSigSize - 6)) & 0b11'1111);
+ Uint new_mantissa = static_cast<Uint>(kRecipSqrtMantissaTable[index])
+ << (FPTypeInfo<T>::kSigSize - 7);
+ Uint new_exponent = (3 * FPTypeInfo<T>::kExpBias - 1 - norm_exponent) / 2;
+ Uint new_value = (new_exponent << FPTypeInfo<T>::kSigSize) | new_mantissa;
+ T new_fp_value = *reinterpret_cast<T *>(&new_value);
+ return new_fp_value;
+}
+
+// Templated helper function to compute the Reciprocal Square Root
+// approximation for all values.
+template <typename T>
+inline std::tuple<T, uint32_t> RecipSqrt7Helper(T value) {
+ auto fp_class = std::fpclassify(value);
+ T return_value = std::numeric_limits<T>::quiet_NaN();
+ uint32_t fflags = 0;
+ switch (fp_class) {
+ case FP_INFINITE:
+ return_value =
+ std::signbit(value) ? std::numeric_limits<T>::quiet_NaN() : 0.0;
+ fflags = (uint32_t)FPExceptions::kInvalidOp;
+ break;
+ case FP_NAN:
+ // Just propagate the NaN.
+ return_value = std::numeric_limits<T>::quiet_NaN();
+ fflags = (uint32_t)FPExceptions::kInvalidOp;
+ break;
+ case FP_ZERO:
+ return_value = std::signbit(value) ? -std::numeric_limits<T>::infinity()
+ : std::numeric_limits<T>::infinity();
+ fflags = (uint32_t)FPExceptions::kDivByZero;
+ break;
+ case FP_SUBNORMAL:
+ case FP_NORMAL:
+ if (std::signbit(value)) {
+ return_value = std::numeric_limits<T>::quiet_NaN();
+ fflags = (uint32_t)FPExceptions::kInvalidOp;
+ } else {
+ return_value = RecipSqrt7(value);
+ }
+ break;
+ default:
+ LOG(ERROR) << "RecipSqrt7Helper: Illegal fp_class (" << fp_class << ")";
+ break;
+ }
+ return std::make_tuple(return_value, fflags);
+}
+
+// Approximation of reciprocal square root to 7 bits mantissa.
+void Vfrsqrt7v(const Instruction *inst) {
+ auto *rv_fp = static_cast<CheriotState *>(inst->state())->rv_fp();
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 4:
+ return RiscVUnaryVectorOpWithFflags<float, float>(
+ rv_vector, inst, [rv_fp](float vs2) -> std::tuple<float, uint32_t> {
+ ScopedFPStatus set_fpstatus(rv_fp->host_fp_interface());
+ return RecipSqrt7Helper(vs2);
+ });
+ case 8:
+ return RiscVUnaryVectorOpWithFflags<double, double>(
+ rv_vector, inst, [rv_fp](double vs2) -> std::tuple<double, uint32_t> {
+ ScopedFPStatus set_fpstatus(rv_fp->host_fp_interface());
+ return RecipSqrt7Helper(vs2);
+ });
+ default:
+ LOG(ERROR) << "vfrsqrt7.v: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Templated helper function to compute the Reciprocal approximation for valid
+// normal floating point inputs.
+template <typename T>
+inline T Recip7(T value, FPRoundingMode rm) {
+ using Uint = typename FPTypeInfo<T>::UIntType;
+ using Int = typename FPTypeInfo<T>::IntType;
+ Uint uint_value = *reinterpret_cast<Uint *>(&value);
+ Int norm_exponent =
+ (uint_value & FPTypeInfo<T>::kExpMask) >> FPTypeInfo<T>::kSigSize;
+ Uint norm_mantissa = uint_value & FPTypeInfo<T>::kSigMask;
+ if (norm_exponent == 0) { // The value is a denormal.
+ Uint msb = static_cast<Uint>(1) << (FPTypeInfo<T>::kSigSize - 1);
+ // Normalize the mantissa and exponent by shifting the mantissa left until
+ // the most significant bit is one.
+ while (norm_mantissa && ((norm_mantissa & msb) == 0)) {
+ norm_exponent--;
+ norm_mantissa <<= 1;
+ }
+ // Shift it left once more - so it becomes the "implied" bit, and not used
+ // in the lookup below.
+ norm_mantissa <<= 1;
+ }
+ Int new_exponent = 2 * FPTypeInfo<T>::kExpBias - 1 - norm_exponent;
+ // If the exponent is too high, then return exceptional values.
+ if (new_exponent > 2 * FPTypeInfo<T>::kExpBias) {
+ switch (rm) {
+ case FPRoundingMode::kRoundDown:
+ return std::signbit(value) ? -std::numeric_limits<T>::infinity()
+ : std::numeric_limits<T>::max();
+ case FPRoundingMode::kRoundTowardsZero:
+ return std::signbit(value) ? std::numeric_limits<T>::lowest()
+ : std::numeric_limits<T>::max();
+ case FPRoundingMode::kRoundToNearestTiesToMax:
+ case FPRoundingMode::kRoundToNearest:
+ return std::signbit(value) ? -std::numeric_limits<T>::infinity()
+ : std::numeric_limits<T>::infinity();
+ case FPRoundingMode::kRoundUp:
+ return std::signbit(value) ? std::numeric_limits<T>::lowest()
+ : std::numeric_limits<T>::infinity();
+ default:
+ // kDynamic can't happen.
+ return std::numeric_limits<T>::quiet_NaN();
+ }
+ }
+ // Perform table lookup and compute the new value using the new exponent.
+ int index = (norm_mantissa >> (FPTypeInfo<T>::kSigSize - 7)) & 0b111'1111;
+ Uint new_mantissa = static_cast<Uint>(kRecipMantissaTable[index])
+ << (FPTypeInfo<T>::kSigSize - 7);
+ // If the new exponent is negative or 0, the result is denormal. First
+ // shift the mantissa right and or in the implied '1'.
+ if (new_exponent <= 0) {
+ new_mantissa = (new_mantissa >> 1) | 0b100'0000;
+ // If the exponent is less than 0, shift the mantissa right.
+ if (new_exponent < 0) {
+ new_mantissa >>= 1;
+ new_exponent = 0;
+ }
+ new_mantissa &= 0b111'1111;
+ }
+ Uint new_value = (new_exponent << FPTypeInfo<T>::kSigSize) | new_mantissa;
+ T new_fp_value = *reinterpret_cast<T *>(&new_value);
+ return value < 0.0 ? -new_fp_value : new_fp_value;
+}
+
+// Templated helper function to compute the Reciprocal approximation for all
+// values including non-normal floating point values.
+template <typename T>
+inline T Recip7Helper(T value, FPRoundingMode rm) {
+ auto fp_class = std::fpclassify(value);
+
+ switch (fp_class) {
+ case FP_INFINITE:
+ // TODO: raise exception.
+ return std::signbit(value) ? -0.0 : 0;
+ case FP_NAN:
+ // Just propagate the NaN.
+ return std::numeric_limits<T>::quiet_NaN();
+ case FP_ZERO:
+ return std::signbit(value) ? -std::numeric_limits<T>::infinity()
+ : std::numeric_limits<T>::infinity();
+ case FP_SUBNORMAL:
+ case FP_NORMAL:
+ return Recip7(value, rm);
+ }
+ return std::numeric_limits<T>::quiet_NaN();
+}
+
+// Approximate reciprocal to 7 bits of mantissa.
+void Vfrec7v(const Instruction *inst) {
+ auto *rv_fp = static_cast<CheriotState *>(inst->state())->rv_fp();
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ ScopedFPStatus set_fpstatus(rv_fp->host_fp_interface());
+ auto rm = rv_fp->GetRoundingMode();
+ switch (sew) {
+ case 4:
+ return RiscVUnaryVectorOp<float, float>(
+ rv_vector, inst,
+ [rm](float vs2) -> float { return Recip7Helper(vs2, rm); });
+ case 8:
+ return RiscVUnaryVectorOp<double, double>(
+ rv_vector, inst,
+ [rm](double vs2) -> double { return Recip7Helper(vs2, rm); });
+ default:
+ LOG(ERROR) << "vfrec7.v: Illegal sew (" << sew << ")";
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Classify floating point value.
+void Vfclassv(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 4:
+ return RiscVUnaryVectorOp<uint32_t, float>(
+ rv_vector, inst, [](float vs2) -> uint32_t {
+ return static_cast<uint32_t>(ClassifyFP(vs2));
+ });
+ case 8:
+ return RiscVUnaryVectorOp<uint64_t, double>(
+ rv_vector, inst, [](double vs2) -> uint64_t {
+ return static_cast<uint64_t>(ClassifyFP(vs2));
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "vfclass.v: Illegal sew (" << sew << ")";
+ return;
+ }
+}
+
+} // namespace cheriot
+} // namespace sim
+} // namespace mpact
diff --git a/cheriot/riscv_cheriot_vector_fp_unary_instructions.h b/cheriot/riscv_cheriot_vector_fp_unary_instructions.h
new file mode 100644
index 0000000..ad607d3
--- /dev/null
+++ b/cheriot/riscv_cheriot_vector_fp_unary_instructions.h
@@ -0,0 +1,115 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef MPACT_CHERIOT_RISCV_CHERIOT_VECTOR_FP_UNARY_INSTRUCTIONS_H_
+#define MPACT_CHERIOT_RISCV_CHERIOT_VECTOR_FP_UNARY_INSTRUCTIONS_H_
+
+#include "mpact/sim/generic/instruction.h"
+
+// This file lists the semantic functions for RiscV vector unary floating point
+// instructions.
+
+namespace mpact {
+namespace sim {
+namespace cheriot {
+
+using Instruction = ::mpact::sim::generic::Instruction;
+
+// Move a single floating point value from vector[0] to vector[vl-1].
+void Vfmvvf(const Instruction *inst);
+// Move a single floating point value from vector[0] to scalar fp register.
+void Vfmvsf(const Instruction *inst);
+// Move single floating point value from scalar fp register to vector[0].
+void Vfmvfs(const Instruction *inst);
+
+// Each of the following semantic functions take 2 source operands and 1
+// destination operand. Source operand 0 is a vector register group, source
+// operand 1 is a vector mask register, and destination operand 0 is a vector
+// register group.
+
+// Vector element conversion instructions. These convert same sized values
+// from/to signed/unsigned integer and floating point. The 'rtz' versions use
+// truncation (round to zero), whereas the others use the dynamically set
+// rounding mode.
+
+// FP to unsigned integer.
+void Vfcvtxufv(const Instruction *inst);
+// FP to signed integer.
+void Vfcvtxfv(const Instruction *inst);
+// Unsigned integer to FP.
+void Vfcvtfxuv(const Instruction *inst);
+// Signed integer to FP.
+void Vfcvtfxv(const Instruction *inst);
+// FP to unsigned integer using round to zero.
+void Vfcvtrtzxufv(const Instruction *inst);
+// FP to signed integer using round to zero.
+void Vfcvtrtzxfv(const Instruction *inst);
+
+// Vector element widening conversion instructions. These convert values from/to
+// signed/unsigned integer and floating point, where the resulting value is 2x
+// the width of the source value. The 'rtz' versions use truncation (round to
+// zero), whereas the others use the dynamically set rounding mode.
+
+// FP to wider unsigned integer.
+void Vfwcvtxufv(const Instruction *inst);
+// FP to wider signed integer.
+void Vfwcvtxfv(const Instruction *inst);
+// FP to next wider FP.
+void Vfwcvtffv(const Instruction *inst);
+// Unsigned integer to wider FP.
+void Vfwcvtfxuv(const Instruction *inst);
+// Signed integer to wider FP.
+void Vfwcvtfxv(const Instruction *inst);
+// FP to wider unsigned integer using round to zero.
+void Vfwcvtrtzxufv(const Instruction *inst);
+// FP to wider signed integer using round to zero.
+void Vfwcvtrtzxfv(const Instruction *inst);
+
+// Vector element widening conversion instructions. These convert values from/to
+// signed/unsigned integer and floating point, where the resulting value is 1/2x
+// the width of the source value. The 'rtz' versions use truncation (round to
+// zero), the 'rod' version uses 'round to odd', whereas the others use the
+// dynamically set rounding mode.
+
+// FP to narrower unsigned integer.
+void Vfncvtxufw(const Instruction *inst);
+// FP to narrower signed integer.
+void Vfncvtxfw(const Instruction *inst);
+// FP to next narrower FP.
+void Vfncvtffw(const Instruction *inst);
+// FP to next narrower FP with round to odd.
+void Vfncvtrodffw(const Instruction *inst);
+// Unsigned integer to narrower FP.
+void Vfncvtfxuw(const Instruction *inst);
+// Signed integer to narrower FP.
+void Vfncvtfxw(const Instruction *inst);
+// FP to narrower unsigned integer using round to zero.
+void Vfncvtrtzxufw(const Instruction *inst);
+// FP to narrower signed integer using round to zero.
+void Vfncvtrtzxfw(const Instruction *inst);
+
+// Vector element square root instruction.
+void Vfsqrtv(const Instruction *inst);
+// Vector element approximate reciprocal square root instruction.
+void Vfrsqrt7v(const Instruction *inst);
+// Vector element approximate reciprocal instruction.
+void Vfrec7v(const Instruction *inst);
+// Vector element floating point value classify instruction.
+void Vfclassv(const Instruction *inst);
+
+} // namespace cheriot
+} // namespace sim
+} // namespace mpact
+
+#endif // MPACT_CHERIOT_RISCV_CHERIOT_VECTOR_FP_UNARY_INSTRUCTIONS_H_
diff --git a/cheriot/riscv_cheriot_vector_instruction_helpers.h b/cheriot/riscv_cheriot_vector_instruction_helpers.h
new file mode 100644
index 0000000..b7640e0
--- /dev/null
+++ b/cheriot/riscv_cheriot_vector_instruction_helpers.h
@@ -0,0 +1,748 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef THIRD_PARTY_MPACT_RISCV_RISCV_RISCV_VECTOR_INSTRUCTION_HELPERS_H_
+#define THIRD_PARTY_MPACT_RISCV_RISCV_RISCV_VECTOR_INSTRUCTION_HELPERS_H_
+
+#include <algorithm>
+#include <cstdint>
+#include <functional>
+#include <limits>
+#include <optional>
+#include <tuple>
+
+#include "absl/log/log.h"
+#include "absl/strings/str_cat.h"
+#include "cheriot/cheriot_vector_state.h"
+#include "mpact/sim/generic/instruction.h"
+#include "mpact/sim/generic/type_helpers.h"
+#include "riscv//riscv_fp_host.h"
+#include "riscv//riscv_fp_info.h"
+#include "riscv//riscv_register.h"
+#include "riscv//riscv_state.h"
+
+namespace mpact {
+namespace sim {
+namespace cheriot {
+
+using ::mpact::sim::cheriot::CheriotVectorState;
+using ::mpact::sim::generic::FPTypeInfo;
+using ::mpact::sim::generic::GetInstructionSource;
+using ::mpact::sim::generic::Instruction;
+using ::mpact::sim::riscv::FPExceptions;
+using ::mpact::sim::riscv::RV32VectorDestinationOperand;
+using ::mpact::sim::riscv::RV32VectorSourceOperand;
+using ::mpact::sim::riscv::ScopedFPStatus;
+using ::mpact::sim::riscv::VectorLoadContext;
+
+// This helper function handles the case of instructions that target a vector
+// mask.
+// It clears the masked bit and uses the mask value in the
+// instruction, such as carry generation from add with carry.
+// Note that this function will modify masked bits no matter what the mask
+// value is.
+template <typename Vs2, typename Vs1>
+void RiscVSetMaskBinaryVectorMaskOp(CheriotVectorState *rv_vector,
+ const Instruction *inst,
+ std::function<bool(Vs2, Vs1, bool)> op) {
+ if (rv_vector->vector_exception()) return;
+ auto *dest_op =
+ static_cast<RV32VectorDestinationOperand *>(inst->Destination(0));
+ // Get the vector start element index and compute where to start
+ // the operation.
+ const int num_elements = rv_vector->vector_length();
+ const int vector_index = rv_vector->vstart();
+ // Allocate data buffer for the new register data.
+ auto *dest_db = dest_op->CopyDataBuffer();
+ auto dest_span = dest_db->Get<uint8_t>();
+ // Determine if it's vector-vector or vector-scalar.
+ const bool vector_scalar = inst->Source(1)->shape()[0] == 1;
+ // Get the vector mask.
+ auto *mask_op = static_cast<RV32VectorSourceOperand *>(inst->Source(2));
+ bool vm_unmasked_bit = false;
+ if (inst->SourcesSize() > 3) {
+ vm_unmasked_bit = GetInstructionSource<bool>(inst, 3);
+ }
+ const bool mask_used = !vm_unmasked_bit;
+ auto mask_span = mask_op->GetRegister(0)->data_buffer()->Get<uint8_t>();
+ for (int i = vector_index; i < num_elements; i++) {
+ const int mask_index = i >> 3;
+ const int mask_offset = i & 0b111;
+ const bool mask_value = ((mask_span[mask_index] >> mask_offset) & 0b1) != 0;
+ const Vs2 vs2 = GetInstructionSource<Vs2>(inst, 0, i);
+ const Vs1 vs1 = GetInstructionSource<Vs1>(inst, 1, vector_scalar ? 0 : i);
+
+ // Clear the masked register bit.
+ dest_span[mask_index] &= ~(1 << mask_offset);
+
+ // Mask value is used only when `vm_unmasked_bit` is 0.
+ dest_span[mask_index] |=
+ (op(vs2, vs1, mask_used & mask_value) << mask_offset);
+ }
+ // Submit the destination db .
+ dest_db->Submit();
+ rv_vector->clear_vstart();
+}
+
+// This helper function handles the case of instructions that target a vector
+// mask and uses the mask value in the instruction, such as carry generation
+// from add with carry.
+template <typename Vs2, typename Vs1>
+void RiscVMaskBinaryVectorMaskOp(CheriotVectorState *rv_vector,
+ const Instruction *inst,
+ std::function<bool(Vs2, Vs1, bool)> op) {
+ if (rv_vector->vector_exception()) return;
+ auto *dest_op =
+ static_cast<RV32VectorDestinationOperand *>(inst->Destination(0));
+ // Get the vector start element index and compute where to start
+ // the operation.
+ int num_elements = rv_vector->vector_length();
+ int vector_index = rv_vector->vstart();
+ // Allocate data buffer for the new register data.
+ auto *dest_db = dest_op->CopyDataBuffer();
+ auto dest_span = dest_db->Get<uint8_t>();
+ // Determine if it's vector-vector or vector-scalar.
+ bool vector_scalar = inst->Source(1)->shape()[0] == 1;
+ // Get the vector mask.
+ auto *mask_op = static_cast<RV32VectorSourceOperand *>(inst->Source(2));
+ bool vm_unmasked_bit = false;
+ if (inst->SourcesSize() > 3) {
+ vm_unmasked_bit = GetInstructionSource<bool>(inst, 3);
+ }
+ const bool mask_used = !vm_unmasked_bit;
+ auto mask_span = mask_op->GetRegister(0)->data_buffer()->Get<uint8_t>();
+ for (int i = vector_index; i < num_elements; i++) {
+ int mask_index = i >> 3;
+ int mask_offset = i & 0b111;
+ bool mask_value = ((mask_span[mask_index] >> mask_offset) & 0b1) != 0;
+ if (mask_used && !mask_value) {
+ continue;
+ }
+
+ Vs2 vs2 = GetInstructionSource<Vs2>(inst, 0, i);
+ Vs1 vs1 = GetInstructionSource<Vs1>(inst, 1, vector_scalar ? 0 : i);
+
+ // Clear the masked register bit.
+ dest_span[mask_index] &= ~(1 << mask_offset);
+
+ // Mask value is used only when `vm_unmasked_bit` is 0.
+ dest_span[mask_index] |=
+ (op(vs2, vs1, mask_used & mask_value) << mask_offset);
+ }
+ // Submit the destination db .
+ dest_db->Submit();
+ rv_vector->clear_vstart();
+}
+
+// This helper function handles the case of vector mask
+// operations.
+template <typename Vs2, typename Vs1>
+void RiscVBinaryVectorMaskOp(CheriotVectorState *rv_vector,
+ const Instruction *inst,
+ std::function<bool(Vs2, Vs1)> op) {
+ RiscVMaskBinaryVectorMaskOp<Vs2, Vs1>(
+ rv_vector, inst, [op](Vs2 vs2, Vs1 vs1, bool mask_value) -> bool {
+ if (mask_value) {
+ return op(vs2, vs1);
+ }
+ return false;
+ });
+}
+
+// This helper function handles the case of nullary vector
+// operations. It implements all the checking necessary for both widening and
+// narrowing operations.
+template <typename Vd>
+void RiscVMaskNullaryVectorOp(CheriotVectorState *rv_vector,
+ const Instruction *inst,
+ std::function<Vd(bool)> op) {
+ if (rv_vector->vector_exception()) return;
+ int num_elements = rv_vector->vector_length();
+ int elements_per_vector =
+ rv_vector->vector_register_byte_length() / sizeof(Vd);
+ int max_regs = (num_elements + elements_per_vector - 1) / elements_per_vector;
+ auto *dest_op =
+ static_cast<RV32VectorDestinationOperand *>(inst->Destination(0));
+ // Verify that there are enough registers in the destination operand.
+ if (dest_op->size() < max_regs) {
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << absl::StrCat(
+ "Vector destination '", dest_op->AsString(), "' has fewer registers (",
+ dest_op->size(), ") than required by the operation (", max_regs, ")");
+ return;
+ }
+ // There 2 types of instruction with different number of source operands.
+ // 1. inst vd, vs2, vmask (viota instruction)
+ // 2. inst vd, vmask (vid instruction)
+ RV32VectorSourceOperand *vs2_op = nullptr;
+ RV32VectorSourceOperand *mask_op = nullptr;
+ if (inst->SourcesSize() > 1) {
+ vs2_op = static_cast<RV32VectorSourceOperand *>(inst->Source(0));
+ mask_op = static_cast<RV32VectorSourceOperand *>(inst->Source(1));
+ } else {
+ mask_op = static_cast<RV32VectorSourceOperand *>(inst->Source(0));
+ }
+ auto mask_span = mask_op->GetRegister(0)->data_buffer()->Get<uint8_t>();
+ // Get the vector start element index and compute where to start
+ // the operation.
+ int vector_index = rv_vector->vstart();
+ int start_reg = vector_index / elements_per_vector;
+ int item_index = vector_index % elements_per_vector;
+ // Iterate over the number of registers to write.
+ for (int reg = start_reg; (reg < max_regs) && (vector_index < num_elements);
+ reg++) {
+ // Allocate data buffer for the new register data.
+ auto *dest_db = dest_op->CopyDataBuffer(reg);
+ auto dest_span = dest_db->Get<Vd>();
+ // Write data into register subject to masking.
+ int element_count = std::min(elements_per_vector, num_elements);
+ for (int i = item_index;
+ (i < element_count) && (vector_index < num_elements); i++) {
+ // Get the mask value.
+ int mask_index = vector_index >> 3;
+ int mask_offset = vector_index & 0b111;
+ bool mask_value = ((mask_span[mask_index] >> mask_offset) & 0b1) != 0;
+ bool operation_mask = mask_value;
+ // Instruction with rs2 operand checks vs2 bit value.
+ if (vs2_op != nullptr) {
+ const auto rs2_span =
+ vs2_op->GetRegister(0)->data_buffer()->Get<uint8_t>();
+ const bool rs2_value = ((rs2_span[mask_index] >> mask_offset) & 0b1);
+ // If rs2 is set, then the operation is performed.
+ operation_mask &= rs2_value;
+ }
+
+ auto result = op(operation_mask);
+ if (mask_value) {
+ dest_span[i] = result;
+ }
+ vector_index++;
+ }
+ // Submit the destination db .
+ dest_db->Submit();
+ item_index = 0;
+ }
+ rv_vector->clear_vstart();
+}
+
+// This helper function handles the case of unary vector
+// operations. It implements all the checking necessary for both widening and
+// narrowing operations.
+template <typename Vd, typename Vs2>
+void RiscVUnaryVectorOp(CheriotVectorState *rv_vector, const Instruction *inst,
+ std::function<Vd(Vs2)> op) {
+ if (rv_vector->vector_exception()) return;
+ int num_elements = rv_vector->vector_length();
+ int lmul = rv_vector->vector_length_multiplier();
+ int sew = rv_vector->selected_element_width();
+ int lmul_vd = lmul * sizeof(Vd) / sew;
+ int lmul_vs2 = lmul * sizeof(Vs2) / sew;
+ if (lmul_vd > 64 || lmul_vd == 0) {
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal lmul value vd (" << lmul_vd << ")";
+ return;
+ }
+ if (lmul_vs2 > 64 || lmul_vs2 == 0) {
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal lmul_value vs2 (" << lmul_vs2 << ")";
+ return;
+ }
+ int elements_per_vector =
+ rv_vector->vector_register_byte_length() / sizeof(Vd);
+ int max_regs = (num_elements + elements_per_vector - 1) / elements_per_vector;
+ auto *dest_op =
+ static_cast<RV32VectorDestinationOperand *>(inst->Destination(0));
+ // Verify that there are enough registers in the destination operand.
+ if (dest_op->size() < max_regs) {
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << absl::StrCat(
+ "Vector destination '", dest_op->AsString(), "' has fewer registers (",
+ dest_op->size(), ") than required by the operation (", max_regs, ")");
+ return;
+ }
+ // Get the vector mask.
+ auto *mask_op = static_cast<RV32VectorSourceOperand *>(inst->Source(1));
+ auto mask_span = mask_op->GetRegister(0)->data_buffer()->Get<uint8_t>();
+ // Get the vector start element index and compute where to start
+ // the operation.
+ int vector_index = rv_vector->vstart();
+ int start_reg = vector_index / elements_per_vector;
+ int item_index = vector_index % elements_per_vector;
+ // Iterate over the number of registers to write.
+ for (int reg = start_reg; (reg < max_regs) && (vector_index < num_elements);
+ reg++) {
+ // Allocate data buffer for the new register data.
+ auto *dest_db = dest_op->CopyDataBuffer(reg);
+ auto dest_span = dest_db->Get<Vd>();
+ // Write data into register subject to masking.
+ int element_count = std::min(elements_per_vector, num_elements);
+ for (int i = item_index;
+ (i < element_count) && (vector_index < num_elements); i++) {
+ // Get the mask value.
+ int mask_index = vector_index >> 3;
+ int mask_offset = vector_index & 0b111;
+ bool mask_value = ((mask_span[mask_index] >> mask_offset) & 0b1) != 0;
+ if (mask_value) {
+ // Compute result.
+ Vs2 vs2 = GetInstructionSource<Vs2>(inst, 0, vector_index);
+ dest_span[i] = op(vs2);
+ }
+ vector_index++;
+ }
+ // Submit the destination db .
+ dest_db->Submit();
+ item_index = 0;
+ }
+ rv_vector->clear_vstart();
+}
+
+// This helper function handles the case of unary vector operations that set
+// fflags. It implements all the checking necessary for both widening and
+// narrowing operations.
+template <typename Vd, typename Vs2>
+void RiscVUnaryVectorOpWithFflags(
+ CheriotVectorState *rv_vector, const Instruction *inst,
+ std::function<std::tuple<Vd, uint32_t>(Vs2)> op) {
+ if (rv_vector->vector_exception()) return;
+ int num_elements = rv_vector->vector_length();
+ int lmul = rv_vector->vector_length_multiplier();
+ int sew = rv_vector->selected_element_width();
+ int lmul_vd = lmul * sizeof(Vd) / sew;
+ int lmul_vs2 = lmul * sizeof(Vs2) / sew;
+ if (lmul_vd > 64 || lmul_vd == 0) {
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal lmul value vd (" << lmul_vd << ")";
+ return;
+ }
+ if (lmul_vs2 > 64 || lmul_vs2 == 0) {
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal lmul_value vs2 (" << lmul_vs2 << ")";
+ return;
+ }
+ int elements_per_vector =
+ rv_vector->vector_register_byte_length() / sizeof(Vd);
+ int max_regs = (num_elements + elements_per_vector - 1) / elements_per_vector;
+ auto *dest_op =
+ static_cast<RV32VectorDestinationOperand *>(inst->Destination(0));
+ // Verify that there are enough registers in the destination operand.
+ if (dest_op->size() < max_regs) {
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << absl::StrCat(
+ "Vector destination '", dest_op->AsString(), "' has fewer registers (",
+ dest_op->size(), ") than required by the operation (", max_regs, ")");
+ return;
+ }
+ // Get the vector mask.
+ auto *mask_op = static_cast<RV32VectorSourceOperand *>(inst->Source(1));
+ auto mask_span = mask_op->GetRegister(0)->data_buffer()->Get<uint8_t>();
+ // Get the vector start element index and compute where to start
+ // the operation.
+ int vector_index = rv_vector->vstart();
+ int start_reg = vector_index / elements_per_vector;
+ int item_index = vector_index % elements_per_vector;
+ // Iterate over the number of registers to write.
+ uint32_t fflags = 0;
+ for (int reg = start_reg; (reg < max_regs) && (vector_index < num_elements);
+ reg++) {
+ // Allocate data buffer for the new register data.
+ auto *dest_db = dest_op->CopyDataBuffer(reg);
+ auto dest_span = dest_db->Get<Vd>();
+ // Write data into register subject to masking.
+ int element_count = std::min(elements_per_vector, num_elements);
+ for (int i = item_index;
+ (i < element_count) && (vector_index < num_elements); i++) {
+ // Get the mask value.
+ int mask_index = vector_index >> 3;
+ int mask_offset = vector_index & 0b111;
+ bool mask_value = ((mask_span[mask_index] >> mask_offset) & 0b1) != 0;
+ if (mask_value) {
+ // Compute result.
+ Vs2 vs2 = GetInstructionSource<Vs2>(inst, 0, vector_index);
+ auto [value, flag] = op(vs2);
+ dest_span[i] = value;
+ fflags |= flag;
+ }
+ vector_index++;
+ }
+ // Submit the destination db .
+ dest_db->Submit();
+ item_index = 0;
+ }
+ auto *flag_db = inst->Destination(1)->AllocateDataBuffer();
+ flag_db->Set<uint32_t>(0, fflags);
+ flag_db->Submit();
+ rv_vector->clear_vstart();
+}
+
+// This helper function handles the case of mask + two source operand vector
+// operations. It implements all the checking necessary for both widening and
+// narrowing operations.
+template <typename Vd, typename Vs2, typename Vs1>
+void RiscVMaskBinaryVectorOp(
+ CheriotVectorState *rv_vector, const Instruction *inst,
+ std::function<std::optional<Vd>(Vs2, Vs1, bool)> op) {
+ if (rv_vector->vector_exception()) return;
+ int num_elements = rv_vector->vector_length();
+ int lmul = rv_vector->vector_length_multiplier();
+ int sew = rv_vector->selected_element_width();
+ int lmul_vd = lmul * sizeof(Vd) / sew;
+ int lmul_vs2 = lmul * sizeof(Vs2) / sew;
+ int lmul_vs1 = lmul * sizeof(Vs1) / sew;
+ if (lmul_vd > 64 || lmul_vs2 > 64 || lmul_vs1 > 64) {
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal lmul value";
+ return;
+ }
+ if (lmul_vd == 0 || lmul_vs2 == 0 || lmul_vs1 == 0) {
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal lmul_value";
+ return;
+ }
+ int elements_per_vector =
+ rv_vector->vector_register_byte_length() / sizeof(Vd);
+ int max_regs = (num_elements + elements_per_vector - 1) / elements_per_vector;
+ auto *dest_op =
+ static_cast<RV32VectorDestinationOperand *>(inst->Destination(0));
+ // Verify that there are enough registers in the destination operand.
+ if (dest_op->size() < max_regs) {
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << absl::StrCat(
+ "Vector destination '", dest_op->AsString(), "' has fewer registers (",
+ dest_op->size(), ") than required by the operation (", max_regs, ")");
+ return;
+ }
+ // Get the vector mask.
+ auto *mask_op = static_cast<RV32VectorSourceOperand *>(inst->Source(2));
+ auto mask_span = mask_op->GetRegister(0)->data_buffer()->Get<uint8_t>();
+ // Get the vector start element index and compute where to start
+ // the operation.
+ int vector_index = rv_vector->vstart();
+ int start_reg = vector_index / elements_per_vector;
+ int item_index = vector_index % elements_per_vector;
+ // Determine if it's vector-vector or vector-scalar.
+ bool vector_scalar = inst->Source(1)->shape()[0] == 1;
+ // Iterate over the number of registers to write.
+ bool exception = false;
+ for (int reg = start_reg;
+ !exception && (reg < max_regs) && (vector_index < num_elements); reg++) {
+ // Allocate data buffer for the new register data.
+ auto *dest_db = dest_op->CopyDataBuffer(reg);
+ auto dest_span = dest_db->Get<Vd>();
+ // Write data into register subject to masking.
+ int element_count = std::min(elements_per_vector, num_elements);
+ for (int i = item_index;
+ (i < element_count) && (vector_index < num_elements); i++) {
+ // Get the mask value.
+ int mask_index = vector_index >> 3;
+ int mask_offset = vector_index & 0b111;
+ bool mask_value = ((mask_span[mask_index] >> mask_offset) & 0b1) != 0;
+ // Compute result.
+ Vs2 vs2 = GetInstructionSource<Vs2>(inst, 0, vector_index);
+ Vs1 vs1 = GetInstructionSource<Vs1>(inst, 1,
+ (vector_scalar ? 0 : vector_index));
+ auto value = op(vs2, vs1, mask_value);
+ if (value.has_value()) {
+ dest_span[i] = value.value();
+ } else if (mask_value) {
+ // If there is no value returned, but the mask_value is true, check
+ // to see if there was an exception.
+ if (rv_vector->vector_exception()) {
+ rv_vector->set_vstart(vector_index);
+ exception = true;
+ break;
+ }
+ }
+ vector_index++;
+ }
+ // Submit the destination db .
+ dest_db->Submit();
+ item_index = 0;
+ }
+ rv_vector->clear_vstart();
+}
+
+// This helper function handles the case of two source operand vector
+// operations. It implements all the checking necessary for both widening and
+// narrowing operations.
+template <typename Vd, typename Vs2, typename Vs1>
+void RiscVBinaryVectorOp(CheriotVectorState *rv_vector, const Instruction *inst,
+ std::function<Vd(Vs2, Vs1)> op) {
+ RiscVMaskBinaryVectorOp<Vd, Vs2, Vs1>(
+ rv_vector, inst,
+ [op](Vs2 vs2, Vs1 vs1, bool mask_value) -> std::optional<Vd> {
+ if (mask_value) {
+ return op(vs2, vs1);
+ }
+ return std::nullopt;
+ });
+}
+
+template <typename Vd, typename Vs2, typename Vs1>
+void RiscVBinaryVectorOpWithFflags(
+ CheriotVectorState *rv_vector, const Instruction *inst,
+ std::function<std::tuple<Vd, uint32_t>(Vs2, Vs1)> op) {
+ if (rv_vector->vector_exception()) return;
+ int num_elements = rv_vector->vector_length();
+ int lmul = rv_vector->vector_length_multiplier();
+ int sew = rv_vector->selected_element_width();
+ int lmul_vd = lmul * sizeof(Vd) / sew;
+ int lmul_vs2 = lmul * sizeof(Vs2) / sew;
+ int lmul_vs1 = lmul * sizeof(Vs1) / sew;
+ if (lmul_vd > 64 || lmul_vs2 > 64 || lmul_vs1 > 64) {
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal lmul value";
+ return;
+ }
+ if (lmul_vd == 0 || lmul_vs2 == 0 || lmul_vs1 == 0) {
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal lmul_value";
+ return;
+ }
+ int elements_per_vector =
+ rv_vector->vector_register_byte_length() / sizeof(Vd);
+ int max_regs = (num_elements + elements_per_vector - 1) / elements_per_vector;
+ auto *dest_op =
+ static_cast<RV32VectorDestinationOperand *>(inst->Destination(0));
+ // Verify that there are enough registers in the destination operand.
+ if (dest_op->size() < max_regs) {
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << absl::StrCat(
+ "Vector destination '", dest_op->AsString(), "' has fewer registers (",
+ dest_op->size(), ") than required by the operation (", max_regs, ")");
+ return;
+ }
+ // Get the vector mask.
+ auto *mask_op = static_cast<RV32VectorSourceOperand *>(inst->Source(2));
+ auto mask_span = mask_op->GetRegister(0)->data_buffer()->Get<uint8_t>();
+ // Get the vector start element index and compute where to start
+ // the operation.
+ int vector_index = rv_vector->vstart();
+ int start_reg = vector_index / elements_per_vector;
+ int item_index = vector_index % elements_per_vector;
+ // Determine if it's vector-vector or vector-scalar.
+ bool vector_scalar = inst->Source(1)->shape()[0] == 1;
+ // Iterate over the number of registers to write.
+ bool exception = false;
+ uint32_t fflags = 0;
+ for (int reg = start_reg;
+ !exception && (reg < max_regs) && (vector_index < num_elements); reg++) {
+ // Allocate data buffer for the new register data.
+ auto *dest_db = dest_op->CopyDataBuffer(reg);
+ auto dest_span = dest_db->Get<Vd>();
+ // Write data into register subject to masking.
+ int element_count = std::min(elements_per_vector, num_elements);
+ for (int i = item_index;
+ (i < element_count) && (vector_index < num_elements); i++) {
+ // Get the mask value.
+ int mask_index = vector_index >> 3;
+ int mask_offset = vector_index & 0b111;
+ bool mask_value = ((mask_span[mask_index] >> mask_offset) & 0b1) != 0;
+ // Compute result.
+ Vs2 vs2 = GetInstructionSource<Vs2>(inst, 0, vector_index);
+ Vs1 vs1 = GetInstructionSource<Vs1>(inst, 1,
+ (vector_scalar ? 0 : vector_index));
+ if (mask_value) {
+ auto [value, flag] = op(vs2, vs1);
+ dest_span[i] = value;
+ fflags |= flag;
+ if (rv_vector->vector_exception()) {
+ rv_vector->set_vstart(vector_index);
+ exception = true;
+ break;
+ }
+ }
+ vector_index++;
+ }
+ // Submit the destination dbs.
+ dest_db->Submit();
+ item_index = 0;
+ }
+ auto *flag_db = inst->Destination(1)->AllocateDataBuffer();
+ flag_db->Set<uint32_t>(0, fflags);
+ flag_db->Submit();
+ rv_vector->clear_vstart();
+}
+
+// This helper function handles three source operand vector operations. It
+// implements all the checking necessary for both widening and narrowing
+// operations.
+template <typename Vd, typename Vs2, typename Vs1>
+void RiscVTernaryVectorOp(CheriotVectorState *rv_vector,
+ const Instruction *inst,
+ std::function<Vd(Vs2, Vs1, Vd)> op) {
+ if (rv_vector->vector_exception()) return;
+ int num_elements = rv_vector->vector_length();
+ int lmul = rv_vector->vector_length_multiplier();
+ int sew = rv_vector->selected_element_width();
+ int lmul_vd = lmul * sizeof(Vd) / sew;
+ int lmul_vs2 = lmul * sizeof(Vs2) / sew;
+ int lmul_vs1 = lmul * sizeof(Vs1) / sew;
+ if (lmul_vd > 64 || lmul_vs2 > 64 || lmul_vs1 > 64) {
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal lmul value";
+ return;
+ }
+ if (lmul_vd == 0 || lmul_vs2 == 0 || lmul_vs1 == 0) {
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal lmul_value";
+ return;
+ }
+ int elements_per_vector =
+ rv_vector->vector_register_byte_length() / sizeof(Vd);
+ int max_regs = (num_elements + elements_per_vector - 1) / elements_per_vector;
+ auto *dest_op =
+ static_cast<RV32VectorDestinationOperand *>(inst->Destination(0));
+ // Verify that there are enough registers in the destination operand.
+ if (dest_op->size() < max_regs) {
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << absl::StrCat(
+ "Vector destination '", dest_op->AsString(), "' has fewer registers (",
+ dest_op->size(), ") than required by the operation (", max_regs, ")");
+ return;
+ }
+ // Get the vector mask.
+ auto *mask_op = static_cast<RV32VectorSourceOperand *>(inst->Source(3));
+ auto mask_span = mask_op->GetRegister(0)->data_buffer()->Get<uint8_t>();
+ // Get the vector start element index and compute where to start
+ // the operation.
+ int vector_index = rv_vector->vstart();
+ int start_reg = vector_index / elements_per_vector;
+ int item_index = vector_index % elements_per_vector;
+ // Determine if it's vector-vector or vector-scalar.
+ bool vector_scalar = inst->Source(1)->shape()[0] == 1;
+ // Iterate over the number of registers to write.
+ for (int reg = start_reg; (reg < max_regs) && (vector_index < num_elements);
+ reg++) {
+ // Allocate data buffer for the new register data.
+ auto *dest_db = dest_op->CopyDataBuffer(reg);
+ auto dest_span = dest_db->Get<Vd>();
+ // Write data into register subject to masking.
+ int element_count = std::min(elements_per_vector, num_elements);
+ for (int i = item_index;
+ (i < element_count) && (vector_index < num_elements); i++) {
+ // Get the mask value.
+ int mask_index = vector_index >> 3;
+ int mask_offset = vector_index & 0b111;
+ bool mask_value = ((mask_span[mask_index] >> mask_offset) & 0b1) != 0;
+ // Compute result.
+ Vs2 vs2 = GetInstructionSource<Vs2>(inst, 0, vector_index);
+ Vs1 vs1 = GetInstructionSource<Vs1>(inst, 1,
+ (vector_scalar ? 0 : vector_index));
+ Vd vd = GetInstructionSource<Vd>(inst, 2, vector_index);
+ if (mask_value) {
+ dest_span[i] = op(vs2, vs1, vd);
+ }
+ vector_index++;
+ }
+ // Submit the destination db .
+ dest_db->Submit();
+ item_index = 0;
+ }
+ rv_vector->clear_vstart();
+}
+
+// The reduction instructions take Vs1[0], and all the elements (subject to
+// masking) from Vs2 and apply the reduction operation to produce a single
+// element that is written to Vd[0].
+template <typename Vd, typename Vs2, typename Vs1>
+void RiscVBinaryReductionVectorOp(CheriotVectorState *rv_vector,
+ const Instruction *inst,
+ std::function<Vd(Vd, Vs2)> op) {
+ if (rv_vector->vector_exception()) return;
+ if (rv_vector->vstart()) {
+ rv_vector->vector_exception();
+ return;
+ }
+ int sew = rv_vector->selected_element_width();
+ int lmul = rv_vector->vector_length_multiplier();
+ int lmul_vd = lmul * sizeof(Vd) / sew;
+ int lmul_vs2 = lmul * sizeof(Vs2) / sew;
+ int lmul_vs1 = lmul * sizeof(Vs1) / sew;
+ if (lmul_vd > 64 || lmul_vs2 > 64 || lmul_vs1 > 64) {
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal lmul value";
+ return;
+ }
+ if (lmul_vd == 0 || lmul_vs2 == 0 || lmul_vs1 == 0) {
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal lmul_value";
+ return;
+ }
+ int num_elements = rv_vector->vector_length();
+ // Get the vector mask.
+ auto *mask_op = static_cast<RV32VectorSourceOperand *>(inst->Source(2));
+ auto mask_span = mask_op->GetRegister(0)->data_buffer()->Get<uint8_t>();
+ Vd accumulator =
+ static_cast<Vd>(generic::GetInstructionSource<Vs1>(inst, 1, 0));
+ for (int i = 0; i < num_elements; i++) {
+ int mask_index = i >> 3;
+ int mask_offset = i & 0b111;
+ bool mask_value = (mask_span[mask_index] >> mask_offset) & 0b1;
+ if (mask_value) {
+ accumulator =
+ op(accumulator, generic::GetInstructionSource<Vs2>(inst, 0, i));
+ }
+ }
+ auto *dest_op =
+ static_cast<RV32VectorDestinationOperand *>(inst->Destination(0));
+ auto dest_db = dest_op->CopyDataBuffer();
+ dest_db->Set<Vd>(0, accumulator);
+ dest_db->Submit();
+ rv_vector->clear_vstart();
+}
+
+template <typename T>
+T GetRoundingBit(int rounding_mode, T rounding_bits, int size) {
+ switch (rounding_mode) {
+ case 0: // Round-to-nearest-up (add +0.5 lsb)
+ if (size < 2) return 0;
+ return (rounding_bits >> (size - 2)) & 0b1;
+ case 1: { // Round-to-nearest-event
+ T v_d_minus_1 = (size < 2) ? 0 : (rounding_bits >> (size - 2)) & 0b1;
+ T v_d = (size == 0) ? 0 : (rounding_bits >> (size - 1)) & 0b1;
+ T v_d_minus_2_0 = (size < 3)
+ ? 0
+ : (rounding_bits & ~(std::numeric_limits<T>::max()
+ << (size - 2))) != 0;
+ return v_d_minus_1 & (v_d_minus_2_0 | v_d);
+ }
+ case 2: // Round-down (truncate).
+ return 0;
+ case 3: { // Round-to-odd.
+ T v_d_minus_1_0 = (size < 2)
+ ? 0
+ : (rounding_bits & ~(std::numeric_limits<T>::max()
+ << (size - 1))) != 0;
+ T v_d = (rounding_bits >> (size - 1)) & 0b1;
+ return (!v_d) & v_d_minus_1_0;
+ }
+ default:
+ LOG(ERROR) << "GetRoundingBit: Invalid value for rounding mode";
+ break;
+ }
+ return 0;
+}
+
+template <typename T>
+T RoundOff(CheriotVectorState *rv_vector, T value, int size) {
+ auto rm = rv_vector->vxrm();
+ auto ret = (value >> size) + GetRoundingBit<T>(rm, value, size + 1);
+ return ret;
+}
+
+} // namespace cheriot
+} // namespace sim
+} // namespace mpact
+
+#endif // THIRD_PARTY_MPACT_RISCV_RISCV_RISCV_VECTOR_INSTRUCTION_HELPERS_H_
diff --git a/cheriot/riscv_cheriot_vector_memory_instructions.cc b/cheriot/riscv_cheriot_vector_memory_instructions.cc
new file mode 100644
index 0000000..66f3b41
--- /dev/null
+++ b/cheriot/riscv_cheriot_vector_memory_instructions.cc
@@ -0,0 +1,1518 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cheriot/riscv_cheriot_vector_memory_instructions.h"
+
+#include <algorithm>
+#include <any>
+#include <cstdint>
+
+#include "absl/log/log.h"
+#include "absl/status/status.h"
+#include "absl/strings/str_cat.h"
+#include "cheriot/cheriot_register.h"
+#include "cheriot/cheriot_state.h"
+#include "cheriot/cheriot_vector_state.h"
+#include "cheriot/riscv_cheriot_instruction_helpers.h"
+#include "mpact/sim/generic/instruction.h"
+#include "mpact/sim/generic/register.h"
+#include "riscv//riscv_register.h"
+#include "riscv//riscv_state.h"
+
+namespace mpact {
+namespace sim {
+namespace cheriot {
+
+using generic::GetInstructionSource;
+using ::mpact::sim::generic::RegisterBase;
+using ::mpact::sim::riscv::RV32VectorDestinationOperand;
+using ::mpact::sim::riscv::RV32VectorSourceOperand;
+using ::mpact::sim::riscv::VectorLoadContext;
+using CapReg = CheriotRegister;
+
+// Helper to get capability register source and destination registers.
+static inline CapReg *GetCapSource(const Instruction *instruction, int i) {
+ return static_cast<CapReg *>(
+ std::any_cast<RegisterBase *>(instruction->Source(i)->GetObject()));
+}
+
+static inline bool CheckCapForMemoryAccess(const Instruction *instruction,
+ CapReg *cap_reg,
+ CheriotState *state) {
+ // Check for tag unset.
+ if (!cap_reg->tag()) {
+ state->HandleCheriRegException(instruction, instruction->address(),
+ ExceptionCode::kCapExTagViolation, cap_reg);
+ return false;
+ }
+ // Check for sealed.
+ if (cap_reg->IsSealed()) {
+ state->HandleCheriRegException(instruction, instruction->address(),
+ ExceptionCode::kCapExSealViolation, cap_reg);
+ return false;
+ }
+ // Check for permissions.
+ if (!cap_reg->HasPermission(CheriotRegister::kPermitLoad)) {
+ state->HandleCheriRegException(instruction, instruction->address(),
+ ExceptionCode::kCapExPermitLoadViolation,
+ cap_reg);
+ return false;
+ }
+ return true;
+}
+
+static inline bool CheckCapBounds(const Instruction *instruction,
+ uint64_t address, int el_width,
+ CapReg *cap_reg, CheriotState *state) {
+ // Check for bounds.
+ if (!cap_reg->IsInBounds(address, el_width)) {
+ state->HandleCheriRegException(instruction, instruction->address(),
+ ExceptionCode::kCapExBoundsViolation,
+ cap_reg);
+ return false;
+ }
+ return true;
+}
+
+// Helper function used by the load child instructions (non segment loads) that
+// writes the loaded data into the registers.
+template <typename T>
+absl::Status WriteBackLoadData(int vector_register_byte_length,
+ const Instruction *inst) {
+ // Get values from context.
+ auto *context = static_cast<VectorLoadContext *>(inst->context());
+ auto masks = context->mask_db->Get<bool>();
+ auto values = context->value_db->Get<T>();
+ int vector_start = context->vstart;
+ int vector_length = context->vlength;
+
+ int element_size = sizeof(T);
+ int elements_per_vector = vector_register_byte_length / element_size;
+ int max_regs =
+ (vector_length + elements_per_vector - 1) / elements_per_vector;
+ // Verify that the dest_op has enough registers. Else signal error.
+ auto *dest_op =
+ static_cast<RV32VectorDestinationOperand *>(inst->Destination(0));
+ if (dest_op->size() < max_regs) {
+ // TODO: signal error.
+ return absl::InternalError("Not enough registers in destination operand");
+ }
+ // Compute the number of values to be written.
+ int value_count = masks.size();
+ if (vector_length - vector_start != value_count) {
+ // TODO: signal error.
+ return absl::InternalError(
+ absl::StrCat("The number of mask elements (", value_count,
+ ") differs from the number of elements to write (",
+ vector_length - vector_start, ")"));
+ }
+ int load_data_index = 0;
+ int start_reg = vector_start / elements_per_vector;
+ int item_index = vector_start % elements_per_vector;
+ // Iterate over the number of registers to write.
+ for (int reg = start_reg; (reg < max_regs) && (value_count > 0); reg++) {
+ // Allocate data buffer for the new register data.
+ auto *dest_db = dest_op->CopyDataBuffer(reg);
+ auto dest_span = dest_db->Get<T>();
+ // Write data into register subject to masking.
+ int count = std::min(elements_per_vector - item_index, value_count);
+ for (int i = item_index; i < count; i++) {
+ if (masks[load_data_index + i]) {
+ dest_span[i] = values[load_data_index + i];
+ }
+ }
+ value_count -= count;
+ load_data_index += count;
+ dest_db->Submit(0);
+ item_index = 0;
+ }
+ return absl::OkStatus();
+}
+
+// Helper function used by the load child instructions (for segment loads) that
+// writes the loaded data into the registers.
+template <typename T>
+absl::Status WriteBackSegmentLoadData(int vector_register_byte_length,
+ const Instruction *inst) {
+ // The number of fields in each segment.
+ int num_fields = GetInstructionSource<uint32_t>(inst, 0) + 1;
+ // Get values from context.
+ auto *context = static_cast<VectorLoadContext *>(inst->context());
+ auto masks = context->mask_db->Get<bool>();
+ auto values = context->value_db->Get<T>();
+ int start_segment = context->vstart;
+ int vector_length = context->vlength;
+
+ int element_size = sizeof(T);
+ int num_segments = masks.size() / num_fields;
+ // Number of registers written for each field.
+ int max_elements_per_vector =
+ std::min(vector_register_byte_length / element_size, num_segments);
+ int num_regs =
+ std::max(1, num_segments * element_size / vector_register_byte_length);
+ // Total number of registers written.
+ int total_regs = num_fields * num_regs;
+ // Verify that the dest_op has enough registers. Else signal error.
+ auto *dest_op =
+ static_cast<RV32VectorDestinationOperand *>(inst->Destination(0));
+ if (dest_op->size() < total_regs) {
+ return absl::InternalError("Not enough registers in destination operand");
+ }
+ // Compute the number of segments to be written.
+ if (vector_length - start_segment != num_segments) {
+ return absl::InternalError(
+ absl::StrCat("The number of mask elements (", num_segments,
+ ") differs from the number of elements to write (",
+ vector_length - start_segment, ")"));
+ }
+ int load_data_index = 0;
+ // Data is organized by field. So write back in that order.
+ for (int field = 0; field < num_fields; field++) {
+ int start_reg =
+ field * num_regs + (start_segment / max_elements_per_vector);
+ int offset = start_segment % max_elements_per_vector;
+ int remaining_data = num_segments;
+ for (int reg = start_reg; reg < start_reg + num_regs; reg++) {
+ auto *dest_db = dest_op->CopyDataBuffer(reg);
+ auto span = dest_db->Get<T>();
+ int max_entry =
+ std::min(remaining_data + offset, max_elements_per_vector);
+ for (int i = offset; i < max_entry; i++) {
+ if (masks[load_data_index]) {
+ span[i] = values[load_data_index];
+ }
+ load_data_index++;
+ remaining_data--;
+ }
+ offset = 0;
+ dest_db->Submit(0);
+ }
+ }
+ return absl::OkStatus();
+}
+
+// This models the vsetvl set of instructions. The immediate versus register
+// versions are all modeled by the same function. Flags are bound during decode
+// to the two first parameters to specify if rd or rs1 are x0.
+void Vsetvl(bool rd_zero, bool rs1_zero, const Instruction *inst) {
+ auto *rv_state = static_cast<CheriotState *>(inst->state());
+ auto *rv_vector = rv_state->rv_vector();
+ uint32_t vtype = GetInstructionSource<uint32_t>(inst, 1) & 0b1'1'111'111;
+ // Get previous vtype.
+ uint32_t prev_vtype = rv_vector->vtype();
+ // Get previous max length.
+ int old_max_length = rv_vector->max_vector_length();
+ // Set the new vector type.
+ rv_vector->SetVectorType(vtype);
+ auto new_max_length = rv_vector->max_vector_length();
+ uint32_t vl = new_max_length;
+ if (rs1_zero && rd_zero) { // If rs1 and rd are both zero.
+ // If max_length changed, then there's an error, otherwise, vector length
+ // is now vl.
+ if (old_max_length != new_max_length) {
+ // ERROR: cannot change max_vector_length.
+ // Revert, then set error flag.
+ rv_vector->SetVectorType(prev_vtype);
+ rv_vector->set_vector_exception();
+ return;
+ }
+ rv_vector->set_vector_length(new_max_length);
+ return;
+ }
+ if (!rs1_zero) { // There is a requested vector length.
+ uint32_t avl = GetInstructionSource<uint32_t>(inst, 0);
+ // Unless the requested vl is less than 2 * max, set it to max.
+ if (avl <= new_max_length) {
+ // If the requested vl is less than max use it.
+ vl = avl;
+ }
+
+ // The RISCV spec has the following constraint when VLMAX < AVL < 2 * VLMAX:
+ // ceil(AVL / 2) <= vl <= VLMAX
+ //
+ // This allows vl to be assigned to half of the requested AVL value, however
+ // vl may be assigned to VLMAX instead. SiFive implementations of the RISCV
+ // vector engine set vl to VLMAX in this case, which is the same approach
+ // followed here.
+ }
+ rv_vector->set_vector_length(vl);
+ if (!rd_zero) { // Update register if there is a writable destination.
+ WriteCapIntResult<uint32_t>(inst, 0, vl);
+ }
+}
+
+// Vector load - models both strided and unit stride. Strides can be positive,
+// zero, or negative.
+
+// Source(0): base address.
+// Source(1): vector mask register, vector constant {1..} if not masked.
+// Destination(0): vector destination register.
+void VlUnitStrided(int element_width, const Instruction *inst) {
+ auto *state = static_cast<CheriotState *>(inst->state());
+ auto *rv_vector = state->rv_vector();
+ int start = rv_vector->vstart();
+ auto cap_reg = GetCapSource(inst, 0);
+ if (!CheckCapForMemoryAccess(inst, cap_reg, state)) return;
+ uint64_t base = cap_reg->address();
+ int emul = element_width * rv_vector->vector_length_multiplier() /
+ rv_vector->selected_element_width();
+ if ((emul > 64) || (emul == 0)) {
+ // TODO: signal vector error.
+ LOG(WARNING) << "EMUL (" << emul << ") out of range";
+ return;
+ }
+
+ // Compute total number of elements to be loaded.
+ int num_elements = rv_vector->vector_length();
+ int num_elements_loaded = num_elements - start;
+
+ // Allocate address data buffer.
+ auto *db_factory = inst->state()->db_factory();
+ auto *address_db = db_factory->Allocate<uint64_t>(num_elements_loaded);
+
+ // Allocate the value data buffer that the loaded data is returned in.
+ auto *value_db = db_factory->Allocate(num_elements_loaded * element_width);
+
+ // Get the source mask (stored in a single vector register).
+ auto *src_mask_op = static_cast<RV32VectorSourceOperand *>(inst->Source(1));
+ auto src_masks = src_mask_op->GetRegister(0)->data_buffer()->Get<uint8_t>();
+
+ // Allocate a byte mask data buffer for the load.
+ auto *mask_db = db_factory->Allocate<bool>(num_elements_loaded);
+
+ // Get the spans for addresses and masks.
+ auto addresses = address_db->Get<uint64_t>();
+ auto masks = mask_db->Get<bool>();
+
+ // The vector mask in the vector register is a bit mask. The mask used in
+ // the LoadMemory call is a bool mask so convert the bit masks to bool masks
+ // and compute the element addresses.
+ for (int i = start; i < num_elements; i++) {
+ int index = i >> 3;
+ int offset = i & 0b111;
+ addresses[i - start] = base + i * element_width;
+ masks[i - start] = ((src_masks[index] >> offset) & 0b1) != 0;
+ if (masks[i - start]) {
+ if (!CheckCapBounds(inst, addresses[i - start], element_width, cap_reg,
+ state)) {
+ address_db->DecRef();
+ mask_db->DecRef();
+ value_db->DecRef();
+ return;
+ }
+ }
+ }
+
+ // Set up the context, and submit the load.
+ auto *context = new VectorLoadContext(value_db, mask_db, element_width, start,
+ rv_vector->vector_length());
+ value_db->set_latency(0);
+ state->LoadMemory(inst, address_db, mask_db, element_width, value_db,
+ inst->child(), context);
+ // Release the context and address_db. The others will be released elsewhere.
+ context->DecRef();
+ address_db->DecRef();
+ rv_vector->clear_vstart();
+}
+
+// Source(0): base address.
+// Source(1): stride size bytes.
+// Source(2): vector mask register, vector constant {1..} if not masked.
+// Destination(0): vector destination register.
+void VlStrided(int element_width, const Instruction *inst) {
+ auto *state = static_cast<CheriotState *>(inst->state());
+ auto *rv_vector = state->rv_vector();
+ int start = rv_vector->vstart();
+ auto cap_reg = GetCapSource(inst, 0);
+ if (!CheckCapForMemoryAccess(inst, cap_reg, state)) return;
+ uint64_t base = cap_reg->address();
+ int64_t stride = GetInstructionSource<int64_t>(inst, 1);
+ int emul = element_width * rv_vector->vector_length_multiplier() /
+ rv_vector->selected_element_width();
+ if ((emul > 64) || (emul == 0)) {
+ // TODO: signal vector error.
+ LOG(WARNING) << "EMUL (" << emul << ") out of range";
+ return;
+ }
+
+ // Compute total number of elements to be loaded.
+ int num_elements = rv_vector->vector_length();
+ int num_elements_loaded = num_elements - start;
+
+ // Allocate address data buffer.
+ auto *db_factory = inst->state()->db_factory();
+ auto *address_db = db_factory->Allocate<uint64_t>(num_elements_loaded);
+
+ // Allocate the value data buffer that the loaded data is returned in.
+ auto *value_db = db_factory->Allocate(num_elements_loaded * element_width);
+
+ // Get the source mask (stored in a single vector register).
+ auto *src_mask_op = static_cast<RV32VectorSourceOperand *>(inst->Source(2));
+ auto src_masks = src_mask_op->GetRegister(0)->data_buffer()->Get<uint8_t>();
+
+ // Allocate a byte mask data buffer for the load.
+ auto *mask_db = db_factory->Allocate<bool>(num_elements_loaded);
+
+ // Get the spans for addresses and masks.
+ auto addresses = address_db->Get<uint64_t>();
+ auto masks = mask_db->Get<bool>();
+
+ // The vector mask in the vector register is a bit mask. The mask used in
+ // the LoadMemory call is a bool mask so convert the bit masks to bool masks
+ // and compute the element addresses.
+ for (int i = start; i < num_elements; i++) {
+ int index = i >> 3;
+ int offset = i & 0b111;
+ addresses[i - start] = base + i * stride;
+ masks[i - start] = ((src_masks[index] >> offset) & 0b1) != 0;
+ if (masks[i - start]) {
+ if (!CheckCapBounds(inst, addresses[i - start], element_width, cap_reg,
+ state)) {
+ address_db->DecRef();
+ mask_db->DecRef();
+ value_db->DecRef();
+ return;
+ }
+ }
+ }
+
+ // Set up the context, and submit the load.
+ auto *context = new VectorLoadContext(value_db, mask_db, element_width, start,
+ rv_vector->vector_length());
+ value_db->set_latency(0);
+ state->LoadMemory(inst, address_db, mask_db, element_width, value_db,
+ inst->child(), context);
+ // Release the context and address_db. The others will be released elsewhere.
+ context->DecRef();
+ address_db->DecRef();
+ rv_vector->clear_vstart();
+}
+
+// Vector load vector-mask. This is simple, just a single register.
+
+// Source(0): base address.
+// Destination(0): vector destination register (for the child instruction).
+void Vlm(const Instruction *inst) {
+ auto *state = static_cast<CheriotState *>(inst->state());
+ auto *rv_vector = state->rv_vector();
+ int start = rv_vector->vstart();
+ auto cap_reg = GetCapSource(inst, 0);
+ if (!CheckCapForMemoryAccess(inst, cap_reg, state)) return;
+ uint64_t base = cap_reg->address();
+ // Compute the number of bytes to be loaded.
+ int num_bytes = rv_vector->vector_register_byte_length() - start;
+ // Allocate address data buffer.
+ auto *db_factory = inst->state()->db_factory();
+ auto *address_db = db_factory->Allocate<uint64_t>(num_bytes);
+ // Allocate the value data buffer that the loaded data is returned in.
+ auto *value_db = db_factory->Allocate<uint8_t>(num_bytes);
+ // Allocate a byte mask data buffer.
+ auto *mask_db = db_factory->Allocate<bool>(num_bytes);
+ // Get the spans for addresses and masks.
+ auto masks = mask_db->Get<bool>();
+ auto addresses = address_db->Get<uint64_t>();
+ // Set up addresses, mark all masks elements as true.
+ for (int i = start; i < num_bytes; i++) {
+ addresses[i - start] = base + i;
+ masks[i - start] = true;
+ if (!CheckCapBounds(inst, addresses[i - start], 1, cap_reg, state)) {
+ address_db->DecRef();
+ mask_db->DecRef();
+ value_db->DecRef();
+ return;
+ }
+ }
+ // Set up the context, and submit the load.
+ auto *context =
+ new VectorLoadContext(value_db, mask_db, sizeof(uint8_t), start,
+ rv_vector->vector_register_byte_length());
+ auto *rv32_state = static_cast<CheriotState *>(inst->state());
+ value_db->set_latency(0);
+ rv32_state->LoadMemory(inst, address_db, mask_db, sizeof(uint8_t), value_db,
+ inst->child(), context);
+ // Release the context and address db.
+ address_db->DecRef();
+ context->DecRef();
+ rv_vector->clear_vstart();
+}
+
+// Vector load indexed (ordered and unordered). Index values are not scaled by
+// element size, as the index values can also be treated as multiple base
+// addresses with the base address acting as a common offset. Index values are
+// treated as unsigned integers, and are zero extended from the element size to
+// the internal address size (or truncated in case the internal XLEN is < index
+// element size).
+
+// Source(0) base address.
+// Source(1) index vector.
+// Source(2) masks.
+// Destination(0): vector destination register (for the child instruction).
+void VlIndexed(int index_width, const Instruction *inst) {
+ auto *state = static_cast<CheriotState *>(inst->state());
+ auto *rv_vector = state->rv_vector();
+ int start = rv_vector->vstart();
+ auto cap_reg = GetCapSource(inst, 0);
+ if (!CheckCapForMemoryAccess(inst, cap_reg, state)) return;
+ uint64_t base = cap_reg->address();
+ int element_width = rv_vector->selected_element_width();
+ int lmul = rv_vector->vector_length_multiplier();
+ auto *index_op = static_cast<RV32VectorSourceOperand *>(inst->Source(1));
+ int index_emul = index_width * lmul / element_width;
+ // Validate that emul has a legal value.
+ if ((index_emul > 64) || (index_emul == 0)) {
+ // TODO: signal vector error.
+ LOG(WARNING) << absl::StrCat(
+ "Vector load indexed: emul (index) out of range: ", index_emul);
+ rv_vector->set_vector_exception();
+ return;
+ }
+
+ // Compute the number of bytes and elements to be loaded.
+ int num_elements = rv_vector->vector_length();
+ int num_elements_loaded = num_elements - start;
+ int num_bytes_loaded = num_elements_loaded * element_width;
+
+ // Allocate address data buffer.
+ auto *db_factory = inst->state()->db_factory();
+ auto *address_db = db_factory->Allocate<uint64_t>(num_elements_loaded);
+ auto addresses = address_db->Get<uint64_t>();
+
+ // Allocate the value data buffer that the loaded data is returned in.
+ auto *value_db = db_factory->Allocate(num_bytes_loaded);
+
+ // Get the source mask (stored in a single vector register).
+ auto *src_mask_op = static_cast<RV32VectorSourceOperand *>(inst->Source(2));
+ auto src_masks = src_mask_op->GetRegister(0)->data_buffer()->Get<uint8_t>();
+
+ // Allocate a byte mask data buffer for the load.
+ auto *mask_db = db_factory->Allocate<bool>(num_elements);
+ auto masks = mask_db->Get<bool>();
+
+ // Convert the bit masks to byte masks and compute the element addresses.
+ // The index elements are treated as unsigned values.
+ for (int i = start; i < num_elements; i++) {
+ int mask_index = i >> 3;
+ int mask_offset = i & 0b111;
+ uint64_t offset;
+ switch (index_width) {
+ case 1:
+ offset = index_op->AsUint8(i);
+ break;
+ case 2:
+ offset = index_op->AsUint16(i);
+ break;
+ case 4:
+ offset = index_op->AsUint32(i);
+ break;
+ case 8:
+ offset = index_op->AsUint64(i);
+ break;
+ default:
+ offset = 0;
+ LOG(ERROR) << absl::StrCat("Illegal index width (", index_width, ")");
+ rv_vector->set_vector_exception();
+ break;
+ }
+ addresses[i - start] = base + offset;
+ masks[i - start] = ((src_masks[mask_index] >> mask_offset) & 0b1) != 0;
+ if (masks[i - start]) {
+ if (!CheckCapBounds(inst, addresses[i - start], element_width, cap_reg,
+ state)) {
+ address_db->DecRef();
+ mask_db->DecRef();
+ value_db->DecRef();
+ return;
+ }
+ }
+ }
+
+ // Set up context and submit load.
+ auto *context = new VectorLoadContext(value_db, mask_db, element_width, start,
+ rv_vector->vector_length());
+ value_db->set_latency(0);
+ state->LoadMemory(inst, address_db, mask_db, element_width, value_db,
+ inst->child(), context);
+ // Release the context and address db.
+ address_db->DecRef();
+ context->DecRef();
+ rv_vector->clear_vstart();
+}
+
+// Vector load whole register(s). The number of registers is passed as
+// a parameter to this function - bound to the called function object by the
+// instruction decoder. Simple function, no masks, no diffrentiation between
+// element sizes.
+// Source(0): base address.
+// Destination(0): vector destination register (for the child instruction).
+void VlRegister(int num_regs, int element_width_bytes,
+ const Instruction *inst) {
+ auto *state = static_cast<CheriotState *>(inst->state());
+ auto *rv_vector = state->rv_vector();
+ auto cap_reg = GetCapSource(inst, 0);
+ if (!CheckCapForMemoryAccess(inst, cap_reg, state)) return;
+ uint64_t base = cap_reg->address();
+ int num_elements =
+ rv_vector->vector_register_byte_length() * num_regs / element_width_bytes;
+ // Allocate data buffers.
+ auto *db_factory = inst->state()->db_factory();
+ auto *data_db = db_factory->Allocate(num_elements * element_width_bytes);
+ auto *address_db = db_factory->Allocate<uint64_t>(num_elements);
+ auto *mask_db = db_factory->Allocate<bool>(num_elements);
+ // Get spans for addresses and masks.
+ auto addresses = address_db->Get<uint64_t>();
+ auto masks = mask_db->Get<bool>();
+
+ // Compute addresses and set masks to true.
+ // Note that the width of each load operation is `element_width_bytes`, not
+ // SEW (selected element width).
+ // The SEW is the width of vector element of the vector register, and the
+ // element width here is the width of the data being loaded, it may differ
+ // from SEW.
+ for (int i = 0; i < num_elements; i++) {
+ addresses[i] = base + i * element_width_bytes;
+ masks[i] = true;
+ if (!CheckCapBounds(inst, addresses[i], element_width_bytes, cap_reg,
+ state)) {
+ address_db->DecRef();
+ mask_db->DecRef();
+ data_db->DecRef();
+ return;
+ }
+ }
+
+ // Set up context and submit load.
+ auto *context = new VectorLoadContext(data_db, mask_db, element_width_bytes,
+ 0, num_elements);
+ data_db->set_latency(0);
+ state->LoadMemory(inst, address_db, mask_db, element_width_bytes, data_db,
+ inst->child(), context);
+ // Release the context and address db.
+ address_db->DecRef();
+ context->DecRef();
+ rv_vector->clear_vstart();
+}
+
+// Vector load segment, unit stride. The stride is the size of each segment,
+// i.e., number of fields * element size. The first field of each segment is
+// loaded into the first register, the second into the second, etc. If there
+// are more segments than elements in the vector register, adjacent vector
+// registers are grouped together. So the first field goes in the first register
+// group, etc.
+// Source(0): base address
+// Source(1): mask
+// Source(2): number of fields - 1
+// Destination(0): vector destination register (for the child instruction).
+void VlSegment(int element_width, const Instruction *inst) {
+ auto *state = static_cast<CheriotState *>(inst->state());
+ auto *rv_vector = state->rv_vector();
+ int start = rv_vector->vstart();
+ auto cap_reg = GetCapSource(inst, 0);
+ if (!CheckCapForMemoryAccess(inst, cap_reg, state)) return;
+ uint64_t base = cap_reg->address();
+ auto src_mask_op = static_cast<RV32VectorSourceOperand *>(inst->Source(1));
+ auto src_masks = src_mask_op->GetRegister(0)->data_buffer()->Get<uint8_t>();
+ int num_fields = GetInstructionSource<int32_t>(inst, 2) + 1;
+ // Effective vector length multiplier.
+ int emul = (element_width * rv_vector->vector_length_multiplier()) /
+ rv_vector->selected_element_width();
+ if (emul * num_fields > 64) {
+ // This is a reserved encoding error.
+ // If > 64, it means that the number of registers required is > 8.
+ // TODO: signal error.
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int num_segments = rv_vector->vector_length();
+ int segment_stride = num_fields * element_width;
+ int num_elements = num_fields * num_segments;
+ // Set up data buffers.
+ auto *db_factory = inst->state()->db_factory();
+ auto *data_db = db_factory->Allocate(num_elements * element_width);
+ auto *address_db = db_factory->Allocate<uint64_t>(num_elements);
+ auto *mask_db = db_factory->Allocate<bool>(num_elements);
+ // Get spans for addresses and masks.
+ auto addresses = address_db->Get<uint64_t>();
+ auto masks = mask_db->Get<bool>();
+
+ for (int i = start; i < num_segments; i++) {
+ int mask_index = i >> 3;
+ int mask_offset = i & 0b111;
+ bool mask_value = ((src_masks[mask_index] >> mask_offset) & 0x1) != 0;
+ for (int field = 0; field < num_fields; field++) {
+ masks[field * num_segments + i] = mask_value;
+ addresses[field * num_segments + i] =
+ base + i * segment_stride + field * element_width;
+ if (masks[field * num_segments + i]) {
+ if (!CheckCapBounds(inst, addresses[field * num_segments + i],
+ element_width, cap_reg, state)) {
+ address_db->DecRef();
+ mask_db->DecRef();
+ data_db->DecRef();
+ return;
+ }
+ }
+ }
+ }
+ auto *context = new VectorLoadContext(data_db, mask_db, element_width, start,
+ num_segments);
+ data_db->set_latency(0);
+ state->LoadMemory(inst, address_db, mask_db, element_width, data_db,
+ inst->child(), context);
+ // Release the context and address db.
+ address_db->DecRef();
+ context->DecRef();
+ rv_vector->clear_vstart();
+}
+
+// Vector load strided adds a byte address stride to the base address for each
+// segment. Note, the stride offset is not scaled by the segment size.
+// Source(0): base address
+// Source(1): stride
+// Source(2): mask
+// Source(3): number of fields - 1
+// Destination(0): vector destination register (for the child instruction).
+void VlSegmentStrided(int element_width, const Instruction *inst) {
+ auto *state = static_cast<CheriotState *>(inst->state());
+ auto *rv_vector = state->rv_vector();
+ int start = rv_vector->vstart();
+ auto cap_reg = GetCapSource(inst, 0);
+ if (!CheckCapForMemoryAccess(inst, cap_reg, state)) return;
+ uint64_t base = cap_reg->address();
+ int64_t segment_stride = GetInstructionSource<int64_t>(inst, 1);
+ auto src_mask_op = static_cast<RV32VectorSourceOperand *>(inst->Source(2));
+ auto src_masks = src_mask_op->GetRegister(0)->data_buffer()->Get<uint8_t>();
+ int num_fields = GetInstructionSource<int32_t>(inst, 3) + 1;
+ // Effective vector length multiplier.
+ int emul = (element_width * rv_vector->vector_length_multiplier()) /
+ rv_vector->selected_element_width();
+ if (emul * num_fields > 64) {
+ // This is a reserved encoding error.
+ // If > 64, it means that the number of registers required is > 8.
+ // TODO: signal error.
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int num_segments = rv_vector->vector_length();
+ int num_elements = num_fields * num_segments;
+ // Set up data buffers.
+ auto *db_factory = inst->state()->db_factory();
+ auto *data_db = db_factory->Allocate(num_elements * element_width);
+ auto *address_db = db_factory->Allocate<uint64_t>(num_elements);
+ auto *mask_db = db_factory->Allocate<bool>(num_elements);
+ // Get the spans for addresses and masks.
+ auto addresses = address_db->Get<uint64_t>();
+ auto masks = mask_db->Get<bool>();
+ for (int i = start; i < num_segments; i++) {
+ int mask_index = i >> 3;
+ int mask_offset = i & 0b111;
+ bool mask_value = ((src_masks[mask_index] >> mask_offset) & 0x1) != 0;
+ for (int field = 0; field < num_fields; field++) {
+ masks[field * num_segments + i] = mask_value;
+ addresses[field * num_segments + i] =
+ base + i * segment_stride + field * element_width;
+ if (masks[field * num_segments + i]) {
+ if (!CheckCapBounds(inst, addresses[field * num_segments + i],
+ element_width, cap_reg, state)) {
+ address_db->DecRef();
+ mask_db->DecRef();
+ data_db->DecRef();
+ return;
+ }
+ }
+ }
+ }
+ // Allocate the context and submit the load.
+ auto *context = new VectorLoadContext(data_db, mask_db, element_width, start,
+ num_segments);
+ data_db->set_latency(0);
+ state->LoadMemory(inst, address_db, mask_db, element_width, data_db,
+ inst->child(), context);
+ // Release the context and address db.
+ address_db->DecRef();
+ context->DecRef();
+ rv_vector->clear_vstart();
+}
+
+// Vector load segment, indexed. Similar to the other segment loads, except
+// that the offset to the base address comes from a vector of indices. Each
+// offset is a byte address, and is not scaled by the segment size.
+// Source(0): base address
+// Source(1): index vector
+// Source(2): mask
+// Source(3): number of fields - 1
+// Destination(0): vector destination register (for the child instruction).
+void VlSegmentIndexed(int index_width, const Instruction *inst) {
+ auto *state = static_cast<CheriotState *>(inst->state());
+ auto *rv_vector = state->rv_vector();
+ int start = rv_vector->vstart();
+ auto cap_reg = GetCapSource(inst, 0);
+ if (!CheckCapForMemoryAccess(inst, cap_reg, state)) return;
+ uint64_t base = cap_reg->address();
+ auto *index_op = static_cast<RV32VectorSourceOperand *>(inst->Source(1));
+ auto src_mask_op = static_cast<RV32VectorSourceOperand *>(inst->Source(2));
+ auto src_masks = src_mask_op->GetRegister(0)->data_buffer()->Get<uint8_t>();
+ int num_fields = GetInstructionSource<int32_t>(inst, 3) + 1;
+ int element_width = rv_vector->selected_element_width();
+ // Effective vector length multiplier.
+ int lmul8 = rv_vector->vector_length_multiplier();
+ // Validate lmul.
+ if (lmul8 * num_fields > 64) {
+ LOG(WARNING) << "Vector segment load indexed: too many registers";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ // Index lmul is scaled from the lmul by the relative size of the index
+ // element to the SEW (selected element width).
+ int index_emul = (element_width * lmul8) / element_width;
+ // Validate that index_emul has a legal value.
+ if ((index_emul > 64) || (index_emul == 0)) {
+ // TODO: signal vector error.
+ LOG(WARNING) << absl::StrCat(
+ "Vector load indexed: emul (index) out of range: ", index_emul);
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int num_segments = rv_vector->vector_length();
+ int num_elements = num_fields * num_segments;
+
+ // Set up data buffers.
+ auto *db_factory = inst->state()->db_factory();
+ auto *data_db = db_factory->Allocate(num_elements * element_width);
+ auto *address_db = db_factory->Allocate<uint64_t>(num_elements);
+ auto *mask_db = db_factory->Allocate<bool>(num_elements);
+ // Get the spans for the addresses and masks.
+ auto addresses = address_db->Get<uint64_t>();
+ auto masks = mask_db->Get<bool>();
+
+ for (int i = start; i < num_segments; i++) {
+ // The mask value is per segment.
+ int mask_index = i >> 3;
+ int mask_offset = i & 0b111;
+ bool mask_value = ((src_masks[mask_index] >> mask_offset) & 0x1) != 0;
+ // Read the index value.
+ uint64_t offset;
+ switch (index_width) {
+ case 1:
+ offset = index_op->AsUint8(i);
+ break;
+ case 2:
+ offset = index_op->AsUint16(i);
+ break;
+ case 4:
+ offset = index_op->AsUint32(i);
+ break;
+ case 8:
+ offset = index_op->AsUint64(i);
+ break;
+ default:
+ offset = 0;
+ // TODO: signal error.
+ LOG(ERROR) << "Internal error - illegal value for index_width";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ for (int field = 0; field < num_fields; field++) {
+ masks[field * num_segments + i] = mask_value;
+ addresses[field * num_segments + i] = base + offset + field;
+ if (masks[field * num_segments + i]) {
+ if (!CheckCapBounds(inst, addresses[field * num_segments + i],
+ element_width, cap_reg, state)) {
+ address_db->DecRef();
+ mask_db->DecRef();
+ data_db->DecRef();
+ return;
+ }
+ }
+ }
+ }
+ auto *context = new VectorLoadContext(data_db, mask_db, element_width, start,
+ num_segments);
+ data_db->set_latency(0);
+ state->LoadMemory(inst, address_db, mask_db, element_width, data_db,
+ inst->child(), context);
+ // Release the context and address db.
+ address_db->DecRef();
+ context->DecRef();
+ rv_vector->clear_vstart();
+}
+
+// Child instruction used for non-segment vector loads. This function really
+// only is used to select a type specific version of the helper function to
+// write back the load data.
+void VlChild(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ absl::Status status;
+ int byte_length = rv_vector->vector_register_byte_length();
+ switch (static_cast<VectorLoadContext *>(inst->context())->element_width) {
+ case 1:
+ status = WriteBackLoadData<uint8_t>(byte_length, inst);
+ break;
+ case 2:
+ status = WriteBackLoadData<uint16_t>(byte_length, inst);
+ break;
+ case 4:
+ status = WriteBackLoadData<uint32_t>(byte_length, inst);
+ break;
+ case 8:
+ status = WriteBackLoadData<uint64_t>(byte_length, inst);
+ break;
+ default:
+ LOG(ERROR) << "Illegal element width";
+ return;
+ }
+ if (!status.ok()) {
+ LOG(WARNING) << status.message();
+ rv_vector->set_vector_exception();
+ }
+}
+
+// Child instruction used for segmen vector loads. This function really only is
+// used to select a type specific version of the helper function to write back
+// the load data.
+void VlSegmentChild(int element_width, const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ absl::Status status;
+ int byte_length = rv_vector->vector_register_byte_length();
+ switch (static_cast<VectorLoadContext *>(inst->context())->element_width) {
+ case 1:
+ status = WriteBackSegmentLoadData<uint8_t>(byte_length, inst);
+ break;
+ case 2:
+ status = WriteBackSegmentLoadData<uint16_t>(byte_length, inst);
+ break;
+ case 4:
+ status = WriteBackSegmentLoadData<uint32_t>(byte_length, inst);
+ break;
+ case 8:
+ status = WriteBackSegmentLoadData<uint64_t>(byte_length, inst);
+ break;
+ default:
+ LOG(ERROR) << "Illegal element width";
+ return;
+ }
+ if (!status.ok()) {
+ LOG(WARNING) << status.message();
+ rv_vector->set_vector_exception();
+ }
+}
+
+// Templated helper function for vector stores.
+template <typename T>
+void StoreVectorStrided(int vector_length, int vstart, int emul,
+ const Instruction *inst) {
+ auto *state = static_cast<CheriotState *>(inst->state());
+ auto cap_reg = GetCapSource(inst, 1);
+ if (!CheckCapForMemoryAccess(inst, cap_reg, state)) return;
+ uint64_t base = cap_reg->address();
+ int64_t stride = GetInstructionSource<int64_t>(inst, 2);
+ auto *src_mask_op = static_cast<RV32VectorSourceOperand *>(inst->Source(3));
+ auto src_masks = src_mask_op->GetRegister(0)->data_buffer()->Get<uint8_t>();
+
+ // Compute total number of elements to be stored.
+ int num_elements = vector_length;
+ // Allocate data buffers.
+ auto *db_factory = inst->state()->db_factory();
+ auto *address_db = db_factory->Allocate<uint64_t>(num_elements);
+ auto addresses = address_db->Get<uint64_t>();
+ auto *store_data_db = db_factory->Allocate(num_elements * sizeof(T));
+ auto *mask_db = db_factory->Allocate<bool>(num_elements);
+
+ // Get the spans for addresses and masks.
+ auto store_data = store_data_db->Get<T>();
+ auto masks = mask_db->Get<bool>();
+
+ // Convert the bit masks to byte masks. Set up addresses.
+ for (int i = vstart; i < num_elements; i++) {
+ int mask_index = i >> 3;
+ int mask_offset = i & 0b111;
+ addresses[i - vstart] = base + i * stride;
+ masks[i - vstart] = ((src_masks[mask_index] >> mask_offset) & 0b1) != 0;
+ store_data[i - vstart] = GetInstructionSource<T>(inst, 0, i);
+ if (masks[i - vstart]) {
+ if (!CheckCapBounds(inst, addresses[i - vstart], sizeof(T), cap_reg,
+ state)) {
+ address_db->DecRef();
+ mask_db->DecRef();
+ store_data_db->DecRef();
+ return;
+ }
+ }
+ }
+ // Perform the store.
+ state->StoreMemory(inst, address_db, mask_db, sizeof(T), store_data_db);
+ address_db->DecRef();
+ mask_db->DecRef();
+ store_data_db->DecRef();
+}
+
+// Vector store - strided.
+// Source(0): store data.
+// Source(1): base address.
+// Source(2): stride.
+// Source(3): vector mask register, vector constant {1..} if not masked.
+void VsStrided(int element_width, const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int emul = element_width * rv_vector->vector_length_multiplier() /
+ rv_vector->selected_element_width();
+ // Validate that emul has a legal value.
+ if ((emul > 64) || (emul == 0)) {
+ LOG(WARNING) << absl::StrCat("Illegal emul value for vector store (", emul,
+ ")");
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int vlength = rv_vector->vector_length();
+ int vstart = rv_vector->vstart();
+ switch (element_width) {
+ case 1:
+ StoreVectorStrided<uint8_t>(vlength, vstart, emul, inst);
+ break;
+ case 2:
+ StoreVectorStrided<uint16_t>(vlength, vstart, emul, inst);
+ break;
+ case 4:
+ StoreVectorStrided<uint32_t>(vlength, vstart, emul, inst);
+ break;
+ case 8:
+ StoreVectorStrided<uint64_t>(vlength, vstart, emul, inst);
+ break;
+ default:
+ break;
+ }
+ rv_vector->clear_vstart();
+}
+
+// Store vector mask. Single vector register store.
+// Source(0): store data
+// Source(1): base address
+void Vsm(const Instruction *inst) {
+ auto *state = static_cast<CheriotState *>(inst->state());
+ auto *rv_vector = state->rv_vector();
+ auto cap_reg = GetCapSource(inst, 1);
+ if (!CheckCapForMemoryAccess(inst, cap_reg, state)) return;
+ uint64_t base = cap_reg->address();
+ // Compute base address.
+ int start = rv_vector->vstart();
+ // Compute the number of bytes and elements to be stored.
+ int num_bytes = rv_vector->vector_register_byte_length();
+ int num_bytes_stored = num_bytes - start;
+ // Allocate address data buffer.
+ auto *db_factory = inst->state()->db_factory();
+ auto *address_db = db_factory->Allocate<uint64_t>(num_bytes_stored);
+ auto *store_data_db = db_factory->Allocate(num_bytes_stored);
+ auto *mask_db = db_factory->Allocate<uint8_t>(num_bytes_stored);
+ // Get the spans for addresses, masks, and store data.
+ auto addresses = address_db->Get<uint64_t>();
+ auto masks = mask_db->Get<bool>();
+ auto store_data = store_data_db->Get<uint8_t>();
+ // Convert the bit masks to byte masks. Set up addresses.
+ for (int i = start; i < num_bytes; i++) {
+ addresses[i - start] = base + i;
+ masks[i - start] = true;
+ store_data[i - start] = GetInstructionSource<uint8_t>(inst, 0, i);
+ if (!CheckCapBounds(inst, addresses[i - start], sizeof(uint8_t), cap_reg,
+ state)) {
+ address_db->DecRef();
+ mask_db->DecRef();
+ store_data_db->DecRef();
+ return;
+ }
+ }
+ state->StoreMemory(inst, address_db, mask_db, sizeof(uint8_t), store_data_db);
+ address_db->DecRef();
+ mask_db->DecRef();
+ store_data_db->DecRef();
+ rv_vector->clear_vstart();
+}
+
+// Vector store indexed. Index values are not scaled by
+// element size, as the index values can also be treated as multiple base
+// addresses with the base address acting as a common offset. Index values are
+// treated as unsigned integers, and are zero extended from the element size to
+// the internal address size (or truncated in case the internal XLEN is < index
+// element size).
+// Source(0): store data.
+// Source(1): base address.
+// Source(2): offset vector.
+// Source(3): mask.
+void VsIndexed(int index_width, const Instruction *inst) {
+ auto *state = static_cast<CheriotState *>(inst->state());
+ auto *rv_vector = state->rv_vector();
+ auto cap_reg = GetCapSource(inst, 1);
+ if (!CheckCapForMemoryAccess(inst, cap_reg, state)) return;
+ uint64_t base = cap_reg->address();
+ // Compute base address.
+ int start = rv_vector->vstart();
+ int num_elements = rv_vector->vector_length() - start;
+ int element_width = rv_vector->selected_element_width();
+ int lmul8 = rv_vector->vector_length_multiplier();
+ int index_emul = index_width * lmul8 / element_width;
+ // Validate that emul has a legal value.
+ if ((index_emul > 64) || (index_emul == 0)) {
+ // TODO: signal vector error.
+ rv_vector->set_vector_exception();
+ return;
+ }
+
+ auto *index_op = static_cast<RV32VectorSourceOperand *>(inst->Source(2));
+
+ // Allocate data buffers.
+ auto *db_factory = inst->state()->db_factory();
+ auto *address_db = db_factory->Allocate<uint64_t>(num_elements);
+ auto *value_db = db_factory->Allocate(num_elements * element_width);
+ auto *mask_db = db_factory->Allocate<bool>(num_elements);
+
+ // Get the source mask (stored in a single vector register).
+ auto *src_mask_op = static_cast<RV32VectorSourceOperand *>(inst->Source(3));
+ auto src_masks = src_mask_op->GetRegister(0)->data_buffer()->Get<uint8_t>();
+
+ // Get the spans for addresses, masks, and data.
+ auto masks = mask_db->Get<bool>();
+ auto addresses = address_db->Get<uint64_t>();
+
+ // Convert the bit masks to byte masks and compute the element addresses.
+ for (int i = start; i < num_elements; i++) {
+ int mask_index = i >> 3;
+ int mask_offset = i & 0b111;
+ uint64_t offset;
+ switch (index_width) {
+ case 1:
+ offset = index_op->AsUint8(i);
+ break;
+ case 2:
+ offset = index_op->AsUint16(i);
+ break;
+ case 4:
+ offset = index_op->AsUint32(i);
+ break;
+ case 8:
+ offset = index_op->AsUint64(i);
+ break;
+ default:
+ offset = 0;
+ // TODO: signal error.
+ LOG(ERROR) << "Illegal value for index type width";
+ return;
+ }
+ addresses[i - start] = base + offset;
+ masks[i - start] = ((src_masks[mask_index] >> mask_offset) & 0b1) != 0;
+ switch (element_width) {
+ case 1:
+ value_db->Set<uint8_t>(i, GetInstructionSource<uint8_t>(inst, 0, i));
+ break;
+ case 2:
+ value_db->Set<uint16_t>(i, GetInstructionSource<uint16_t>(inst, 0, i));
+ break;
+ case 4:
+ value_db->Set<uint32_t>(i, GetInstructionSource<uint32_t>(inst, 0, i));
+ break;
+ case 8:
+ value_db->Set<uint64_t>(i, GetInstructionSource<uint64_t>(inst, 0, i));
+ break;
+ default:
+ offset = 0;
+ // TODO: signal error.
+ LOG(ERROR) << "Illegal value for element width";
+ break;
+ }
+ if (masks[i - start]) {
+ if (!CheckCapBounds(inst, addresses[i - start], element_width, cap_reg,
+ state)) {
+ address_db->DecRef();
+ mask_db->DecRef();
+ value_db->DecRef();
+ return;
+ }
+ }
+ }
+
+ // Set up context and submit store
+ state->StoreMemory(inst, address_db, mask_db, element_width, value_db);
+ address_db->DecRef();
+ mask_db->DecRef();
+ value_db->DecRef();
+ rv_vector->clear_vstart();
+}
+
+void VsRegister(int num_regs, const Instruction *inst) {
+ auto *state = static_cast<CheriotState *>(inst->state());
+ auto *rv_vector = state->rv_vector();
+ auto cap_reg = GetCapSource(inst, 1);
+ if (!CheckCapForMemoryAccess(inst, cap_reg, state)) return;
+ uint64_t base = cap_reg->address();
+ int num_elements =
+ rv_vector->vector_register_byte_length() * num_regs / sizeof(uint64_t);
+ // Allocate data buffers.
+ auto *db_factory = inst->state()->db_factory();
+ auto *data_db = db_factory->Allocate<uint64_t>(num_elements);
+ auto *address_db = db_factory->Allocate<uint64_t>(num_elements);
+ auto *mask_db = db_factory->Allocate<bool>(num_elements);
+ // Get the address, mask, and data spans.
+ auto addresses = address_db->Get<uint64_t>();
+ auto masks = mask_db->Get<bool>();
+ auto data = data_db->Get<uint64_t>();
+ for (int i = 0; i < num_elements; i++) {
+ addresses[i] = base + i * sizeof(uint64_t);
+ masks[i] = true;
+ data[i] = GetInstructionSource<uint64_t>(inst, 0, i);
+ if (!CheckCapBounds(inst, addresses[i], sizeof(uint64_t), cap_reg, state)) {
+ address_db->DecRef();
+ mask_db->DecRef();
+ data_db->DecRef();
+ return;
+ }
+ } // Submit store.
+ state->StoreMemory(inst, address_db, mask_db, sizeof(uint64_t), data_db);
+ address_db->DecRef();
+ mask_db->DecRef();
+ data_db->DecRef();
+ rv_vector->clear_vstart();
+}
+
+// Vector store segment (unit stride). This stores the segments contiguously
+// in memory in a sequential manner.
+void VsSegment(int element_width, const Instruction *inst) {
+ auto *state = static_cast<CheriotState *>(inst->state());
+ auto *rv_vector = state->rv_vector();
+ auto cap_reg = GetCapSource(inst, 1);
+ if (!CheckCapForMemoryAccess(inst, cap_reg, state)) return;
+ uint64_t base_address = cap_reg->address();
+ int start = rv_vector->vstart();
+ auto src_mask_op = static_cast<RV32VectorSourceOperand *>(inst->Source(2));
+ auto src_masks = src_mask_op->GetRegister(0)->data_buffer()->Get<uint8_t>();
+ int num_fields = GetInstructionSource<int32_t>(inst, 3) + 1;
+ // Effective vector length multiplier.
+ int emul = (element_width * rv_vector->vector_length_multiplier()) /
+ rv_vector->selected_element_width();
+ if (emul * num_fields > 64) {
+ // This is a reserved encoding error.
+ // If > 64, it means that the number of registers required is > 8.
+ // TODO: signal error.
+ LOG(ERROR) << "Reserved encoding error";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int num_segments = rv_vector->vector_length();
+ int num_elements = num_fields * num_segments;
+ int num_elements_per_reg =
+ rv_vector->vector_register_byte_length() / element_width;
+ int reg_mul = std::max(1, emul / 8);
+ // Set up data buffers.
+ auto *db_factory = inst->state()->db_factory();
+ auto *data_db = db_factory->Allocate(num_elements * element_width);
+ auto *address_db = db_factory->Allocate<uint64_t>(num_elements);
+ auto *mask_db = db_factory->Allocate<bool>(num_elements);
+ // Get spans for addresses and masks.
+ auto addresses = address_db->Get<uint64_t>();
+ auto masks = mask_db->Get<bool>();
+ auto data1 = data_db->Get<uint8_t>();
+ auto data2 = data_db->Get<uint16_t>();
+ auto data4 = data_db->Get<uint32_t>();
+ auto data8 = data_db->Get<uint64_t>();
+
+ auto *data_op = static_cast<RV32VectorSourceOperand *>(inst->Source(0));
+ uint64_t address = base_address;
+ int count = 0;
+ for (int segment = start; segment < num_segments; segment++) {
+ // Masks are applied on a segment basis.
+ int mask_index = segment >> 3;
+ int mask_offset = segment & 0b111;
+ bool mask_value = ((src_masks[mask_index] >> mask_offset) & 0x1) != 0;
+ // If the segments span multiple registers, compute the register offset
+ // from the current segment number (upper bits).
+ int reg_offset = segment / num_elements_per_reg;
+ for (int field = 0; field < num_fields; field++) {
+ // Compute register offset number within register group.
+ int reg_no = field * reg_mul + reg_offset;
+ // Compute element address and set mask value.
+ addresses[count] = address;
+ address += element_width;
+ masks[count] = mask_value;
+ if (!mask_value) {
+ // If mask is false, just increment count and go to next field.
+ count++;
+ continue;
+ }
+ if (!CheckCapBounds(inst, addresses[count], element_width, cap_reg,
+ state)) {
+ address_db->DecRef();
+ mask_db->DecRef();
+ data_db->DecRef();
+ return;
+ }
+ // Write store data from register db to data db.
+ auto *reg_db = data_op->GetRegister(reg_no)->data_buffer();
+ switch (element_width) {
+ case 1:
+ data1[count] = reg_db->Get<uint8_t>(segment % num_elements_per_reg);
+ break;
+ case 2:
+ data2[count] = reg_db->Get<uint16_t>(segment % num_elements_per_reg);
+ break;
+ case 4:
+ data4[count] = reg_db->Get<uint32_t>(segment % num_elements_per_reg);
+ break;
+ case 8:
+ data8[count] = reg_db->Get<uint64_t>(segment % num_elements_per_reg);
+ break;
+ default:
+ break;
+ }
+ count++;
+ }
+ }
+ state->StoreMemory(inst, address_db, mask_db, element_width, data_db);
+ // Release the dbs.
+ address_db->DecRef();
+ mask_db->DecRef();
+ data_db->DecRef();
+ rv_vector->clear_vstart();
+}
+
+// Vector strided segment store. This stores each segment contiguously at
+// locations separated by the segment stride.
+void VsSegmentStrided(int element_width, const Instruction *inst) {
+ auto *state = static_cast<CheriotState *>(inst->state());
+ auto *rv_vector = state->rv_vector();
+ auto cap_reg = GetCapSource(inst, 1);
+ if (!CheckCapForMemoryAccess(inst, cap_reg, state)) return;
+ uint64_t base_address = cap_reg->address();
+ int start = rv_vector->vstart();
+ int64_t segment_stride = GetInstructionSource<int64_t>(inst, 2);
+ auto src_mask_op = static_cast<RV32VectorSourceOperand *>(inst->Source(3));
+ auto src_masks = src_mask_op->GetRegister(0)->data_buffer()->Get<uint8_t>();
+ int num_fields = GetInstructionSource<int32_t>(inst, 4) + 1;
+ // Effective vector length multiplier.
+ int emul = (element_width * rv_vector->vector_length_multiplier()) /
+ rv_vector->selected_element_width();
+ if (emul * num_fields > 64) {
+ // This is a reserved encoding error.
+ // If > 64, it means that the number of registers required is > 8.
+ // TODO: signal error.
+ LOG(ERROR) << "Reserved encoding error";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int num_segments = rv_vector->vector_length();
+ int num_elements = num_fields * num_segments;
+ int num_elements_per_reg =
+ rv_vector->vector_register_byte_length() / element_width;
+ int reg_mul = std::max(1, emul / 8);
+ // Set up data buffers.
+ auto *db_factory = inst->state()->db_factory();
+ auto *data_db = db_factory->Allocate(num_elements * element_width);
+ auto *address_db = db_factory->Allocate<uint64_t>(num_elements);
+ auto *mask_db = db_factory->Allocate<bool>(num_elements);
+ // Get spans for addresses and masks.
+ auto addresses = address_db->Get<uint64_t>();
+ auto masks = mask_db->Get<bool>();
+ auto data1 = data_db->Get<uint8_t>();
+ auto data2 = data_db->Get<uint16_t>();
+ auto data4 = data_db->Get<uint32_t>();
+ auto data8 = data_db->Get<uint64_t>();
+
+ auto *data_op = static_cast<RV32VectorSourceOperand *>(inst->Source(0));
+ uint64_t segment_address = base_address;
+ int count = 0;
+ for (int segment = start; segment < num_segments; segment++) {
+ // Masks are applied on a segment basis.
+ int mask_index = segment >> 3;
+ int mask_offset = segment & 0b111;
+ bool mask_value = ((src_masks[mask_index] >> mask_offset) & 0x1) != 0;
+ // If the segments span multiple registers, compute the register offset
+ // from the current segment number (upper bits).
+ int reg_offset = segment / num_elements_per_reg;
+ uint64_t field_address = segment_address;
+ for (int field = 0; field < num_fields; field++) {
+ // Compute register offset number within register group.
+ int reg_no = field * reg_mul + reg_offset;
+ // Compute element address and set mask value.
+ addresses[count] = field_address;
+ field_address += element_width;
+ masks[count] = mask_value;
+ if (!mask_value) {
+ // If mask is false, just increment count and go to next field.
+ count++;
+ continue;
+ }
+ if (!CheckCapBounds(inst, addresses[count], element_width, cap_reg,
+ state)) {
+ address_db->DecRef();
+ mask_db->DecRef();
+ data_db->DecRef();
+ return;
+ }
+ // Write store data from register db to data db.
+ auto *reg_db = data_op->GetRegister(reg_no)->data_buffer();
+ switch (element_width) {
+ case 1:
+ data1[count] = reg_db->Get<uint8_t>(segment % num_elements_per_reg);
+ break;
+ case 2:
+ data2[count] = reg_db->Get<uint16_t>(segment % num_elements_per_reg);
+ break;
+ case 4:
+ data4[count] = reg_db->Get<uint32_t>(segment % num_elements_per_reg);
+ break;
+ case 8:
+ data8[count] = reg_db->Get<uint64_t>(segment % num_elements_per_reg);
+ break;
+ default:
+ break;
+ }
+ count++;
+ }
+ segment_address += segment_stride;
+ }
+ state->StoreMemory(inst, address_db, mask_db, element_width, data_db);
+ // Release the dbs.
+ address_db->DecRef();
+ mask_db->DecRef();
+ data_db->DecRef();
+ rv_vector->clear_vstart();
+}
+
+// Vector indexed segment store. This instruction stores each segment
+// contiguously at an address formed by adding the index value for that
+// segment (from the index vector source operand) to the base address.
+void VsSegmentIndexed(int index_width, const Instruction *inst) {
+ auto *state = static_cast<CheriotState *>(inst->state());
+ auto *rv_vector = state->rv_vector();
+ auto cap_reg = GetCapSource(inst, 1);
+ if (!CheckCapForMemoryAccess(inst, cap_reg, state)) return;
+ uint64_t base_address = cap_reg->address();
+ int start = rv_vector->vstart();
+ auto src_mask_op = static_cast<RV32VectorSourceOperand *>(inst->Source(3));
+ auto src_masks = src_mask_op->GetRegister(0)->data_buffer()->Get<uint8_t>();
+ int num_fields = GetInstructionSource<int32_t>(inst, 4) + 1;
+ int element_width = rv_vector->selected_element_width();
+ // Effective vector length multiplier.
+ int lmul = rv_vector->vector_length_multiplier();
+ int emul = index_width * lmul / element_width;
+ if (lmul * num_fields > 64) {
+ // This is a reserved encoding error.
+ // If > 64, it means that the number of registers required is > 8.
+ // TODO: signal error.
+ LOG(ERROR) << "Reserved encoding error - lmul * num_fields out of range";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ if (emul == 0 || emul > 64) {
+ // This is a reserved encoding error.
+ // If > 64, it means that the number of registers required is > 8.
+ // TODO: signal error.
+ LOG(ERROR) << "Reserved encoding error - emul out of range.";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int num_segments = rv_vector->vector_length();
+ int num_elements = num_fields * num_segments;
+ int num_elements_per_reg =
+ rv_vector->vector_register_byte_length() / element_width;
+ int reg_mul = std::max(1, lmul / 8);
+ // Set up data buffers.
+ auto *db_factory = inst->state()->db_factory();
+ auto *data_db = db_factory->Allocate(num_elements * element_width);
+ auto *address_db = db_factory->Allocate<uint64_t>(num_elements);
+ auto *mask_db = db_factory->Allocate<bool>(num_elements);
+ // Get spans for addresses and masks.
+ auto addresses = address_db->Get<uint64_t>();
+ auto masks = mask_db->Get<bool>();
+ auto data1 = data_db->Get<uint8_t>();
+ auto data2 = data_db->Get<uint16_t>();
+ auto data4 = data_db->Get<uint32_t>();
+ auto data8 = data_db->Get<uint64_t>();
+
+ auto *data_op = static_cast<RV32VectorSourceOperand *>(inst->Source(0));
+ int count = 0;
+ for (int segment = start; segment < num_segments; segment++) {
+ // Masks are applied on a segment basis.
+ int mask_index = segment >> 3;
+ int mask_offset = segment & 0b111;
+ bool mask_value = ((src_masks[mask_index] >> mask_offset) & 0x1) != 0;
+ // If the segments span multiple registers, compute the register offset
+ // from the current segment number (upper bits).
+ int reg_offset = segment / num_elements_per_reg;
+ int64_t index_value;
+ switch (index_width) {
+ case 1:
+ index_value = GetInstructionSource<int8_t>(inst, 2, segment);
+ break;
+ case 2:
+ index_value = GetInstructionSource<int16_t>(inst, 2, segment);
+ break;
+ case 4:
+ index_value = GetInstructionSource<int32_t>(inst, 2, segment);
+ break;
+ case 8:
+ index_value = GetInstructionSource<int64_t>(inst, 2, segment);
+ break;
+ default:
+ LOG(ERROR) << "Invalid index width: " << index_width << ".";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ uint64_t field_address = base_address + index_value;
+ for (int field = 0; field < num_fields; field++) {
+ // Compute register offset number within register group.
+ int reg_no = field * reg_mul + reg_offset;
+ // Compute element address and set mask value.
+ addresses[count] = field_address;
+ field_address += element_width;
+ masks[count] = mask_value;
+ if (!mask_value) {
+ // If mask is false, just increment count and go to next field.
+ count++;
+ continue;
+ }
+ if (!CheckCapBounds(inst, addresses[count], element_width, cap_reg,
+ state)) {
+ address_db->DecRef();
+ mask_db->DecRef();
+ data_db->DecRef();
+ return;
+ }
+ // Write store data from register db to data db.
+ auto *reg_db = data_op->GetRegister(reg_no)->data_buffer();
+ switch (element_width) {
+ case 1:
+ data1[count] = reg_db->Get<uint8_t>(segment % num_elements_per_reg);
+ break;
+ case 2:
+ data2[count] = reg_db->Get<uint16_t>(segment % num_elements_per_reg);
+ break;
+ case 4:
+ data4[count] = reg_db->Get<uint32_t>(segment % num_elements_per_reg);
+ break;
+ case 8:
+ data8[count] = reg_db->Get<uint64_t>(segment % num_elements_per_reg);
+ break;
+ default:
+ LOG(ERROR) << "Invalid element width: " << element_width << ".";
+ return;
+ }
+ count++;
+ }
+ }
+ state->StoreMemory(inst, address_db, mask_db, element_width, data_db);
+ // Release the dbs.
+ address_db->DecRef();
+ mask_db->DecRef();
+ data_db->DecRef();
+ rv_vector->clear_vstart();
+}
+
+} // namespace cheriot
+} // namespace sim
+} // namespace mpact
diff --git a/cheriot/riscv_cheriot_vector_memory_instructions.h b/cheriot/riscv_cheriot_vector_memory_instructions.h
new file mode 100644
index 0000000..ab3cb41
--- /dev/null
+++ b/cheriot/riscv_cheriot_vector_memory_instructions.h
@@ -0,0 +1,140 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef MPACT_CHERIOT_RISCV_CHERIOT_VECTOR_MEMORY_INSTRUCTIONS_H_
+#define MPACT_CHERIOT_RISCV_CHERIOT_VECTOR_MEMORY_INSTRUCTIONS_H_
+
+#include "mpact/sim/generic/instruction.h"
+
+// This file declares the semantic functions used to implement RiscV vector
+// load store instructions.
+
+namespace mpact {
+namespace sim {
+namespace cheriot {
+
+using Instruction = ::mpact::sim::generic::Instruction;
+
+// Set vector length. rd/rs1_ zero is true if the corresponding operand is
+// register x0.
+// The instruction takes 2 instruction source scalar operands: source operand 0
+// is the requested vector length, source operand 1 is the requested vector
+// configuration value. The destination operand is a scalar.
+void Vsetvl(bool rd_zero, bool rs1_zero, const Instruction *inst);
+
+// Vector load semantic functions.
+// Load with unit stride as element width. The instruction takes 2 source and 1
+// destination operands. Source operand 0 is a scalar base address, source
+// operand 1 is the vector mask (either a vector register, or a constant).
+// Destination operand 0 is assigned to the child instruction and is a vector
+// register (group).
+void VlUnitStrided(int element_width, const Instruction *inst);
+// Load with constant stride, the parameter specifies the width of the vector
+// elements. This instruction takes 3 source and 1 destination operands. Source
+// operand 0 is a scalar base address, source operand 1 is a scalar stride,
+// source operand 2 is the vector mask (either a vector register, or a
+// constant). Destination operand 0 is assigned to the child instruction and
+// is a vector register (group).
+void VlStrided(int element_width, const Instruction *inst);
+// Load vector mask. This instruction takes 1 source and 1 destination operand.
+// The source operand is a scalar base address, the destination operand is the
+// vector register to write the mask to.
+void Vlm(const Instruction *inst);
+// Indexed vector load (ordered and unordered). This instruction takes 3 source
+// and 1 destination operands. Source operand 0 is a scalar base address, source
+// operand 1 is a vector register (group) of indices, source operand 2 is the
+// vector mask. Destination operand 0 is assigned to the child instruction and
+// is a vector register (group).
+void VlIndexed(int index_width, const Instruction *inst);
+// Load vector register(s). Takes a parameter specifying how many registers to
+// load. This instruction takes 1 source and 1 destination operand. Source
+// operand 0 is a scalar base address. Destination operand 0 is assigned to the
+// child instruction and is a vector register (group).
+void VlRegister(int num_regs, int element_width_bytes, const Instruction *inst);
+// Child instruction semantic functions for non-segment loads responsible for
+// writing load data back to the target register(s). It takes a single
+// destination operand. Destination operand 0 is a vector register (group).
+void VlChild(const Instruction *inst);
+// Load segment, unit stride. The function takes one parameter that specifies
+// the element width. The instruction takes 3 source operands and 1 destination
+// operand. Source operand 0 is a scalar base address, source operand 1 is
+// the vector mask, and source operand 2 is the number of fields - 1.
+// Destination operand 0 is assigned to the child instruction and is a vector
+// register (group).
+void VlSegment(int element_width, const Instruction *inst);
+// Load segment strided. The function takes one parameter that specifies
+// the element width. The instruction takes 4 source operands and 1 destination
+// operand. Source operand 0 is a scalar base address, source operand 1 is a
+// scalar stride, source operand 2 is the vector mask, and source operand 3 is
+// the number of fields - 1. Destination operand 0 is assigned to the child
+// instruction and is a vector register (group).
+void VlSegmentStrided(int element_width, const Instruction *inst);
+// Load segment indexed. The function takes one parameter that specifies
+// the index element width. The instruction takes 4 source operands and 1
+// destination operand. Source operand 0 is a scalar base address, source
+// operand 1 is a vector register (group) of indices, source operand 2 is the
+// vector mask, and source operand 3 is the number of fields - 1. Destination
+// operand 0 is assigned to the child instruction and is a vector register
+// (group).
+void VlSegmentIndexed(int index_width, const Instruction *inst);
+// Child instruction semantic functions for segment loads responsible for
+// writing load data back to the target register(s). It takes a single
+// destination operand. Destination operand 0 is a vector register (group).
+void VlSegmentChild(int element_width, const Instruction *inst);
+
+// Vector store semantic functions.
+
+// Store strided. The function takes one parameter that specifies the element
+// width. The instruction takes 4 source parameters. Source 0 is the store data
+// vector register (group), source 1 is the scalar base address, source 2 is the
+// stride, and source 3 is the vector mask.
+void VsStrided(int element_width, const Instruction *inst);
+// Store vector mask. This instruction takes 2 source operands. Source 0 is the
+// vector mask register to be stored, the second is the scalar base address.
+void Vsm(const Instruction *inst);
+// Store indexed. The function takes one parameter that specifies the element
+// width. The instruction takes 4 source parameters. Source 0 is the store data
+// vector register (group), source 1 is a vector (group) of indices, source 2 is
+// the stride, and source 3 is the vector mask.
+void VsIndexed(int index_width, const Instruction *inst);
+// Store vector register (group). This function takes one parameter that
+// specifies the number of registers to store. The instruction takes 2 source
+// operands. Source 0 is the source vector register (group), the second is the
+// scalar base address.
+void VsRegister(int num_regs, const Instruction *inst);
+// Store segment, unit stride. The function takes one parameter that specifies
+// the element width. The instruction takes 4 source operands. Source operand 0
+// is the store data, source operand 1 is the scalar base address, source
+// operand 2 is the vector mask, and source operand 3 is the number of fields
+// - 1.
+void VsSegment(int element_width, const Instruction *inst);
+// Store segment, unit stride. The function takes one parameter that specifies
+// the element width. The instruction takes 5 source operands. Source operand 0
+// is the store data, source operand 1 is the scalar base address, source
+// operand 2 is the segment stride, source operand 3 is the vector mask, and
+// source operand 4 is the number of fields
+// - 1.
+void VsSegmentStrided(int element_width, const Instruction *inst);
+// Load segment indexed. The function takes one parameter that specifies
+// the index element width. The instruction takes 5 source operands. Source
+// operand 0 is the store data, source operand 1 is a scalar base address,
+// source operand 2 is a vector register (group) of indices, source operand 3 is
+// the vector mask, and source operand 4 is the number of fields - 1.
+void VsSegmentIndexed(int index_width, const Instruction *inst);
+
+} // namespace cheriot
+} // namespace sim
+} // namespace mpact
+
+#endif // MPACT_CHERIOT_RISCV_CHERIOT_VECTOR_MEMORY_INSTRUCTIONS_H_
diff --git a/cheriot/riscv_cheriot_vector_opi_instructions.cc b/cheriot/riscv_cheriot_vector_opi_instructions.cc
new file mode 100644
index 0000000..e2fa037
--- /dev/null
+++ b/cheriot/riscv_cheriot_vector_opi_instructions.cc
@@ -0,0 +1,1411 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cheriot/riscv_cheriot_vector_opi_instructions.h"
+
+#include <algorithm>
+#include <cstdint>
+#include <cstring>
+#include <limits>
+#include <type_traits>
+
+#include "absl/log/log.h"
+#include "cheriot/cheriot_state.h"
+#include "cheriot/cheriot_vector_state.h"
+#include "cheriot/riscv_cheriot_vector_instruction_helpers.h"
+#include "mpact/sim/generic/type_helpers.h"
+#include "riscv//riscv_register.h"
+
+// This file contains the instruction semantic functions for most of the
+// vector instructions in the OPIVV, OPIVX, and OPIVI encoding spaces. The
+// exception is vector element permute instructions and a couple of reduction
+// instructions.
+
+namespace mpact {
+namespace sim {
+namespace cheriot {
+
+using ::mpact::sim::generic::MakeUnsigned;
+using ::mpact::sim::generic::WideType;
+using riscv::RV32VectorSourceOperand;
+using std::numeric_limits;
+
+// Vector arithmetic operations.
+
+// Vector add.
+void Vadd(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorOp<uint8_t, uint8_t, uint8_t>(
+ rv_vector, inst,
+ [](uint8_t vs2, uint8_t vs1) -> uint8_t { return vs2 + vs1; });
+ case 2:
+ return RiscVBinaryVectorOp<uint16_t, uint16_t, uint16_t>(
+ rv_vector, inst,
+ [](uint16_t vs2, uint16_t vs1) -> uint16_t { return vs2 + vs1; });
+ case 4:
+ return RiscVBinaryVectorOp<uint32_t, uint32_t, uint32_t>(
+ rv_vector, inst,
+ [](uint32_t vs2, uint32_t vs1) -> uint32_t { return vs2 + vs1; });
+ case 8:
+ return RiscVBinaryVectorOp<uint64_t, uint64_t, uint64_t>(
+ rv_vector, inst,
+ [](uint64_t vs2, uint64_t vs1) -> uint64_t { return vs2 + vs1; });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Vector subtract.
+void Vsub(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorOp<uint8_t, uint8_t, uint8_t>(
+ rv_vector, inst,
+ [](uint8_t vs2, uint8_t vs1) -> uint8_t { return vs2 - vs1; });
+ case 2:
+ return RiscVBinaryVectorOp<uint16_t, uint16_t, uint16_t>(
+ rv_vector, inst,
+ [](uint16_t vs2, uint16_t vs1) -> uint16_t { return vs2 - vs1; });
+ case 4:
+ return RiscVBinaryVectorOp<uint32_t, uint32_t, uint32_t>(
+ rv_vector, inst,
+ [](uint32_t vs2, uint32_t vs1) -> uint32_t { return vs2 - vs1; });
+ case 8:
+ return RiscVBinaryVectorOp<uint64_t, uint64_t, uint64_t>(
+ rv_vector, inst,
+ [](uint64_t vs2, uint64_t vs1) -> uint64_t { return vs2 - vs1; });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Vector reverse subtract.
+void Vrsub(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorOp<uint8_t, uint8_t, uint8_t>(
+ rv_vector, inst,
+ [](uint8_t vs2, uint8_t vs1) -> uint8_t { return vs1 - vs2; });
+ case 2:
+ return RiscVBinaryVectorOp<uint16_t, uint16_t, uint16_t>(
+ rv_vector, inst,
+ [](uint16_t vs2, uint16_t vs1) -> uint16_t { return vs1 - vs2; });
+ case 4:
+ return RiscVBinaryVectorOp<uint32_t, uint32_t, uint32_t>(
+ rv_vector, inst,
+ [](uint32_t vs2, uint32_t vs1) -> uint32_t { return vs1 - vs2; });
+ case 8:
+ return RiscVBinaryVectorOp<uint64_t, uint64_t, uint64_t>(
+ rv_vector, inst,
+ [](uint64_t vs2, uint64_t vs1) -> uint64_t { return vs1 - vs2; });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Vector logical operations.
+
+// Vector and.
+void Vand(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorOp<uint8_t, uint8_t, uint8_t>(
+ rv_vector, inst,
+ [](uint8_t vs2, uint8_t vs1) -> uint8_t { return vs2 & vs1; });
+ case 2:
+ return RiscVBinaryVectorOp<uint16_t, uint16_t, uint16_t>(
+ rv_vector, inst,
+ [](uint16_t vs2, uint16_t vs1) -> uint16_t { return vs2 & vs1; });
+ case 4:
+ return RiscVBinaryVectorOp<uint32_t, uint32_t, uint32_t>(
+ rv_vector, inst,
+ [](uint32_t vs2, uint32_t vs1) -> uint32_t { return vs2 & vs1; });
+ case 8:
+ return RiscVBinaryVectorOp<uint64_t, uint64_t, uint64_t>(
+ rv_vector, inst,
+ [](uint64_t vs2, uint64_t vs1) -> uint64_t { return vs2 & vs1; });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Vector or.
+void Vor(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorOp<uint8_t, uint8_t, uint8_t>(
+ rv_vector, inst,
+ [](uint8_t vs2, uint8_t vs1) -> uint8_t { return vs2 | vs1; });
+ case 2:
+ return RiscVBinaryVectorOp<uint16_t, uint16_t, uint16_t>(
+ rv_vector, inst,
+ [](uint16_t vs2, uint16_t vs1) -> uint16_t { return vs2 | vs1; });
+ case 4:
+ return RiscVBinaryVectorOp<uint32_t, uint32_t, uint32_t>(
+ rv_vector, inst,
+ [](uint32_t vs2, uint32_t vs1) -> uint32_t { return vs2 | vs1; });
+ case 8:
+ return RiscVBinaryVectorOp<uint64_t, uint64_t, uint64_t>(
+ rv_vector, inst,
+ [](uint64_t vs2, uint64_t vs1) -> uint64_t { return vs2 | vs1; });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Vector xor.
+void Vxor(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorOp<uint8_t, uint8_t, uint8_t>(
+ rv_vector, inst,
+ [](uint8_t vs2, uint8_t vs1) -> uint8_t { return vs2 ^ vs1; });
+ case 2:
+ return RiscVBinaryVectorOp<uint16_t, uint16_t, uint16_t>(
+ rv_vector, inst,
+ [](uint16_t vs2, uint16_t vs1) -> uint16_t { return vs2 ^ vs1; });
+ case 4:
+ return RiscVBinaryVectorOp<uint32_t, uint32_t, uint32_t>(
+ rv_vector, inst,
+ [](uint32_t vs2, uint32_t vs1) -> uint32_t { return vs2 ^ vs1; });
+ case 8:
+ return RiscVBinaryVectorOp<uint64_t, uint64_t, uint64_t>(
+ rv_vector, inst,
+ [](uint64_t vs2, uint64_t vs1) -> uint64_t { return vs2 ^ vs1; });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Vector shift operations.
+
+// Vector shift left logical.
+void Vsll(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorOp<uint8_t, uint8_t, uint8_t>(
+ rv_vector, inst, [](uint8_t vs2, uint8_t vs1) -> uint8_t {
+ return vs2 << (vs1 & 0b111);
+ });
+ case 2:
+ return RiscVBinaryVectorOp<uint16_t, uint16_t, uint16_t>(
+ rv_vector, inst, [](uint16_t vs2, uint16_t vs1) -> uint16_t {
+ return vs2 << (vs1 & 0b1111);
+ });
+ case 4:
+ return RiscVBinaryVectorOp<uint32_t, uint32_t, uint32_t>(
+ rv_vector, inst, [](uint32_t vs2, uint32_t vs1) -> uint32_t {
+ return vs2 << (vs1 & 0b1'1111);
+ });
+ case 8:
+ return RiscVBinaryVectorOp<uint64_t, uint64_t, uint64_t>(
+ rv_vector, inst, [](uint64_t vs2, uint64_t vs1) -> uint64_t {
+ return vs2 << (vs1 & 0b11'1111);
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Vector shift right logical.
+void Vsrl(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorOp<uint8_t, uint8_t, uint8_t>(
+ rv_vector, inst, [](uint8_t vs2, uint8_t vs1) -> uint8_t {
+ return vs2 >> (vs1 & 0b111);
+ });
+ case 2:
+ return RiscVBinaryVectorOp<uint16_t, uint16_t, uint16_t>(
+ rv_vector, inst, [](uint16_t vs2, uint16_t vs1) -> uint16_t {
+ return vs2 >> (vs1 & 0b1111);
+ });
+ case 4:
+ return RiscVBinaryVectorOp<uint32_t, uint32_t, uint32_t>(
+ rv_vector, inst, [](uint32_t vs2, uint32_t vs1) -> uint32_t {
+ return vs2 >> (vs1 & 0b1'1111);
+ });
+ case 8:
+ return RiscVBinaryVectorOp<uint64_t, uint64_t, uint64_t>(
+ rv_vector, inst, [](uint64_t vs2, uint64_t vs1) -> uint64_t {
+ return vs2 >> (vs1 & 0b11'1111);
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Vector shift right arithmetic.
+void Vsra(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorOp<int8_t, int8_t, int8_t>(
+ rv_vector, inst, [](int8_t vs2, int8_t vs1) -> int8_t {
+ return vs2 >> (vs1 & 0b111);
+ });
+ case 2:
+ return RiscVBinaryVectorOp<int16_t, int16_t, int16_t>(
+ rv_vector, inst, [](int16_t vs2, int16_t vs1) -> int16_t {
+ return vs2 >> (vs1 & 0b1111);
+ });
+ case 4:
+ return RiscVBinaryVectorOp<int32_t, int32_t, int32_t>(
+ rv_vector, inst, [](int32_t vs2, int32_t vs1) -> int32_t {
+ return vs2 >> (vs1 & 0b1'1111);
+ });
+ case 8:
+ return RiscVBinaryVectorOp<int64_t, int64_t, int64_t>(
+ rv_vector, inst, [](int64_t vs2, int64_t vs1) -> int64_t {
+ return vs2 >> (vs1 & 0b11'1111);
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Vector narrowing shift operations. Narrow from sew * 2 to sew.
+
+// Vector narrowing shift right logical. Source op 0 is shifted right
+// by source op 1 and the result is 1/2 the size of source op 0.
+void Vnsrl(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ // LMUL8 cannot be 64.
+ if (rv_vector->vector_length_multiplier() > 32) {
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Vector length multiplier out of range for narrowing shift";
+ return;
+ }
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorOp<uint8_t, uint16_t, uint8_t>(
+ rv_vector, inst, [](uint16_t vs2, uint8_t vs1) -> uint8_t {
+ return static_cast<uint8_t>(vs2 >> (vs1 & 0b1111));
+ });
+ case 2:
+ return RiscVBinaryVectorOp<uint16_t, uint32_t, uint16_t>(
+ rv_vector, inst, [](uint32_t vs2, uint16_t vs1) -> uint16_t {
+ return static_cast<uint16_t>(vs2 >> (vs1 & 0b1'1111));
+ });
+ case 4:
+ return RiscVBinaryVectorOp<uint32_t, uint64_t, uint32_t>(
+ rv_vector, inst, [](uint64_t vs2, uint32_t vs1) -> uint32_t {
+ return static_cast<uint32_t>(vs2 >> (vs1 & 0b11'1111));
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value for narrowing shift right: " << sew;
+ return;
+ }
+}
+
+// Vector narrowing shift right arithmetic. Source op 0 is shifted right
+// by source op 1 and the result is 1/2 the size of source op 0.
+void Vnsra(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ // If the vector length multiplier (x8) is greater than 32, that means that
+ // the source values (sew * 2) would exceed the available register group.
+ if (rv_vector->vector_length_multiplier() > 32) {
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Vector length multiplier out of range for narrowing shift";
+ return;
+ }
+ // Note, sew cannot be 64 bits, as there is no support for operations on
+ // 128 bit quantities.
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorOp<int8_t, int16_t, int8_t>(
+ rv_vector, inst, [](int16_t vs2, int8_t vs1) -> int8_t {
+ return vs2 >> (vs1 & 0b1111);
+ });
+ case 2:
+ return RiscVBinaryVectorOp<int16_t, int32_t, int16_t>(
+ rv_vector, inst, [](int32_t vs2, int16_t vs1) -> int16_t {
+ return vs2 >> (vs1 & 0b1'1111);
+ });
+ case 4:
+ return RiscVBinaryVectorOp<int32_t, int64_t, int32_t>(
+ rv_vector, inst, [](int64_t vs2, int32_t vs1) -> int32_t {
+ return vs2 >> (vs1 & 0b11'1111);
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value for narrowing shift right: " << sew;
+ return;
+ }
+}
+
+// Vector unsigned min.
+void Vminu(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorOp<uint8_t, uint8_t, uint8_t>(
+ rv_vector, inst, [](uint8_t vs2, uint8_t vs1) -> uint8_t {
+ return std::min(vs2, vs1);
+ });
+ case 2:
+ return RiscVBinaryVectorOp<uint16_t, uint16_t, uint16_t>(
+ rv_vector, inst, [](uint16_t vs2, uint16_t vs1) -> uint16_t {
+ return std::min(vs2, vs1);
+ });
+ case 4:
+ return RiscVBinaryVectorOp<uint32_t, uint32_t, uint32_t>(
+ rv_vector, inst, [](uint32_t vs2, uint32_t vs1) -> uint32_t {
+ return std::min(vs2, vs1);
+ });
+ case 8:
+ return RiscVBinaryVectorOp<uint64_t, uint64_t, uint64_t>(
+ rv_vector, inst, [](uint64_t vs2, uint64_t vs1) -> uint64_t {
+ return std::min(vs2, vs1);
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Vector signed min.
+void Vmin(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorOp<int8_t, int8_t, int8_t>(
+ rv_vector, inst,
+ [](int8_t vs2, int8_t vs1) -> int8_t { return std::min(vs2, vs1); });
+ case 2:
+ return RiscVBinaryVectorOp<int16_t, int16_t, int16_t>(
+ rv_vector, inst, [](int16_t vs2, int16_t vs1) -> int16_t {
+ return std::min(vs2, vs1);
+ });
+ case 4:
+ return RiscVBinaryVectorOp<int32_t, int32_t, int32_t>(
+ rv_vector, inst, [](int32_t vs2, int32_t vs1) -> int32_t {
+ return std::min(vs2, vs1);
+ });
+ case 8:
+ return RiscVBinaryVectorOp<int64_t, int64_t, int64_t>(
+ rv_vector, inst, [](int64_t vs2, int64_t vs1) -> int64_t {
+ return std::min(vs2, vs1);
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Vector unsigned max.
+void Vmaxu(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorOp<uint8_t, uint8_t, uint8_t>(
+ rv_vector, inst, [](uint8_t vs2, uint8_t vs1) -> uint8_t {
+ return std::max(vs2, vs1);
+ });
+ case 2:
+ return RiscVBinaryVectorOp<uint16_t, uint16_t, uint16_t>(
+ rv_vector, inst, [](uint16_t vs2, uint16_t vs1) -> uint16_t {
+ return std::max(vs2, vs1);
+ });
+ case 4:
+ return RiscVBinaryVectorOp<uint32_t, uint32_t, uint32_t>(
+ rv_vector, inst, [](uint32_t vs2, uint32_t vs1) -> uint32_t {
+ return std::max(vs2, vs1);
+ });
+ case 8:
+ return RiscVBinaryVectorOp<uint64_t, uint64_t, uint64_t>(
+ rv_vector, inst, [](uint64_t vs2, uint64_t vs1) -> uint64_t {
+ return std::max(vs2, vs1);
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Vector signed max.
+void Vmax(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorOp<int8_t, int8_t, int8_t>(
+ rv_vector, inst,
+ [](int8_t vs2, int8_t vs1) -> int8_t { return std::max(vs2, vs1); });
+ case 2:
+ return RiscVBinaryVectorOp<int16_t, int16_t, int16_t>(
+ rv_vector, inst, [](int16_t vs2, int16_t vs1) -> int16_t {
+ return std::max(vs2, vs1);
+ });
+ case 4:
+ return RiscVBinaryVectorOp<int32_t, int32_t, int32_t>(
+ rv_vector, inst, [](int32_t vs2, int32_t vs1) -> int32_t {
+ return std::max(vs2, vs1);
+ });
+ case 8:
+ return RiscVBinaryVectorOp<int64_t, int64_t, int64_t>(
+ rv_vector, inst, [](int64_t vs2, int64_t vs1) -> int64_t {
+ return std::max(vs2, vs1);
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Set equal.
+void Vmseq(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorMaskOp<uint8_t, uint8_t>(
+ rv_vector, inst,
+ [](uint8_t vs2, uint8_t vs1) -> bool { return (vs2 == vs1); });
+ case 2:
+ return RiscVBinaryVectorMaskOp<uint16_t, uint16_t>(
+ rv_vector, inst,
+ [](uint16_t vs2, uint16_t vs1) -> bool { return (vs2 == vs1); });
+ case 4:
+ return RiscVBinaryVectorMaskOp<uint32_t, uint32_t>(
+ rv_vector, inst,
+ [](uint32_t vs2, uint32_t vs1) -> bool { return (vs2 == vs1); });
+ case 8:
+ return RiscVBinaryVectorMaskOp<uint64_t, uint64_t>(
+ rv_vector, inst,
+ [](uint64_t vs2, uint64_t vs1) -> bool { return (vs2 == vs1); });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Vector compare instructions.
+
+// Set not equal.
+void Vmsne(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorMaskOp<uint8_t, uint8_t>(
+ rv_vector, inst,
+ [](uint8_t vs2, uint8_t vs1) -> bool { return (vs2 != vs1); });
+ case 2:
+ return RiscVBinaryVectorMaskOp<uint16_t, uint16_t>(
+ rv_vector, inst,
+ [](uint16_t vs2, uint16_t vs1) -> bool { return (vs2 != vs1); });
+ case 4:
+ return RiscVBinaryVectorMaskOp<uint32_t, uint32_t>(
+ rv_vector, inst,
+ [](uint32_t vs2, uint32_t vs1) -> bool { return (vs2 != vs1); });
+ case 8:
+ return RiscVBinaryVectorMaskOp<uint64_t, uint64_t>(
+ rv_vector, inst,
+ [](uint64_t vs2, uint64_t vs1) -> bool { return (vs2 != vs1); });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Set less than unsigned.
+void Vmsltu(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorMaskOp<uint8_t, uint8_t>(
+ rv_vector, inst,
+ [](uint8_t vs2, uint8_t vs1) -> bool { return (vs2 < vs1); });
+ case 2:
+ return RiscVBinaryVectorMaskOp<uint16_t, uint16_t>(
+ rv_vector, inst,
+ [](uint16_t vs2, uint16_t vs1) -> bool { return (vs2 < vs1); });
+ case 4:
+ return RiscVBinaryVectorMaskOp<uint32_t, uint32_t>(
+ rv_vector, inst,
+ [](uint32_t vs2, uint32_t vs1) -> bool { return (vs2 < vs1); });
+ case 8:
+ return RiscVBinaryVectorMaskOp<uint64_t, uint64_t>(
+ rv_vector, inst,
+ [](uint64_t vs2, uint64_t vs1) -> bool { return (vs2 < vs1); });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Set less than.
+void Vmslt(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorMaskOp<int8_t, int8_t>(
+ rv_vector, inst,
+ [](int8_t vs2, int8_t vs1) -> bool { return (vs2 < vs1); });
+ case 2:
+ return RiscVBinaryVectorMaskOp<int16_t, int16_t>(
+ rv_vector, inst,
+ [](int16_t vs2, int16_t vs1) -> bool { return (vs2 < vs1); });
+ case 4:
+ return RiscVBinaryVectorMaskOp<int32_t, int32_t>(
+ rv_vector, inst,
+ [](int32_t vs2, int32_t vs1) -> bool { return (vs2 < vs1); });
+ case 8:
+ return RiscVBinaryVectorMaskOp<int64_t, int64_t>(
+ rv_vector, inst,
+ [](int64_t vs2, int64_t vs1) -> bool { return (vs2 < vs1); });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Set less than or equal unsigned.
+void Vmsleu(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorMaskOp<uint8_t, uint8_t>(
+ rv_vector, inst,
+ [](uint8_t vs2, uint8_t vs1) -> bool { return (vs2 <= vs1); });
+ case 2:
+ return RiscVBinaryVectorMaskOp<uint16_t, uint16_t>(
+ rv_vector, inst,
+ [](uint16_t vs2, uint16_t vs1) -> bool { return (vs2 <= vs1); });
+ case 4:
+ return RiscVBinaryVectorMaskOp<uint32_t, uint32_t>(
+ rv_vector, inst,
+ [](uint32_t vs2, uint32_t vs1) -> bool { return (vs2 <= vs1); });
+ case 8:
+ return RiscVBinaryVectorMaskOp<uint64_t, uint64_t>(
+ rv_vector, inst,
+ [](uint64_t vs2, uint64_t vs1) -> bool { return (vs2 <= vs1); });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Set less than or equal.
+void Vmsle(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorMaskOp<int8_t, int8_t>(
+ rv_vector, inst,
+ [](int8_t vs2, int8_t vs1) -> bool { return (vs2 <= vs1); });
+ case 2:
+ return RiscVBinaryVectorMaskOp<int16_t, int16_t>(
+ rv_vector, inst,
+ [](int16_t vs2, int16_t vs1) -> bool { return (vs2 <= vs1); });
+ case 4:
+ return RiscVBinaryVectorMaskOp<int32_t, int32_t>(
+ rv_vector, inst,
+ [](int32_t vs2, int32_t vs1) -> bool { return (vs2 <= vs1); });
+ case 8:
+ return RiscVBinaryVectorMaskOp<int64_t, int64_t>(
+ rv_vector, inst,
+ [](int64_t vs2, int64_t vs1) -> bool { return (vs2 <= vs1); });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Set greater than unsigned.
+void Vmsgtu(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorMaskOp<uint8_t, uint8_t>(
+ rv_vector, inst,
+ [](uint8_t vs2, uint8_t vs1) -> bool { return (vs2 > vs1); });
+ case 2:
+ return RiscVBinaryVectorMaskOp<uint16_t, uint16_t>(
+ rv_vector, inst,
+ [](uint16_t vs2, uint16_t vs1) -> bool { return (vs2 > vs1); });
+ case 4:
+ return RiscVBinaryVectorMaskOp<uint32_t, uint32_t>(
+ rv_vector, inst,
+ [](uint32_t vs2, uint32_t vs1) -> bool { return (vs2 > vs1); });
+ case 8:
+ return RiscVBinaryVectorMaskOp<uint64_t, uint64_t>(
+ rv_vector, inst,
+ [](uint64_t vs2, uint64_t vs1) -> bool { return (vs2 > vs1); });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Set greater than.
+void Vmsgt(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorMaskOp<int8_t, int8_t>(
+ rv_vector, inst,
+ [](int8_t vs2, int8_t vs1) -> bool { return (vs2 > vs1); });
+ case 2:
+ return RiscVBinaryVectorMaskOp<int16_t, int16_t>(
+ rv_vector, inst,
+ [](int16_t vs2, int16_t vs1) -> bool { return (vs2 > vs1); });
+ case 4:
+ return RiscVBinaryVectorMaskOp<int32_t, int32_t>(
+ rv_vector, inst,
+ [](int32_t vs2, int32_t vs1) -> bool { return (vs2 > vs1); });
+ case 8:
+ return RiscVBinaryVectorMaskOp<int64_t, int64_t>(
+ rv_vector, inst,
+ [](int64_t vs2, int64_t vs1) -> bool { return (vs2 > vs1); });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Saturated unsigned addition.
+void Vsaddu(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorOp<uint8_t, uint8_t, uint8_t>(
+ rv_vector, inst, [rv_vector](uint8_t vs2, uint8_t vs1) -> uint8_t {
+ uint8_t sum = vs2 + vs1;
+ if (sum < vs1) {
+ sum = numeric_limits<uint8_t>::max();
+ rv_vector->set_vxsat(true);
+ }
+ return sum;
+ });
+ case 2:
+ return RiscVBinaryVectorOp<uint16_t, uint16_t, uint16_t>(
+ rv_vector, inst, [rv_vector](uint16_t vs2, uint16_t vs1) -> uint16_t {
+ uint16_t sum = vs2 + vs1;
+ if (sum < vs1) {
+ sum = numeric_limits<uint16_t>::max();
+ rv_vector->set_vxsat(true);
+ }
+ return sum;
+ });
+ case 4:
+ return RiscVBinaryVectorOp<uint32_t, uint32_t, uint32_t>(
+ rv_vector, inst, [rv_vector](uint32_t vs2, uint32_t vs1) -> uint32_t {
+ uint32_t sum = vs2 + vs1;
+ if (sum < vs1) {
+ sum = numeric_limits<uint32_t>::max();
+ rv_vector->set_vxsat(true);
+ }
+ return sum;
+ });
+ case 8:
+ return RiscVBinaryVectorOp<uint64_t, uint64_t, uint64_t>(
+ rv_vector, inst, [rv_vector](uint64_t vs2, uint64_t vs1) -> uint64_t {
+ uint64_t sum = vs2 + vs1;
+ if (sum < vs1) {
+ sum = numeric_limits<uint64_t>::max();
+ rv_vector->set_vxsat(true);
+ }
+ return sum;
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Helper function for Vsadd.
+// Uses unsigned arithmetic for the addition to avoid signed overflow, which,
+// when compiled with --config=asan, will trigger an exception.
+template <typename T>
+inline T VsaddHelper(T vs2, T vs1, CheriotVectorState *rv_vector) {
+ using UT = typename std::make_unsigned<T>::type;
+ UT uvs2 = static_cast<UT>(vs2);
+ UT uvs1 = static_cast<UT>(vs1);
+ UT usum = uvs2 + uvs1;
+ T sum = static_cast<T>(usum);
+ if (((vs2 ^ vs1) >= 0) && ((sum ^ vs2) < 0)) {
+ rv_vector->set_vxsat(true);
+ return vs2 > 0 ? numeric_limits<T>::max() : numeric_limits<T>::min();
+ }
+ return sum;
+}
+
+// Saturated signed addition.
+void Vsadd(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorOp<int8_t, int8_t, int8_t>(
+ rv_vector, inst, [rv_vector](int8_t vs2, int8_t vs1) -> int8_t {
+ return VsaddHelper(vs2, vs1, rv_vector);
+ });
+ case 2:
+ return RiscVBinaryVectorOp<int16_t, int16_t, int16_t>(
+ rv_vector, inst, [rv_vector](int16_t vs2, int16_t vs1) -> int16_t {
+ return VsaddHelper(vs2, vs1, rv_vector);
+ });
+ case 4:
+ return RiscVBinaryVectorOp<int32_t, int32_t, int32_t>(
+ rv_vector, inst, [rv_vector](int32_t vs2, int32_t vs1) -> int32_t {
+ return VsaddHelper(vs2, vs1, rv_vector);
+ });
+ case 8:
+ return RiscVBinaryVectorOp<int64_t, int64_t, int64_t>(
+ rv_vector, inst, [rv_vector](int64_t vs2, int64_t vs1) -> int64_t {
+ return VsaddHelper(vs2, vs1, rv_vector);
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Saturated unsigned subtract.
+void Vssubu(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorOp<uint8_t, uint8_t, uint8_t>(
+ rv_vector, inst, [rv_vector](uint8_t vs2, uint8_t vs1) -> uint8_t {
+ uint8_t diff = vs2 - vs1;
+ if (vs2 < vs1) {
+ diff = 0;
+ rv_vector->set_vxsat(true);
+ }
+ return diff;
+ });
+ case 2:
+ return RiscVBinaryVectorOp<uint16_t, uint16_t, uint16_t>(
+ rv_vector, inst, [rv_vector](uint16_t vs2, uint16_t vs1) -> uint16_t {
+ uint16_t diff = vs2 - vs1;
+ if (vs2 < vs1) {
+ diff = 0;
+ rv_vector->set_vxsat(true);
+ }
+ return diff;
+ });
+ case 4:
+ return RiscVBinaryVectorOp<uint32_t, uint32_t, uint32_t>(
+ rv_vector, inst, [rv_vector](uint32_t vs2, uint32_t vs1) -> uint32_t {
+ uint32_t diff = vs2 - vs1;
+ if (vs2 < vs1) {
+ diff = 0;
+ rv_vector->set_vxsat(true);
+ }
+ return diff;
+ });
+ case 8:
+ return RiscVBinaryVectorOp<uint64_t, uint64_t, uint64_t>(
+ rv_vector, inst, [rv_vector](uint64_t vs2, uint64_t vs1) -> uint64_t {
+ uint64_t diff = vs2 - vs1;
+ if (vs2 < vs1) {
+ diff = 0;
+ rv_vector->set_vxsat(true);
+ }
+ return diff;
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+template <typename T>
+T VssubHelper(T vs2, T vs1, CheriotVectorState *rv_vector) {
+ using UT = typename std::make_unsigned<T>::type;
+ UT uvs2 = static_cast<UT>(vs2);
+ UT uvs1 = static_cast<UT>(vs1);
+ UT udiff = uvs2 - uvs1;
+ T diff = static_cast<T>(udiff);
+ if (((vs2 ^ vs1) < 0) && ((diff ^ vs1) >= 0)) {
+ rv_vector->set_vxsat(true);
+ return vs1 < 0 ? numeric_limits<T>::max() : numeric_limits<T>::min();
+ }
+ return diff;
+}
+
+// Saturated signed subtract.
+void Vssub(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorOp<int8_t, int8_t, int8_t>(
+ rv_vector, inst, [rv_vector](int8_t vs2, int8_t vs1) -> int8_t {
+ return VssubHelper(vs2, vs1, rv_vector);
+ });
+ case 2:
+ return RiscVBinaryVectorOp<int16_t, int16_t, int16_t>(
+ rv_vector, inst, [rv_vector](int16_t vs2, int16_t vs1) -> int16_t {
+ return VssubHelper(vs2, vs1, rv_vector);
+ });
+ case 4:
+ return RiscVBinaryVectorOp<int32_t, int32_t, int32_t>(
+ rv_vector, inst, [rv_vector](int32_t vs2, int32_t vs1) -> int32_t {
+ return VssubHelper(vs2, vs1, rv_vector);
+ });
+ case 8:
+ return RiscVBinaryVectorOp<int64_t, int64_t, int64_t>(
+ rv_vector, inst, [rv_vector](int64_t vs2, int64_t vs1) -> int64_t {
+ return VssubHelper(vs2, vs1, rv_vector);
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Add/Subtract with carry, carry generation.
+void Vadc(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVMaskBinaryVectorOp<uint8_t, uint8_t, uint8_t>(
+ rv_vector, inst, [](uint8_t vs2, uint8_t vs1, bool mask) -> uint8_t {
+ return vs2 + vs1 + static_cast<uint8_t>(mask);
+ });
+ case 2:
+ return RiscVMaskBinaryVectorOp<uint16_t, uint16_t, uint16_t>(
+ rv_vector, inst,
+ [](uint16_t vs2, uint16_t vs1, bool mask) -> uint16_t {
+ return vs2 + vs1 + static_cast<uint16_t>(mask);
+ });
+ case 4:
+ return RiscVMaskBinaryVectorOp<uint32_t, uint32_t, uint32_t>(
+ rv_vector, inst,
+ [](uint32_t vs2, uint32_t vs1, bool mask) -> uint32_t {
+ return vs2 + vs1 + static_cast<uint32_t>(mask);
+ });
+ case 8:
+ return RiscVMaskBinaryVectorOp<uint64_t, uint64_t, uint64_t>(
+ rv_vector, inst,
+ [](uint64_t vs2, uint64_t vs1, bool mask) -> uint64_t {
+ return vs2 + vs1 + static_cast<uint64_t>(mask);
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Add with carry - carry generation.
+void Vmadc(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVSetMaskBinaryVectorMaskOp<uint8_t, uint8_t>(
+ rv_vector, inst, [](uint8_t vs2, uint8_t vs1, bool mask) -> bool {
+ uint16_t sum = static_cast<uint16_t>(vs2) +
+ static_cast<uint16_t>(vs1) +
+ static_cast<uint16_t>(mask);
+ sum >>= 8;
+ return sum;
+ });
+ case 2:
+ return RiscVSetMaskBinaryVectorMaskOp<uint16_t, uint16_t>(
+ rv_vector, inst, [](uint16_t vs2, uint16_t vs1, bool mask) -> bool {
+ uint32_t sum = static_cast<uint32_t>(vs2) +
+ static_cast<uint32_t>(vs1) +
+ static_cast<uint32_t>(mask);
+ sum >>= 16;
+ return sum != 0;
+ });
+ case 4:
+ return RiscVSetMaskBinaryVectorMaskOp<uint32_t, uint32_t>(
+ rv_vector, inst, [](uint32_t vs2, uint32_t vs1, bool mask) -> bool {
+ uint64_t sum = static_cast<uint64_t>(vs2) +
+ static_cast<uint64_t>(vs1) +
+ static_cast<uint64_t>(mask);
+ sum >>= 32;
+ return sum != 0;
+ });
+ case 8:
+ return RiscVSetMaskBinaryVectorMaskOp<uint64_t, uint64_t>(
+ rv_vector, inst, [](uint64_t vs2, uint64_t vs1, bool mask) -> bool {
+ // Compute carry by doing two additions. First get the carry out
+ // from adding the low byte.
+ uint64_t carry =
+ (vs1 & 0xff + vs2 & 0xff + static_cast<uint64_t>(mask)) >> 8;
+ // Now add the high 7 bytes together with the carry from the low
+ // byte addition.
+ uint64_t sum = (vs1 >> 8) + (vs2 >> 8) + carry;
+ // The carry out is in the high byte.
+ sum >>= 56;
+ return sum != 0;
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Subtract with borrow.
+void Vsbc(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVMaskBinaryVectorOp<uint8_t, uint8_t, uint8_t>(
+ rv_vector, inst, [](uint8_t vs2, uint8_t vs1, bool mask) -> uint8_t {
+ return vs2 - vs1 - static_cast<uint8_t>(mask);
+ });
+ case 2:
+ return RiscVMaskBinaryVectorOp<uint16_t, uint16_t, uint16_t>(
+ rv_vector, inst,
+ [](uint16_t vs2, uint16_t vs1, bool mask) -> uint16_t {
+ return vs2 - vs1 - static_cast<uint16_t>(mask);
+ });
+ case 4:
+ return RiscVMaskBinaryVectorOp<uint32_t, uint32_t, uint32_t>(
+ rv_vector, inst,
+ [](uint32_t vs2, uint32_t vs1, bool mask) -> uint32_t {
+ return vs2 - vs1 - static_cast<uint32_t>(mask);
+ });
+ case 8:
+ return RiscVMaskBinaryVectorOp<uint64_t, uint64_t, uint64_t>(
+ rv_vector, inst,
+ [](uint64_t vs2, uint64_t vs1, bool mask) -> uint64_t {
+ return vs2 - vs1 - static_cast<uint64_t>(mask);
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Subtract with borrow - borrow generation.
+void Vmsbc(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVSetMaskBinaryVectorMaskOp<uint8_t, uint8_t>(
+ rv_vector, inst, [](uint8_t vs2, uint8_t vs1, bool mask) -> bool {
+ return static_cast<uint16_t>(vs2) <
+ static_cast<uint16_t>(mask) + static_cast<uint16_t>(vs1);
+ });
+ case 2:
+ return RiscVSetMaskBinaryVectorMaskOp<uint16_t, uint16_t>(
+ rv_vector, inst, [](uint16_t vs2, uint16_t vs1, bool mask) -> bool {
+ return static_cast<uint32_t>(vs2) <
+ static_cast<uint32_t>(mask) + static_cast<uint32_t>(vs1);
+ });
+ case 4:
+ return RiscVSetMaskBinaryVectorMaskOp<uint32_t, uint32_t>(
+ rv_vector, inst, [](uint32_t vs2, uint32_t vs1, bool mask) -> bool {
+ return static_cast<uint64_t>(vs2) <
+ static_cast<uint64_t>(mask) + static_cast<uint64_t>(vs1);
+ });
+ case 8:
+ return RiscVSetMaskBinaryVectorMaskOp<uint64_t, uint64_t>(
+ rv_vector, inst, [](uint64_t vs2, uint64_t vs1, bool mask) -> bool {
+ if (vs2 < vs1) return true;
+ if (vs2 == vs1) return mask;
+ return false;
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Vector merge.
+void Vmerge(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVMaskBinaryVectorOp<uint8_t, uint8_t, uint8_t>(
+ rv_vector, inst, [](uint8_t vs2, uint8_t vs1, bool mask) -> uint8_t {
+ return mask ? vs1 : vs2;
+ });
+ case 2:
+ return RiscVMaskBinaryVectorOp<uint16_t, uint16_t, uint16_t>(
+ rv_vector, inst,
+ [](uint16_t vs2, uint16_t vs1, bool mask) -> uint16_t {
+ return mask ? vs1 : vs2;
+ });
+ case 4:
+ return RiscVMaskBinaryVectorOp<uint32_t, uint32_t, uint32_t>(
+ rv_vector, inst,
+ [](uint32_t vs2, uint32_t vs1, bool mask) -> uint32_t {
+ return mask ? vs1 : vs2;
+ });
+ case 8:
+ return RiscVMaskBinaryVectorOp<uint64_t, uint64_t, uint64_t>(
+ rv_vector, inst,
+ [](uint64_t vs2, uint64_t vs1, bool mask) -> uint64_t {
+ return mask ? vs1 : vs2;
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Vector move register(s).
+void Vmvr(int num_regs, Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ if (rv_vector->vector_exception()) return;
+
+ auto *src_op = static_cast<RV32VectorSourceOperand *>(inst->Source(0));
+ auto *dest_op =
+ static_cast<RV32VectorDestinationOperand *>(inst->Destination(0));
+ if (src_op->size() < num_regs) {
+ LOG(ERROR) << "Vmvr: source operand has fewer registers than requested";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ if (dest_op->size() < num_regs) {
+ LOG(ERROR)
+ << "Vmvr: destination operand has fewer registers than requested";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int sew = rv_vector->selected_element_width();
+ int num_elements_per_vector = rv_vector->vector_register_byte_length() / sew;
+ int vstart = rv_vector->vstart();
+ int start_reg = vstart / num_elements_per_vector;
+ for (int i = start_reg; i < num_regs; i++) {
+ auto *src_db = src_op->GetRegister(i)->data_buffer();
+ auto *dest_db = dest_op->AllocateDataBuffer(i);
+ std::memcpy(dest_db->raw_ptr(), src_db->raw_ptr(),
+ dest_db->size<uint8_t>());
+ dest_db->Submit();
+ }
+ rv_vector->clear_vstart();
+}
+
+// Templated helper function for shift right with rounding.
+template <typename T>
+T VssrHelper(CheriotVectorState *rv_vector, T vs2, T vs1) {
+ using UT = typename MakeUnsigned<T>::type;
+ int rm = rv_vector->vxrm();
+ int max_shift = (sizeof(T) << 3) - 1;
+ int shift_amount = static_cast<int>(vs1 & max_shift);
+ // Create mask for the bits that will be shifted out + 1.
+ UT round_bits = vs2;
+ if (shift_amount < max_shift) {
+ UT mask = numeric_limits<UT>::max();
+ mask = ~(numeric_limits<UT>::max() << shift_amount + 1);
+ round_bits = vs2 & mask;
+ }
+ vs2 >>= shift_amount;
+ vs2 += static_cast<T>(GetRoundingBit(rm, round_bits, shift_amount + 1));
+ return vs2;
+}
+
+// Logical shift right with rounding.
+void Vssrl(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorOp<uint8_t, uint8_t, uint8_t>(
+ rv_vector, inst, [rv_vector](uint8_t vs2, uint8_t vs1) -> uint8_t {
+ return VssrHelper(rv_vector, vs2, vs1);
+ });
+ case 2:
+ return RiscVBinaryVectorOp<uint16_t, uint16_t, uint16_t>(
+ rv_vector, inst, [rv_vector](uint16_t vs2, uint16_t vs1) -> uint16_t {
+ return VssrHelper(rv_vector, vs2, vs1);
+ });
+ case 4:
+ return RiscVBinaryVectorOp<uint32_t, uint32_t, uint32_t>(
+ rv_vector, inst, [rv_vector](uint32_t vs2, uint32_t vs1) -> uint32_t {
+ return VssrHelper(rv_vector, vs2, vs1);
+ });
+ case 8:
+ return RiscVBinaryVectorOp<uint64_t, uint64_t, uint64_t>(
+ rv_vector, inst, [rv_vector](uint64_t vs2, uint64_t vs1) -> uint64_t {
+ return VssrHelper(rv_vector, vs2, vs1);
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Arithmetic shift right with rounding.
+void Vssra(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorOp<int8_t, int8_t, int8_t>(
+ rv_vector, inst, [rv_vector](int8_t vs2, int8_t vs1) -> int8_t {
+ return VssrHelper(rv_vector, vs2, vs1);
+ });
+ case 2:
+ return RiscVBinaryVectorOp<int16_t, int16_t, int16_t>(
+ rv_vector, inst, [rv_vector](int16_t vs2, int16_t vs1) -> int16_t {
+ return VssrHelper(rv_vector, vs2, vs1);
+ });
+ case 4:
+ return RiscVBinaryVectorOp<int32_t, int32_t, int32_t>(
+ rv_vector, inst, [rv_vector](int32_t vs2, int32_t vs1) -> int32_t {
+ return VssrHelper(rv_vector, vs2, vs1);
+ });
+ case 8:
+ return RiscVBinaryVectorOp<int64_t, int64_t, int64_t>(
+ rv_vector, inst, [rv_vector](int64_t vs2, int64_t vs1) -> int64_t {
+ return VssrHelper(rv_vector, vs2, vs1);
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Templated helper function for shift right with rounding and saturation.
+template <typename DT, typename WT, typename T>
+T VnclipHelper(CheriotVectorState *rv_vector, WT vs2, T vs1) {
+ using WUT = typename std::make_unsigned<WT>::type;
+ int rm = rv_vector->vxrm();
+ int max_shift = (sizeof(WT) << 3) - 1;
+ int shift_amount = vs1 & ((sizeof(WT) << 3) - 1);
+ // Create mask for the bits that will be shifted out + 1.
+ WUT mask = vs2;
+ if (shift_amount < max_shift) {
+ mask = ~(numeric_limits<WUT>::max() << (shift_amount + 1));
+ }
+ WUT round_bits = vs2 & mask;
+ // Perform the rounded shift.
+ vs2 =
+ (vs2 >> shift_amount) + GetRoundingBit(rm, round_bits, shift_amount + 1);
+ // Saturate if needed.
+ if (vs2 > numeric_limits<DT>::max()) {
+ rv_vector->set_vxsat(true);
+ return numeric_limits<DT>::max();
+ }
+ if (vs2 < numeric_limits<DT>::min()) {
+ rv_vector->set_vxsat(true);
+ return numeric_limits<DT>::min();
+ }
+ return static_cast<DT>(vs2);
+}
+
+// Arithmetic shift right and narrowing from 2*sew to sew with rounding and
+// signed saturation.
+void Vnclip(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int lmul8 = rv_vector->vector_length_multiplier();
+ // This is a narrowing operation and sew is that of the narrow data type.
+ // Thus if lmul > 32, then emul for the wider data type is illegal.
+ if (lmul8 > 32) {
+ LOG(ERROR) << "Illegal lmul value";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorOp<int8_t, int16_t, int8_t>(
+ rv_vector, inst, [rv_vector](int16_t vs2, int8_t vs1) -> int8_t {
+ return VnclipHelper<int8_t, int16_t, int8_t>(rv_vector, vs2, vs1);
+ });
+ case 2:
+ return RiscVBinaryVectorOp<int16_t, int32_t, int16_t>(
+ rv_vector, inst, [rv_vector](int32_t vs2, int16_t vs1) -> int16_t {
+ return VnclipHelper<int16_t, int32_t, int16_t>(rv_vector, vs2, vs1);
+ });
+ case 4:
+ return RiscVBinaryVectorOp<int32_t, int64_t, int32_t>(
+ rv_vector, inst, [rv_vector](int64_t vs2, int32_t vs1) -> int32_t {
+ return VnclipHelper<int32_t, int64_t, int32_t>(rv_vector, vs2, vs1);
+ });
+ case 8:
+ // There is no valid sew * 2 = 16.
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Logical shift right and narrowing from 2*sew to sew with rounding and
+// unsigned saturation.
+void Vnclipu(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int lmul8 = rv_vector->vector_length_multiplier();
+ // This is a narrowing operation and sew is that of the narrow data type.
+ // Thus if lmul > 32, then emul for the wider data type is illegal.
+ if (lmul8 > 32) {
+ LOG(ERROR) << "Illegal lmul value";
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorOp<uint8_t, uint16_t, uint8_t>(
+ rv_vector, inst, [rv_vector](uint16_t vs2, uint8_t vs1) -> uint8_t {
+ return VnclipHelper<uint8_t, uint16_t, uint8_t>(rv_vector, vs2,
+ vs1);
+ });
+ case 2:
+ return RiscVBinaryVectorOp<uint16_t, uint32_t, uint16_t>(
+ rv_vector, inst, [rv_vector](uint32_t vs2, uint16_t vs1) -> uint16_t {
+ return VnclipHelper<uint16_t, uint32_t, uint16_t>(rv_vector, vs2,
+ vs1);
+ });
+ case 4:
+ return RiscVBinaryVectorOp<uint32_t, uint64_t, uint32_t>(
+ rv_vector, inst, [rv_vector](uint64_t vs2, uint32_t vs1) -> uint32_t {
+ return VnclipHelper<uint32_t, uint64_t, uint32_t>(rv_vector, vs2,
+ vs1);
+ });
+ case 8:
+ // There is no valid sew * 2 = 16.
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Perform a signed multiply from T to wider int type. Shift that result
+// right by sizeof(T) * 8 - 1 and round. Saturate if needed to fit into T.
+template <typename T>
+T VsmulHelper(CheriotVectorState *rv_vector, T vs2, T vs1) {
+ using WT = typename WideType<T>::type;
+ WT vd_w;
+ WT vs2_w = static_cast<WT>(vs2);
+ WT vs1_w = static_cast<WT>(vs1);
+ vd_w = vs2_w * vs1_w;
+ vd_w = VssrHelper<WT>(rv_vector, vd_w, sizeof(T) * 8 - 1);
+ if (vd_w < numeric_limits<T>::min()) {
+ rv_vector->set_vxsat(true);
+ return numeric_limits<T>::min();
+ }
+ if (vd_w > numeric_limits<T>::max()) {
+ rv_vector->set_vxsat(true);
+ return numeric_limits<T>::max();
+ }
+ return static_cast<T>(vd_w);
+}
+
+// Vector fractional multiply with rounding and saturation.
+void Vsmul(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorOp<int8_t, int8_t, int8_t>(
+ rv_vector, inst, [rv_vector](int8_t vs2, int8_t vs1) -> int8_t {
+ return VsmulHelper<int8_t>(rv_vector, vs2, vs1);
+ });
+ case 2:
+ return RiscVBinaryVectorOp<int16_t, int16_t, int16_t>(
+ rv_vector, inst, [rv_vector](int16_t vs2, int16_t vs1) -> int16_t {
+ return VsmulHelper<int16_t>(rv_vector, vs2, vs1);
+ });
+ case 4:
+ return RiscVBinaryVectorOp<int32_t, int32_t, int32_t>(
+ rv_vector, inst, [rv_vector](int32_t vs2, int32_t vs1) -> int32_t {
+ return VsmulHelper<int32_t>(rv_vector, vs2, vs1);
+ });
+ case 8:
+ return RiscVBinaryVectorOp<int64_t, int64_t, int64_t>(
+ rv_vector, inst, [rv_vector](int64_t vs2, int64_t vs1) -> int64_t {
+ return VsmulHelper<int64_t>(rv_vector, vs2, vs1);
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+} // namespace cheriot
+} // namespace sim
+} // namespace mpact
diff --git a/cheriot/riscv_cheriot_vector_opi_instructions.h b/cheriot/riscv_cheriot_vector_opi_instructions.h
new file mode 100644
index 0000000..87ccbd9
--- /dev/null
+++ b/cheriot/riscv_cheriot_vector_opi_instructions.h
@@ -0,0 +1,236 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef MPACT_CHERIOT_RISCV_CHERIOT_VECTOR_OPI_INSTRUCTIONS_H_
+#define MPACT_CHERIOT_RISCV_CHERIOT_VECTOR_OPI_INSTRUCTIONS_H_
+
+#include "mpact/sim/generic/instruction.h"
+
+namespace mpact {
+namespace sim {
+namespace cheriot {
+
+// This file declares the vector instruction semantic functions for most of the
+// vector instructions in the OPIVV, OPIVX, and OPIVI encoding spaces. The
+// exceptions are vector permute instructions and some vector reduction
+// instructions.
+
+using Instruction = ::mpact::sim::generic::Instruction;
+
+// Integer vector operations.
+
+// Element wide vector add. This instruction takes three source operands and
+// a vector destination operand. Source 0 is the vs2 vector source. Source 1
+// is either vs1 (vector), rs1 (scalar), or an immediate. Source 2 is a vector
+// mask operand.
+void Vadd(Instruction *inst);
+// Element wide vector subtract. This instruction takes three source operands
+// and a vector destination operand. Source 0 is the vs2 vector source. Source 1
+// is either vs1 (vector) or rs1 (scalar). Source 2 is a vector
+// mask operand.
+void Vsub(Instruction *inst);
+// Element wide vector reverse subtract. This instruction takes three source
+// operands and a vector destination operand. Source 0 is the vs2 vector source.
+// Source 1 is either rs1 (scalar), or an immediate. Source 2 is a vector mask
+// operand.
+void Vrsub(Instruction *inst);
+// Element wide bitwise and. This instruction takes three source operands and
+// a vector destination operand. Source 0 is the vs2 vector source. Source 1
+// is either vs1 (vector), rs1 (scalar), or an immediate. Source 2 is a vector
+// mask operand.
+void Vand(Instruction *inst);
+// Element wide bitwise or. This instruction takes three source operands and
+// a vector destination operand. Source 0 is the vs2 vector source. Source 1
+// is either vs1 (vector), rs1 (scalar), or an immediate. Source 2 is a vector
+// mask operand.
+void Vor(Instruction *inst);
+// Element wide bitwise xor. This instruction takes three source operands and
+// a vector destination operand. Source 0 is the vs2 vector source. Source 1
+// is either vs1 (vector), rs1 (scalar), or an immediate. Source 2 is a vector
+// mask operand.
+void Vxor(Instruction *inst);
+// Element wide logical left shift. This instruction takes three source operands
+// and a vector destination operand. Source 0 is the vs2 vector source. Source 1
+// is either vs1 (vector), rs1 (scalar), or an immediate. Source 2 is a vector
+// mask operand.
+void Vsll(Instruction *inst);
+// Element wide logical right shift. This instruction takes three source
+// operands and a vector destination operand. Source 0 is the vs2 vector source.
+// Source 1 is either vs1 (vector), rs1 (scalar), or an immediate. Source 2 is a
+// vector mask operand.
+void Vsrl(Instruction *inst);
+// Element wide arithmetic right shift. This instruction takes three source
+// operands and a vector destination operand. Source 0 is the vs2 vector source.
+// Source 1 is either vs1 (vector), rs1 (scalar), or an immediate. Source 2 is a
+// vector mask operand.
+void Vsra(Instruction *inst);
+// Element wide narrowing logical right shift. This instruction takes three
+// source operands and a vector destination operand. Source 0 is the vs2 vector
+// source. Source 1 is either vs1 (vector), rs1 (scalar), or an immediate.
+// Source 2 is a vector mask operand.
+void Vnsrl(Instruction *inst);
+// Element wide narrowing arithmetic right shift. This instruction takes three
+// source operands and a vector destination operand. Source 0 is the vs2 vector
+// source. Source 1 is either vs1 (vector), rs1 (scalar), or an immediate.
+// Source 2 is a vector mask operand.
+void Vnsra(Instruction *inst);
+// Vector signed min (pairwise). This instruction takes three source operands
+// and a vector destination operand. Source 0 is the vs2 vector source. Source 1
+// is either vs1 (vector), rs1 (scalar), or an immediate. Source 2 is a vector
+// mask operand.
+void Vmin(Instruction *inst);
+// Vector unsigned min (pairwise). This instruction takes three source operands
+// and a vector destination operand. Source 0 is the vs2 vector source. Source 1
+// is either vs1 (vector), rs1 (scalar), or an immediate. Source 2 is a vector
+// mask operand.
+void Vminu(Instruction *inst);
+// Vector signed max (pairwise). This instruction takes three source operands
+// and a vector destination operand. Source 0 is the vs2 vector source. Source 1
+// is either vs1 (vector), rs1 (scalar), or an immediate. Source 2 is a vector
+// mask operand.
+void Vmax(Instruction *inst);
+// Vector unsigned max (pairwise). This instruction takes three source operands
+// and a vector destination operand. Source 0 is the vs2 vector source. Source 1
+// is either vs1 (vector), rs1 (scalar), or an immediate. Source 2 is a vector
+// mask operand.
+void Vmaxu(Instruction *inst);
+// Vector mask set equal. This instruction takes three source operands and a
+// vector destination operand. Source 0 is the vs2 vector source. Source 1 is
+// either vs1 (vector), rs1 (scalar), or an immediate. Source 2 is a vector mask
+// operand.
+void Vmseq(Instruction *inst);
+// Vector mask set not equal. This instruction takes three source operands and
+// a vector destination operand. Source 0 is the vs2 vector source. Source 1 is
+// either vs1 (vector), rs1 (scalar), or an immediate. Source 2 is a vector mask
+// operand.
+void Vmsne(Instruction *inst);
+// Vector mask set less than unsigned. This instruction takes three source
+// operands and a vector destination operand. Source 0 is the vs2 vector source.
+// Source 1 is either vs1 (vector), rs1 (scalar), or an immediate. Source 2 is a
+// vector mask operand.
+void Vmsltu(Instruction *inst);
+// Vector mask set less than signed. This instruction takes three source
+// operands and a vector destination operand. Source 0 is the vs2 vector source.
+// Source 1 is either vs1 (vector) or rs1 (scalar). Source 2 is a vector mask
+// operand.
+void Vmslt(Instruction *inst);
+// Vector mask set less or equal unsigned. This instruction takes three source
+// operands and a vector destination operand. Source 0 is the vs2 vector source.
+// Source 1 is either vs1 (vector) or rs1 (scalar). Source 2 is a vector mask
+// operand.
+void Vmsleu(Instruction *inst);
+// Vector mask set less or equal signed. This instruction takes three source
+// operands and a vector destination operand. Source 0 is the vs2 vector source.
+// Source 1 is either vs1 (vector), rs1 (scalar), or an immediate. Source 2 is a
+// vector mask operand.
+void Vmsle(Instruction *inst);
+// Vector mask set greater than unsigned. This instruction takes three source
+// operands and a vector destination operand. Source 0 is the vs2 vector source.
+// Source 1 is either rs1 (scalar), or an immediate. Source 2 is a vector mask
+// operand.
+void Vmsgtu(Instruction *inst);
+// Vector mask set greater than signed. This instruction takes three source
+// operands and a vector destination operand. Source 0 is the vs2 vector source.
+// Source 1 is either rs1 (scalar), or an immediate. Source 2 is a vector mask
+// operand.
+void Vmsgt(Instruction *inst);
+// Vector saturating unsigned add. This instruction takes three source operands
+// and a vector destination operand. Source 0 is the vs2 vector source. Source 1
+// is either vs1 (vector), rs1 (scalar), or an immediate. Source 2 is a vector
+// mask operand.
+void Vsaddu(Instruction *inst);
+// Vector saturating signed add. This instruction takes three source operands
+// and a vector destination operand. Source 0 is the vs2 vector source. Source 1
+// is either vs1 (vector), rs1 (scalar), or an immediate. Source 2 is a vector
+// mask operand.
+void Vsadd(Instruction *inst);
+// Vector saturating unsigned subtrract. This instruction takes three source
+// operands and a vector destination operand. Source 0 is the vs2 vector source.
+// Source 1 is either vs1 (vector), rs1 (scalar), or an immediate. Source 2 is a
+// vector mask operand.
+void Vssubu(Instruction *inst);
+// Vector saturating subtract. This instruction takes three source operands and
+// a vector destination operand. Source 0 is the vs2 vector source. Source 1 is
+// either vs1 (vector), rs1 (scalar), or an immediate. Source 2 is a vector mask
+// operand.
+void Vssub(Instruction *inst);
+// Vector add with carry. This instruction takes three source operands and a
+// vector destination operand. Source 0 is the vs2 vector source. Source 1 is
+// either vs1 (vector), rs1 (scalar), or an immediate. Source 2 is a vector mask
+// operand that contains the carry in values.
+void Vadc(Instruction *inst);
+// Vector add with carry - carry generate. This instruction takes three source
+// operands and a vector destination operand. Source 0 is the vs2 vector source.
+// Source 1 is either vs1 (vector), rs1 (scalar), or an immediate. Source 2 is a
+// vector mask operand that contains the carry in values. The output of this
+// instruction is the carry outs of each element wise addition. It is stored in
+// the format of the vector flags.
+void Vmadc(Instruction *inst);
+// Vector subtract with borrow. This instruction takes three source operands and
+// a vector destination operand. Source 0 is the vs2 vector source. Source 1 is
+// either vs1 (vector), rs1 (scalar), or an immediate. Source 2 is a vector mask
+// operand that contains the borrow values.
+void Vsbc(Instruction *inst);
+// Vector subtract with borrow - borrow generate. This instruction takes three
+// source operands and a vector destination operand. Source 0 is the vs2 vector
+// source. Source 1 is either vs1 (vector), rs1 (scalar), or an immediate.
+// Source 2 is a vector mask operand that contains the borrow values. The output
+// of this instruction is the borrow outs of each element wise subtraction. It
+// is stored in the format of the vector flags.
+void Vmsbc(Instruction *inst);
+// Vector pairwise merge. This instruction takes three source operands and a
+// vector destination operand. Source 0 is the vs2 vector source. Source 1 is
+// either vs1 (vector), rs1 (scalar), or an immediate. Source 2 is a vector mask
+// operand. This semantic function also captures the functionality of vmv.vv,
+// vmv.vx, and vmv.vi, in which case vs2 is register group v0, and the mask
+// is all ones.
+void Vmerge(Instruction *inst);
+// Vector register move. This instruction takes one source operands and a
+// vector destination operand. Source 0 is the vs2 vector source. The num_regs
+// value is part of the opcode and should be bound to the semantic function at
+// decode.
+void Vmvr(int num_regs, Instruction *inst);
+// Vector logical right shift with rounding. This instruction takes three
+// source operands and a vector destination operand. Source 0 is the vs2 vector
+// source. Source 1 is either vs1 (vector), rs1 (scalar), or an immediate.
+// Source 2 is a vector mask operand.
+void Vssrl(Instruction *inst);
+// Vector arithmetic right shift with rounding. This instruction takes three
+// source operands and a vector destination operand. Source 0 is the vs2 vector
+// source. Source 1 is either vs1 (vector), rs1 (scalar), or an immediate.
+// Source 2 is a vector mask operand.
+void Vssra(Instruction *inst);
+// Vector logical right shift with rounding and (unsigned) saturation from SEW *
+// 2 to SEW wide elements. This instruction takes three
+// source operands and a vector destination operand. Source 0 is the vs2 vector
+// source. Source 1 is either vs1 (vector), rs1 (scalar), or an immediate.
+// Source 2 is a vector mask operand.
+void Vnclipu(Instruction *inst);
+// Vector arithmetic right shift with rounding and (signed) saturation from SEW
+// * 2 to SEW wide elements. This instruction takes three
+// source operands and a vector destination operand. Source 0 is the vs2 vector
+// source. Source 1 is either vs1 (vector), rs1 (scalar), or an immediate.
+// Source 2 is a vector mask operand.
+void Vnclip(Instruction *inst);
+// Vector fractional multiply. This instruction takes three
+// source operands and a vector destination operand. Source 0 is the vs2 vector
+// source. Source 1 is either vs1 (vector) or rs1 (scalar). Source 2 is a vector
+// mask operand.
+void Vsmul(Instruction *inst);
+
+} // namespace cheriot
+} // namespace sim
+} // namespace mpact
+
+#endif // MPACT_CHERIOT_RISCV_CHERIOT_VECTOR_OPI_INSTRUCTIONS_H_
diff --git a/cheriot/riscv_cheriot_vector_opm_instructions.cc b/cheriot/riscv_cheriot_vector_opm_instructions.cc
new file mode 100644
index 0000000..78617ef
--- /dev/null
+++ b/cheriot/riscv_cheriot_vector_opm_instructions.cc
@@ -0,0 +1,1302 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cheriot/riscv_cheriot_vector_opm_instructions.h"
+
+#include <cstdint>
+#include <functional>
+#include <limits>
+#include <type_traits>
+
+#include "absl/log/log.h"
+#include "cheriot/cheriot_state.h"
+#include "cheriot/cheriot_vector_state.h"
+#include "cheriot/riscv_cheriot_vector_instruction_helpers.h"
+#include "mpact/sim/generic/type_helpers.h"
+#include "riscv//riscv_register.h"
+
+namespace mpact {
+namespace sim {
+namespace cheriot {
+
+using ::mpact::sim::generic::WideType;
+
+// Helper function used to factor out some code from Vaadd* instructions.
+template <typename T>
+inline T VaaddHelper(CheriotVectorState *rv_vector, T vs2, T vs1) {
+ // Perform the addition using a wider type, then shift and round.
+ using WT = typename WideType<T>::type;
+ WT vs2_w = static_cast<WT>(vs2);
+ WT vs1_w = static_cast<WT>(vs1);
+ auto res = RoundOff(rv_vector, vs2_w + vs1_w, 1);
+ return static_cast<T>(res);
+}
+
+// Average unsigned add. The two sources are added, then shifted right by one
+// and rounded.
+void Vaaddu(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorOp<uint8_t, uint8_t, uint8_t>(
+ rv_vector, inst, [rv_vector](uint8_t vs2, uint8_t vs1) -> uint8_t {
+ return VaaddHelper(rv_vector, vs2, vs1);
+ });
+ case 2:
+ return RiscVBinaryVectorOp<uint16_t, uint16_t, uint16_t>(
+ rv_vector, inst, [rv_vector](uint16_t vs2, uint16_t vs1) -> uint16_t {
+ return VaaddHelper(rv_vector, vs2, vs1);
+ });
+ case 4:
+ return RiscVBinaryVectorOp<uint32_t, uint32_t, uint32_t>(
+ rv_vector, inst, [rv_vector](uint32_t vs2, uint32_t vs1) -> uint32_t {
+ return VaaddHelper(rv_vector, vs2, vs1);
+ });
+ case 8:
+ return RiscVBinaryVectorOp<uint64_t, uint64_t, uint64_t>(
+ rv_vector, inst, [rv_vector](uint64_t vs2, uint64_t vs1) -> uint64_t {
+ return VaaddHelper(rv_vector, vs2, vs1);
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Average signed add. The two sources are added, then shifted right by one and
+// rounded.
+void Vaadd(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorOp<int8_t, int8_t, int8_t>(
+ rv_vector, inst, [rv_vector](int8_t vs2, int8_t vs1) -> int8_t {
+ return VaaddHelper(rv_vector, vs2, vs1);
+ });
+ case 2:
+ return RiscVBinaryVectorOp<int16_t, int16_t, int16_t>(
+ rv_vector, inst, [rv_vector](int16_t vs2, int16_t vs1) -> int16_t {
+ return VaaddHelper(rv_vector, vs2, vs1);
+ });
+ case 4:
+ return RiscVBinaryVectorOp<int32_t, int32_t, int32_t>(
+ rv_vector, inst, [rv_vector](int32_t vs2, int32_t vs1) -> int32_t {
+ return VaaddHelper(rv_vector, vs2, vs1);
+ });
+ case 8:
+ return RiscVBinaryVectorOp<int64_t, int64_t, int64_t>(
+ rv_vector, inst, [rv_vector](int64_t vs2, int64_t vs1) -> int64_t {
+ return VaaddHelper(rv_vector, vs2, vs1);
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Helper function for Vasub* instructions. Subract using a wider type, then
+// round.
+template <typename T>
+inline T VasubHelper(CheriotVectorState *rv_vector, T vs2, T vs1) {
+ using WT = typename WideType<T>::type;
+ WT vs2_w = static_cast<WT>(vs2);
+ WT vs1_w = static_cast<WT>(vs1);
+ auto res = RoundOff(rv_vector, vs2_w - vs1_w, 1);
+ return static_cast<T>(res);
+}
+
+// Averaging unsigned subtract - subtract then shift right by 1 and round.
+void Vasubu(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorOp<uint8_t, uint8_t, uint8_t>(
+ rv_vector, inst, [rv_vector](uint8_t vs2, uint8_t vs1) -> uint8_t {
+ return VasubHelper(rv_vector, vs2, vs1);
+ });
+ case 2:
+ return RiscVBinaryVectorOp<uint16_t, uint16_t, uint16_t>(
+ rv_vector, inst, [rv_vector](uint16_t vs2, uint16_t vs1) -> uint16_t {
+ return VasubHelper(rv_vector, vs2, vs1);
+ });
+ case 4:
+ return RiscVBinaryVectorOp<uint32_t, uint32_t, uint32_t>(
+ rv_vector, inst, [rv_vector](uint32_t vs2, uint32_t vs1) -> uint32_t {
+ return VasubHelper(rv_vector, vs2, vs1);
+ });
+ case 8:
+ return RiscVBinaryVectorOp<uint64_t, uint64_t, uint64_t>(
+ rv_vector, inst, [rv_vector](uint64_t vs2, uint64_t vs1) -> uint64_t {
+ return VasubHelper(rv_vector, vs2, vs1);
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Averaging signed subtract. Subtract then shift right by 1 and round.
+void Vasub(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorOp<int8_t, int8_t, int8_t>(
+ rv_vector, inst, [rv_vector](int8_t vs2, int8_t vs1) -> int8_t {
+ return VasubHelper(rv_vector, vs2, vs1);
+ });
+ case 2:
+ return RiscVBinaryVectorOp<int16_t, int16_t, int16_t>(
+ rv_vector, inst, [rv_vector](int16_t vs2, int16_t vs1) -> int16_t {
+ return VasubHelper(rv_vector, vs2, vs1);
+ });
+ case 4:
+ return RiscVBinaryVectorOp<int32_t, int32_t, int32_t>(
+ rv_vector, inst, [rv_vector](int32_t vs2, int32_t vs1) -> int32_t {
+ return VasubHelper(rv_vector, vs2, vs1);
+ });
+ case 8:
+ return RiscVBinaryVectorOp<int64_t, int64_t, int64_t>(
+ rv_vector, inst, [rv_vector](int64_t vs2, int64_t vs1) -> int64_t {
+ return VasubHelper(rv_vector, vs2, vs1);
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Mask operands only operate on a single vector register. This helper function
+// is used by the following bitwise mask manipulation instruction semantic
+// functions.
+static inline void BitwiseMaskBinaryOp(
+ CheriotVectorState *rv_vector, const Instruction *inst,
+ std::function<uint8_t(uint8_t, uint8_t)> op) {
+ if (rv_vector->vector_exception()) return;
+ int vstart = rv_vector->vstart();
+ int vlen = rv_vector->vector_length();
+ // Get spans for vector source and destination registers.
+ auto *vs2_op = static_cast<RV32VectorSourceOperand *>(inst->Source(0));
+ auto vs2_span = vs2_op->GetRegister(0)->data_buffer()->Get<uint8_t>();
+ auto *vs1_op = static_cast<RV32VectorSourceOperand *>(inst->Source(1));
+ auto vs1_span = vs1_op->GetRegister(0)->data_buffer()->Get<uint8_t>();
+ auto *vd_op =
+ static_cast<RV32VectorDestinationOperand *>(inst->Destination(0));
+ auto *vd_db = vd_op->CopyDataBuffer();
+ auto vd_span = vd_db->Get<uint8_t>();
+ // Compute start and end locations.
+ int start_byte = vstart / 8;
+ int start_offset = vstart % 8;
+ uint8_t start_mask = 0b1111'1111 << start_offset;
+ int end_byte = (vlen - 1) / 8;
+ int end_offset = (vlen - 1) % 8;
+ uint8_t end_mask = 0b1111'1111 >> (7 - end_offset);
+ // The start byte is computed first, applying a mask to mask out any preceding
+ // bits.
+ vd_span[start_byte] =
+ (op(vs2_span[start_byte], vs1_span[start_byte]) & start_mask) |
+ (vd_span[start_byte] & ~start_mask);
+ // Perform the bitwise operation on each byte between start and end.
+ for (int i = start_byte + 1; i < end_byte; i++) {
+ vd_span[i] = op(vs2_span[i], vs1_span[i]);
+ }
+ // Perform the bitwise operation with a mask on the end byte.
+ vd_span[end_byte] = (op(vs2_span[end_byte], vs1_span[end_byte]) & end_mask) |
+ (vd_span[end_byte] & ~end_mask);
+ vd_db->Submit();
+ rv_vector->clear_vstart();
+}
+
+// Bitwise vector mask instructions. The operation is clear by their name.
+void Vmandnot(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ BitwiseMaskBinaryOp(rv_vector, inst, [](uint8_t vs2, uint8_t vs1) -> uint8_t {
+ return vs2 & ~vs1;
+ });
+}
+
+void Vmand(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ BitwiseMaskBinaryOp(rv_vector, inst, [](uint8_t vs2, uint8_t vs1) -> uint8_t {
+ return vs2 & vs1;
+ });
+}
+void Vmor(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ BitwiseMaskBinaryOp(rv_vector, inst, [](uint8_t vs2, uint8_t vs1) -> uint8_t {
+ return vs2 | vs1;
+ });
+}
+void Vmxor(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ BitwiseMaskBinaryOp(rv_vector, inst, [](uint8_t vs2, uint8_t vs1) -> uint8_t {
+ return vs2 ^ vs1;
+ });
+}
+void Vmornot(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ BitwiseMaskBinaryOp(rv_vector, inst, [](uint8_t vs2, uint8_t vs1) -> uint8_t {
+ return vs2 | ~vs1;
+ });
+}
+void Vmnand(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ BitwiseMaskBinaryOp(rv_vector, inst, [](uint8_t vs2, uint8_t vs1) -> uint8_t {
+ return ~(vs2 & vs1);
+ });
+}
+void Vmnor(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ BitwiseMaskBinaryOp(rv_vector, inst, [](uint8_t vs2, uint8_t vs1) -> uint8_t {
+ return ~(vs2 | vs1);
+ });
+}
+void Vmxnor(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ BitwiseMaskBinaryOp(rv_vector, inst, [](uint8_t vs2, uint8_t vs1) -> uint8_t {
+ return ~(vs2 ^ vs1);
+ });
+}
+
+// Vector unsigned divide. Note, just like the scalar divide instruction, a
+// divide by zero does not cause an exception, instead it returns all 1s.
+void Vdivu(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorOp<uint8_t, uint8_t, uint8_t>(
+ rv_vector, inst, [](uint8_t vs2, uint8_t vs1) -> uint8_t {
+ if (vs1 == 0) return ~vs1;
+ return vs2 / vs1;
+ });
+ case 2:
+ return RiscVBinaryVectorOp<uint16_t, uint16_t, uint16_t>(
+ rv_vector, inst, [](uint16_t vs2, uint16_t vs1) -> uint16_t {
+ if (vs1 == 0) return ~vs1;
+ return vs2 / vs1;
+ });
+ case 4:
+ return RiscVBinaryVectorOp<uint32_t, uint32_t, uint32_t>(
+ rv_vector, inst, [](uint32_t vs2, uint32_t vs1) -> uint32_t {
+ if (vs1 == 0) return ~vs1;
+ return vs2 / vs1;
+ });
+ case 8:
+ return RiscVBinaryVectorOp<uint64_t, uint64_t, uint64_t>(
+ rv_vector, inst, [](uint64_t vs2, uint64_t vs1) -> uint64_t {
+ if (vs1 == 0) return ~vs1;
+ return vs2 / vs1;
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Signed divide. Divide by 0 returns all 1s. If -1 is divided by the largest
+// magnitude negative number, it returns that negative number.
+void Vdiv(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorOp<int8_t, int8_t, int8_t>(
+ rv_vector, inst, [](int8_t vs2, int8_t vs1) -> int8_t {
+ if (vs1 == 0) return static_cast<int8_t>(-1);
+ if ((vs1 == -1) && (vs2 == std::numeric_limits<int8_t>::min())) {
+ return std::numeric_limits<int8_t>::min();
+ }
+ return vs2 / vs1;
+ });
+ case 2:
+ return RiscVBinaryVectorOp<int16_t, int16_t, int16_t>(
+ rv_vector, inst, [](int16_t vs2, int16_t vs1) -> int16_t {
+ if (vs1 == 0) return static_cast<int16_t>(-1);
+ if ((vs1 == -1) && (vs2 == std::numeric_limits<int16_t>::min())) {
+ return std::numeric_limits<int16_t>::min();
+ }
+ return vs2 / vs1;
+ });
+ case 4:
+ return RiscVBinaryVectorOp<int32_t, int32_t, int32_t>(
+ rv_vector, inst, [](int32_t vs2, int32_t vs1) -> int32_t {
+ if (vs1 == 0) return static_cast<int32_t>(-1);
+ if ((vs1 == -1) && (vs2 == std::numeric_limits<int32_t>::min())) {
+ return std::numeric_limits<int32_t>::min();
+ }
+ return vs2 / vs1;
+ });
+ case 8:
+ return RiscVBinaryVectorOp<int64_t, int64_t, int64_t>(
+ rv_vector, inst, [](int64_t vs2, int64_t vs1) -> int64_t {
+ if (vs1 == 0) return static_cast<int64_t>(-1);
+ if ((vs1 == -1) && (vs2 == std::numeric_limits<int64_t>::min())) {
+ return std::numeric_limits<int64_t>::min();
+ }
+ return vs2 / vs1;
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Unsigned remainder. If the denominator is 0, it returns the enumerator.
+void Vremu(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorOp<uint8_t, uint8_t, uint8_t>(
+ rv_vector, inst, [](uint8_t vs2, uint8_t vs1) -> uint8_t {
+ if (vs1 == 0) return vs2;
+ return vs2 % vs1;
+ });
+ case 2:
+ return RiscVBinaryVectorOp<uint16_t, uint16_t, uint16_t>(
+ rv_vector, inst, [](uint16_t vs2, uint16_t vs1) -> uint16_t {
+ if (vs1 == 0) return vs2;
+ return vs2 % vs1;
+ });
+ case 4:
+ return RiscVBinaryVectorOp<uint32_t, uint32_t, uint32_t>(
+ rv_vector, inst, [](uint32_t vs2, uint32_t vs1) -> uint32_t {
+ if (vs1 == 0) return vs2;
+ return vs2 % vs1;
+ });
+ case 8:
+ return RiscVBinaryVectorOp<uint64_t, uint64_t, uint64_t>(
+ rv_vector, inst, [](uint64_t vs2, uint64_t vs1) -> uint64_t {
+ if (vs1 == 0) return vs2;
+ return vs2 % vs1;
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Signed remainder. If the denominator is 0, it returns the enumerator.
+void Vrem(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorOp<int8_t, int8_t, int8_t>(
+ rv_vector, inst, [](int8_t vs2, int8_t vs1) -> int8_t {
+ if (vs1 == 0) return vs2;
+ return vs2 % vs1;
+ });
+ case 2:
+ return RiscVBinaryVectorOp<int16_t, int16_t, int16_t>(
+ rv_vector, inst, [](int16_t vs2, int16_t vs1) -> int16_t {
+ if (vs1 == 0) return vs2;
+ return vs2 % vs1;
+ });
+ case 4:
+ return RiscVBinaryVectorOp<int32_t, int32_t, int32_t>(
+ rv_vector, inst, [](int32_t vs2, int32_t vs1) -> int32_t {
+ if (vs1 == 0) return vs2;
+ return vs2 % vs1;
+ });
+ case 8:
+ return RiscVBinaryVectorOp<int64_t, int64_t, int64_t>(
+ rv_vector, inst, [](int64_t vs2, int64_t vs1) -> int64_t {
+ if (vs1 == 0) return vs2;
+ return vs2 % vs1;
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Helper function for multiply high. It promotes the to arguments to wider
+// types, performs the multiplication, returns the high half of the result.
+template <typename T>
+inline T VmulHighHelper(T vs2, T vs1) {
+ using WT = typename WideType<T>::type;
+ WT vs2_w = static_cast<WT>(vs2);
+ WT vs1_w = static_cast<WT>(vs1);
+ WT prod = vs2_w * vs1_w;
+ prod >>= sizeof(T) * 8;
+ return static_cast<T>(prod);
+}
+
+// Multiply high, unsigned.
+void Vmulhu(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorOp<uint8_t, uint8_t, uint8_t>(
+ rv_vector, inst, [](uint8_t vs2, uint8_t vs1) -> uint8_t {
+ return VmulHighHelper(vs2, vs1);
+ });
+ case 2:
+ return RiscVBinaryVectorOp<uint16_t, uint16_t, uint16_t>(
+ rv_vector, inst, [](uint16_t vs2, uint16_t vs1) -> uint16_t {
+ return VmulHighHelper(vs2, vs1);
+ });
+ case 4:
+ return RiscVBinaryVectorOp<uint32_t, uint32_t, uint32_t>(
+ rv_vector, inst, [](uint32_t vs2, uint32_t vs1) -> uint32_t {
+ return VmulHighHelper(vs2, vs1);
+ });
+ case 8:
+ return RiscVBinaryVectorOp<uint64_t, uint64_t, uint64_t>(
+ rv_vector, inst, [](uint64_t vs2, uint64_t vs1) -> uint64_t {
+ return VmulHighHelper(vs2, vs1);
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Signed multiply. Note, that signed and unsigned multiply operations have the
+// same result for the low half of the product.
+void Vmul(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorOp<uint8_t, uint8_t, uint8_t>(
+ rv_vector, inst,
+ [](uint8_t vs2, uint8_t vs1) -> uint8_t { return vs2 * vs1; });
+ case 2:
+ return RiscVBinaryVectorOp<uint16_t, uint16_t, uint16_t>(
+ rv_vector, inst, [](uint16_t vs2, uint16_t vs1) -> uint16_t {
+ uint32_t vs2_32 = vs2;
+ uint32_t vs1_32 = vs1;
+ return static_cast<uint16_t>(vs2_32 * vs1_32);
+ });
+ case 4:
+ return RiscVBinaryVectorOp<uint32_t, uint32_t, uint32_t>(
+ rv_vector, inst,
+ [](uint32_t vs2, uint32_t vs1) -> uint32_t { return vs2 * vs1; });
+ case 8:
+ // The 64 bit version is treated a little differently. Because the vs1
+ // operand may come from a register which may be 32 bits wide, it's first
+ // converted to int64_t. Then the product is done on unsigned numbers to
+ // avoid a signed multiply overflow, and returned as a signed number.
+ return RiscVBinaryVectorOp<int64_t, int64_t, int64_t>(
+ rv_vector, inst, [](int64_t vs2, int64_t vs1) -> int64_t {
+ uint64_t vs2_u = vs2;
+ uint64_t vs1_u = vs1;
+ uint64_t prod = vs2_u * vs1_u;
+ return prod;
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Helper for signed-unsigned multiplication return high half.
+template <typename T>
+inline typename std::make_signed<T>::type VmulHighSUHelper(
+ typename std::make_signed<T>::type vs2,
+ typename std::make_unsigned<T>::type vs1) {
+ using WT = typename WideType<T>::type;
+ using WST = typename WideType<typename std::make_signed<T>::type>::type;
+ WST vs2_w = static_cast<WST>(vs2);
+ WT vs1_w = static_cast<WT>(vs1);
+ WST prod = vs2_w * vs1_w;
+ prod >>= sizeof(T) * 8;
+ return static_cast<typename std::make_signed<T>::type>(prod);
+}
+
+// Multiply signed unsigned and return the high half.
+void Vmulhsu(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorOp<int8_t, int8_t, uint8_t>(
+ rv_vector, inst, [](int8_t vs2, uint8_t vs1) -> int8_t {
+ return VmulHighSUHelper<int8_t>(vs2, vs1);
+ });
+ case 2:
+ return RiscVBinaryVectorOp<int16_t, int16_t, uint16_t>(
+ rv_vector, inst, [](int16_t vs2, uint16_t vs1) -> int16_t {
+ return VmulHighSUHelper<int16_t>(vs2, vs1);
+ });
+ case 4:
+ return RiscVBinaryVectorOp<int32_t, int32_t, uint32_t>(
+ rv_vector, inst, [](int32_t vs2, uint32_t vs1) -> int32_t {
+ return VmulHighSUHelper<int32_t>(vs2, vs1);
+ });
+ case 8:
+ return RiscVBinaryVectorOp<int64_t, int64_t, uint64_t>(
+ rv_vector, inst, [](int64_t vs2, uint64_t vs1) -> int64_t {
+ return VmulHighSUHelper<int64_t>(vs2, vs1);
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Signed multiply, return high half.
+void Vmulh(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorOp<int8_t, int8_t, int8_t>(
+ rv_vector, inst, [](int8_t vs2, int8_t vs1) -> int8_t {
+ return VmulHighHelper(vs2, vs1);
+ });
+ case 2:
+ return RiscVBinaryVectorOp<int16_t, int16_t, int16_t>(
+ rv_vector, inst, [](int16_t vs2, int16_t vs1) -> int16_t {
+ return VmulHighHelper(vs2, vs1);
+ });
+ case 4:
+ return RiscVBinaryVectorOp<int32_t, int32_t, int32_t>(
+ rv_vector, inst, [](int32_t vs2, int32_t vs1) -> int32_t {
+ return VmulHighHelper(vs2, vs1);
+ });
+ case 8:
+ return RiscVBinaryVectorOp<int64_t, int64_t, int64_t>(
+ rv_vector, inst, [](int64_t vs2, int64_t vs1) -> int64_t {
+ return VmulHighHelper(vs2, vs1);
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Multiply-add.
+void Vmadd(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVTernaryVectorOp<uint8_t, uint8_t, uint8_t>(
+ rv_vector, inst, [](uint8_t vs2, uint8_t vs1, uint8_t vd) -> uint8_t {
+ uint8_t prod = vs1 * vd;
+ return prod + vs2;
+ });
+ case 2:
+ return RiscVTernaryVectorOp<uint16_t, uint16_t, uint16_t>(
+ rv_vector, inst,
+ [](uint16_t vs2, uint16_t vs1, uint16_t vd) -> uint16_t {
+ uint32_t vs2_32 = vs2;
+ uint32_t vs1_32 = vs1;
+ uint32_t vd_32 = vd;
+ return static_cast<uint16_t>(vs1_32 * vd_32 + vs2_32);
+ });
+ case 4:
+ return RiscVTernaryVectorOp<uint32_t, uint32_t, uint32_t>(
+ rv_vector, inst,
+ [](uint32_t vs2, uint32_t vs1, uint32_t vd) -> uint32_t {
+ return vs1 * vd + vs2;
+ });
+ case 8:
+ return RiscVTernaryVectorOp<uint64_t, uint64_t, uint64_t>(
+ rv_vector, inst,
+ [](uint64_t vs2, uint64_t vs1, uint64_t vd) -> uint64_t {
+ return vs1 * vd + vs2;
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Negated multiply and add.
+void Vnmsub(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVTernaryVectorOp<uint8_t, uint8_t, uint8_t>(
+ rv_vector, inst, [](uint8_t vs2, uint8_t vs1, uint8_t vd) -> uint8_t {
+ return -(vs1 * vd) + vs2;
+ });
+ case 2:
+ return RiscVTernaryVectorOp<uint16_t, uint16_t, uint16_t>(
+ rv_vector, inst,
+ [](uint16_t vs2, uint16_t vs1, uint16_t vd) -> uint16_t {
+ uint32_t vs2_32 = vs2;
+ uint32_t vs1_32 = vs1;
+ uint32_t vd_32 = vd;
+ return static_cast<uint16_t>(-(vs1_32 * vd_32) + vs2_32);
+ });
+ case 4:
+ return RiscVTernaryVectorOp<uint32_t, uint32_t, uint32_t>(
+ rv_vector, inst,
+ [](uint32_t vs2, uint32_t vs1, uint32_t vd) -> uint32_t {
+ return -(vs1 * vd) + vs2;
+ });
+ case 8:
+ return RiscVTernaryVectorOp<uint64_t, uint64_t, uint64_t>(
+ rv_vector, inst,
+ [](uint64_t vs2, uint64_t vs1, uint64_t vd) -> uint64_t {
+ return -(vs1 * vd) + vs2;
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Multiply add overwriting the sum.
+void Vmacc(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVTernaryVectorOp<uint8_t, uint8_t, uint8_t>(
+ rv_vector, inst, [](uint8_t vs2, uint8_t vs1, uint8_t vd) -> uint8_t {
+ return vs1 * vs2 + vd;
+ });
+ case 2:
+ return RiscVTernaryVectorOp<uint16_t, uint16_t, uint16_t>(
+ rv_vector, inst,
+ [](uint16_t vs2, uint16_t vs1, uint16_t vd) -> uint16_t {
+ uint32_t vs2_32 = vs2;
+ uint32_t vs1_32 = vs1;
+ uint32_t vd_32 = vd;
+ return static_cast<uint16_t>(vs1_32 * vs2_32 + vd_32);
+ });
+ case 4:
+ return RiscVTernaryVectorOp<uint32_t, uint32_t, uint32_t>(
+ rv_vector, inst,
+ [](uint32_t vs2, uint32_t vs1, uint32_t vd) -> uint32_t {
+ return vs1 * vs2 + vd;
+ });
+ case 8:
+ return RiscVTernaryVectorOp<uint64_t, uint64_t, uint64_t>(
+ rv_vector, inst,
+ [](uint64_t vs2, uint64_t vs1, uint64_t vd) -> uint64_t {
+ return vs1 * vs2 + vd;
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Negated multiply add, overwriting sum.
+void Vnmsac(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVTernaryVectorOp<uint8_t, uint8_t, uint8_t>(
+ rv_vector, inst, [](uint8_t vs2, uint8_t vs1, uint8_t vd) -> uint8_t {
+ return -(vs1 * vs2) + vd;
+ });
+ case 2:
+ return RiscVTernaryVectorOp<uint16_t, uint16_t, uint16_t>(
+ rv_vector, inst,
+ [](uint16_t vs2, uint16_t vs1, uint16_t vd) -> uint16_t {
+ uint32_t vs2_32 = vs2;
+ uint32_t vs1_32 = vs1;
+ uint32_t vd_32 = vd;
+ return static_cast<uint16_t>(-(vs1_32 * vs2_32) + vd_32);
+ });
+ case 4:
+ return RiscVTernaryVectorOp<uint32_t, uint32_t, uint32_t>(
+ rv_vector, inst,
+ [](uint32_t vs2, uint32_t vs1, uint32_t vd) -> uint32_t {
+ return -(vs1 * vs2) + vd;
+ });
+ case 8:
+ return RiscVTernaryVectorOp<uint64_t, uint64_t, uint64_t>(
+ rv_vector, inst,
+ [](uint64_t vs2, uint64_t vs1, uint64_t vd) -> uint64_t {
+ return -(vs1 * vs2) + vd;
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Widening unsigned add.
+void Vwaddu(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorOp<uint16_t, uint8_t, uint8_t>(
+ rv_vector, inst, [](uint8_t vs2, uint8_t vs1) -> uint16_t {
+ return static_cast<uint16_t>(vs2) + static_cast<uint16_t>(vs1);
+ });
+ case 2:
+ return RiscVBinaryVectorOp<uint32_t, uint16_t, uint16_t>(
+ rv_vector, inst, [](uint16_t vs2, uint16_t vs1) -> uint32_t {
+ return static_cast<uint32_t>(vs2) + static_cast<uint32_t>(vs1);
+ });
+ case 4:
+ return RiscVBinaryVectorOp<uint64_t, uint32_t, uint32_t>(
+ rv_vector, inst, [](uint32_t vs2, uint32_t vs1) -> uint64_t {
+ return static_cast<uint64_t>(vs2) + static_cast<uint64_t>(vs1);
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Widening unsigned subtract.
+void Vwsubu(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorOp<uint16_t, uint8_t, uint8_t>(
+ rv_vector, inst, [](uint8_t vs2, uint8_t vs1) -> uint16_t {
+ return static_cast<uint16_t>(vs2) - static_cast<uint16_t>(vs1);
+ });
+ case 2:
+ return RiscVBinaryVectorOp<uint32_t, uint16_t, uint16_t>(
+ rv_vector, inst, [](uint16_t vs2, uint16_t vs1) -> uint32_t {
+ return static_cast<uint32_t>(vs2) - static_cast<uint32_t>(vs1);
+ });
+ case 4:
+ return RiscVBinaryVectorOp<uint64_t, uint32_t, uint32_t>(
+ rv_vector, inst, [](uint32_t vs2, uint32_t vs1) -> uint64_t {
+ return static_cast<uint64_t>(vs2) - static_cast<uint64_t>(vs1);
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Widening signed add.
+void Vwadd(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ // LMUL8 cannot be 64.
+ if (rv_vector->vector_length_multiplier() > 32) {
+ rv_vector->set_vector_exception();
+ LOG(ERROR)
+ << "Vector length multiplier out of range for widening operation";
+ return;
+ }
+ // The values are first sign extended to the wide signed value, then
+ // an unsigned addition is performed, for which overflow is not undefined,
+ // as opposed to signed additions.
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorOp<uint16_t, int8_t, int8_t>(
+ rv_vector, inst, [](int8_t vs2, int8_t vs1) -> uint16_t {
+ return static_cast<uint16_t>(static_cast<int16_t>(vs2)) +
+ static_cast<uint16_t>(static_cast<int16_t>(vs1));
+ });
+ case 2:
+ return RiscVBinaryVectorOp<uint32_t, int16_t, int16_t>(
+ rv_vector, inst, [](int16_t vs2, int16_t vs1) -> uint32_t {
+ return static_cast<uint32_t>(static_cast<int32_t>(vs2)) +
+ static_cast<uint32_t>(static_cast<int32_t>(vs1));
+ });
+ case 4:
+ return RiscVBinaryVectorOp<uint64_t, int32_t, int32_t>(
+ rv_vector, inst, [](int32_t vs2, int32_t vs1) -> uint64_t {
+ return static_cast<uint64_t>(static_cast<int64_t>(vs2)) +
+ static_cast<uint64_t>(static_cast<int64_t>(vs1));
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Widening signed subtract.
+void Vwsub(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ // LMUL8 cannot be 64.
+ if (rv_vector->vector_length_multiplier() > 32) {
+ rv_vector->set_vector_exception();
+ LOG(ERROR)
+ << "Vector length multiplier out of range for widening operation";
+ return;
+ } // The values are first sign extended to the wide signed value, then
+ // an unsigned subtraction is performed, for which overflow is not undefined,
+ // as opposed to signed subtraction.
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorOp<uint16_t, int8_t, int8_t>(
+ rv_vector, inst, [](int8_t vs2, int8_t vs1) -> uint16_t {
+ return static_cast<uint16_t>(static_cast<int16_t>(vs2)) -
+ static_cast<uint16_t>(static_cast<int16_t>(vs1));
+ });
+ case 2:
+ return RiscVBinaryVectorOp<uint32_t, int16_t, int16_t>(
+ rv_vector, inst, [](int16_t vs2, int16_t vs1) -> uint32_t {
+ return static_cast<uint32_t>(static_cast<int32_t>(vs2)) -
+ static_cast<uint32_t>(static_cast<int32_t>(vs1));
+ });
+ case 4:
+ return RiscVBinaryVectorOp<uint64_t, int32_t, int32_t>(
+ rv_vector, inst, [](int32_t vs2, int32_t vs1) -> uint64_t {
+ return static_cast<uint64_t>(static_cast<int64_t>(vs2)) -
+ static_cast<uint64_t>(static_cast<int64_t>(vs1));
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Widening unsigned add with wide source.
+void Vwadduw(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ // LMUL8 cannot be 64.
+ if (rv_vector->vector_length_multiplier() > 32) {
+ rv_vector->set_vector_exception();
+ LOG(ERROR)
+ << "Vector length multiplier out of range for widening operation";
+ return;
+ }
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorOp<uint16_t, uint16_t, uint8_t>(
+ rv_vector, inst, [](uint16_t vs2, uint8_t vs1) -> uint16_t {
+ return vs2 + static_cast<uint16_t>(vs1);
+ });
+ case 2:
+ return RiscVBinaryVectorOp<uint32_t, uint32_t, uint16_t>(
+ rv_vector, inst, [](uint32_t vs2, uint16_t vs1) -> uint32_t {
+ return vs2 + static_cast<uint32_t>(vs1);
+ });
+ case 4:
+ return RiscVBinaryVectorOp<uint64_t, uint64_t, uint32_t>(
+ rv_vector, inst, [](uint64_t vs2, uint32_t vs1) -> uint64_t {
+ return vs2 + static_cast<uint64_t>(vs1);
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Widening unsigned subtract with wide source.
+void Vwsubuw(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ // LMUL8 cannot be 64.
+ if (rv_vector->vector_length_multiplier() > 32) {
+ rv_vector->set_vector_exception();
+ LOG(ERROR)
+ << "Vector length multiplier out of range for widening operation";
+ return;
+ }
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorOp<uint16_t, uint16_t, uint8_t>(
+ rv_vector, inst, [](uint16_t vs2, uint8_t vs1) -> uint16_t {
+ return vs2 - static_cast<uint16_t>(vs1);
+ });
+ case 2:
+ return RiscVBinaryVectorOp<uint32_t, uint32_t, uint16_t>(
+ rv_vector, inst, [](uint32_t vs2, uint16_t vs1) -> uint32_t {
+ return vs2 - static_cast<uint32_t>(vs1);
+ });
+ case 4:
+ return RiscVBinaryVectorOp<uint64_t, uint64_t, uint32_t>(
+ rv_vector, inst, [](uint64_t vs2, uint32_t vs1) -> uint64_t {
+ return vs2 - static_cast<uint64_t>(vs1);
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Widening signed add with wide source.
+void Vwaddw(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ // LMUL8 cannot be 64.
+ if (rv_vector->vector_length_multiplier() > 32) {
+ rv_vector->set_vector_exception();
+ LOG(ERROR)
+ << "Vector length multiplier out of range for widening operation";
+ return;
+ }
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorOp<int16_t, uint16_t, int8_t>(
+ rv_vector, inst, [](uint16_t vs2, int8_t vs1) -> uint16_t {
+ return vs2 + static_cast<uint16_t>(static_cast<int16_t>(vs1));
+ });
+ case 2:
+ return RiscVBinaryVectorOp<uint32_t, uint32_t, int16_t>(
+ rv_vector, inst, [](uint32_t vs2, int16_t vs1) -> uint32_t {
+ return vs2 + static_cast<uint32_t>(static_cast<int32_t>(vs1));
+ });
+ case 4:
+ return RiscVBinaryVectorOp<uint64_t, uint64_t, int32_t>(
+ rv_vector, inst, [](uint64_t vs2, int32_t vs1) -> uint64_t {
+ return vs2 + static_cast<uint64_t>(static_cast<int64_t>(vs1));
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Widening signed subtract with wide source.
+void Vwsubw(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ // LMUL8 cannot be 64.
+ if (rv_vector->vector_length_multiplier() > 32) {
+ rv_vector->set_vector_exception();
+ LOG(ERROR)
+ << "Vector length multiplier out of range for widening operation";
+ return;
+ }
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorOp<uint16_t, uint16_t, int8_t>(
+ rv_vector, inst, [](uint16_t vs2, int8_t vs1) -> uint16_t {
+ return vs2 - static_cast<uint16_t>(static_cast<int16_t>(vs1));
+ });
+ case 2:
+ return RiscVBinaryVectorOp<uint32_t, uint32_t, int16_t>(
+ rv_vector, inst, [](uint32_t vs2, int16_t vs1) -> uint32_t {
+ return vs2 - static_cast<uint32_t>(static_cast<int32_t>(vs1));
+ });
+ case 4:
+ return RiscVBinaryVectorOp<uint64_t, uint64_t, int32_t>(
+ rv_vector, inst, [](uint64_t vs2, int32_t vs1) -> uint64_t {
+ return vs2 - static_cast<uint64_t>(static_cast<int64_t>(vs1));
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Widening multiply helper function. Factors out some code.
+template <typename T>
+inline typename WideType<T>::type VwmulHelper(T vs2, T vs1) {
+ using WT = typename WideType<T>::type;
+ WT vs2_w = static_cast<WT>(vs2);
+ WT vs1_w = static_cast<WT>(vs1);
+ WT prod = vs2_w * vs1_w;
+ return prod;
+}
+
+// Unsigned widening multiply.
+void Vwmulu(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ // LMUL8 cannot be 64.
+ if (rv_vector->vector_length_multiplier() > 32) {
+ rv_vector->set_vector_exception();
+ LOG(ERROR)
+ << "Vector length multiplier out of range for widening operation";
+ return;
+ }
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorOp<uint16_t, uint8_t, uint8_t>(
+ rv_vector, inst, [](uint8_t vs2, int8_t vs1) -> uint16_t {
+ return VwmulHelper<uint8_t>(vs2, vs1);
+ });
+ case 2:
+ return RiscVBinaryVectorOp<uint32_t, uint16_t, uint16_t>(
+ rv_vector, inst, [](uint16_t vs2, uint16_t vs1) -> uint32_t {
+ return VwmulHelper<uint16_t>(vs2, vs1);
+ });
+ case 4:
+ return RiscVBinaryVectorOp<uint64_t, uint32_t, uint32_t>(
+ rv_vector, inst, [](uint32_t vs2, uint32_t vs1) -> uint64_t {
+ return VwmulHelper<uint32_t>(vs2, vs1);
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Widening signed-unsigned multiply helper function.
+template <typename T>
+inline typename WideType<typename std::make_signed<T>::type>::type
+VwmulSuHelper(typename std::make_signed<T>::type vs2,
+ typename std::make_unsigned<T>::type vs1) {
+ using WST = typename WideType<typename std::make_signed<T>::type>::type;
+ using WT = typename WideType<typename std::make_unsigned<T>::type>::type;
+ WST vs2_w = static_cast<WST>(vs2);
+ WT vs1_w = static_cast<WT>(vs1);
+ WST prod = vs2_w * vs1_w;
+ return prod;
+}
+
+// Widening multiply signed-unsigned.
+void Vwmulsu(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ // LMUL8 cannot be 64.
+ if (rv_vector->vector_length_multiplier() > 32) {
+ rv_vector->set_vector_exception();
+ LOG(ERROR)
+ << "Vector length multiplier out of range for widening operation";
+ return;
+ }
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorOp<int16_t, int8_t, uint8_t>(
+ rv_vector, inst, [](int8_t vs2, int8_t vs1) -> int16_t {
+ return VwmulSuHelper<int8_t>(vs2, vs1);
+ });
+ case 2:
+ return RiscVBinaryVectorOp<int32_t, int16_t, uint16_t>(
+ rv_vector, inst, [](int16_t vs2, int16_t vs1) -> int32_t {
+ return VwmulSuHelper<int16_t>(vs2, vs1);
+ });
+ case 4:
+ return RiscVBinaryVectorOp<int64_t, int32_t, uint32_t>(
+ rv_vector, inst, [](int32_t vs2, int32_t vs1) -> int64_t {
+ return VwmulSuHelper<int32_t>(vs2, vs1);
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Widening signed multiply.
+void Vwmul(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ // LMUL8 cannot be 64.
+ if (rv_vector->vector_length_multiplier() > 32) {
+ rv_vector->set_vector_exception();
+ LOG(ERROR)
+ << "Vector length multiplier out of range for widening operation";
+ return;
+ }
+ switch (sew) {
+ case 1:
+ return RiscVBinaryVectorOp<int16_t, int8_t, int8_t>(
+ rv_vector, inst, [](int8_t vs2, int8_t vs1) -> int16_t {
+ return VwmulHelper<int8_t>(vs2, vs1);
+ });
+ case 2:
+ return RiscVBinaryVectorOp<int32_t, int16_t, int16_t>(
+ rv_vector, inst, [](int16_t vs2, int16_t vs1) -> int32_t {
+ return VwmulHelper<int16_t>(vs2, vs1);
+ });
+ case 4:
+ return RiscVBinaryVectorOp<int64_t, int32_t, int32_t>(
+ rv_vector, inst, [](int32_t vs2, int32_t vs1) -> int64_t {
+ return VwmulHelper<int32_t>(vs2, vs1);
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Widening multiply accumulate helper function.
+template <typename Vd, typename Vs2, typename Vs1>
+Vd VwmaccHelper(Vs2 vs2, Vs1 vs1, Vd vd) {
+ Vd vs2_w = static_cast<Vd>(vs2);
+ Vd vs1_w = static_cast<Vd>(vs1);
+ Vd prod = vs2_w * vs1_w;
+ using UVd = typename std::make_unsigned<Vd>::type;
+ Vd res = absl::bit_cast<UVd>(prod) + absl::bit_cast<UVd>(vd);
+ return res;
+}
+
+// Unsigned widening multiply and add.
+void Vwmaccu(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ // LMUL8 cannot be 64.
+ if (rv_vector->vector_length_multiplier() > 32) {
+ rv_vector->set_vector_exception();
+ LOG(ERROR)
+ << "Vector length multiplier out of range for widening operation";
+ return;
+ }
+ switch (sew) {
+ case 1:
+ return RiscVTernaryVectorOp<uint16_t, uint8_t, uint8_t>(
+ rv_vector, inst,
+ [](uint8_t vs2, uint8_t vs1, uint16_t vd) -> uint16_t {
+ return VwmaccHelper(vs2, vs1, vd);
+ });
+ case 2:
+ return RiscVTernaryVectorOp<uint32_t, uint16_t, uint16_t>(
+ rv_vector, inst,
+ [](uint16_t vs2, uint16_t vs1, uint32_t vd) -> uint32_t {
+ return VwmaccHelper(vs2, vs1, vd);
+ });
+ case 4:
+ return RiscVTernaryVectorOp<uint64_t, uint32_t, uint32_t>(
+ rv_vector, inst,
+ [](uint32_t vs2, uint32_t vs1, uint64_t vd) -> uint64_t {
+ return VwmaccHelper(vs2, vs1, vd);
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Widening signed multiply and add.
+void Vwmacc(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ // LMUL8 cannot be 64.
+ if (rv_vector->vector_length_multiplier() > 32) {
+ rv_vector->set_vector_exception();
+ LOG(ERROR)
+ << "Vector length multiplier out of range for widening operation";
+ return;
+ }
+ switch (sew) {
+ case 1:
+ return RiscVTernaryVectorOp<int16_t, int8_t, int8_t>(
+ rv_vector, inst, [](int8_t vs2, int8_t vs1, int16_t vd) -> int16_t {
+ return VwmaccHelper(vs2, vs1, vd);
+ });
+ case 2:
+ return RiscVTernaryVectorOp<int32_t, int16_t, int16_t>(
+ rv_vector, inst, [](int16_t vs2, int16_t vs1, int32_t vd) -> int32_t {
+ return VwmaccHelper(vs2, vs1, vd);
+ });
+ case 4:
+ return RiscVTernaryVectorOp<int64_t, int32_t, int32_t>(
+ rv_vector, inst, [](int32_t vs2, int32_t vs1, int64_t vd) -> int64_t {
+ return VwmaccHelper(vs2, vs1, vd);
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Widening unsigned-signed multiply and add.
+void Vwmaccus(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ // LMUL8 cannot be 64.
+ if (rv_vector->vector_length_multiplier() > 32) {
+ rv_vector->set_vector_exception();
+ LOG(ERROR)
+ << "Vector length multiplier out of range for widening operation";
+ return;
+ }
+ switch (sew) {
+ case 1:
+ return RiscVTernaryVectorOp<int16_t, int8_t, uint8_t>(
+ rv_vector, inst, [](int8_t vs2, uint8_t vs1, int16_t vd) -> int16_t {
+ return VwmaccHelper(vs2, vs1, vd);
+ });
+ case 2:
+ return RiscVTernaryVectorOp<int32_t, int16_t, uint16_t>(
+ rv_vector, inst,
+ [](int16_t vs2, uint16_t vs1, int32_t vd) -> int32_t {
+ return VwmaccHelper(vs2, vs1, vd);
+ });
+ case 4:
+ return RiscVTernaryVectorOp<int64_t, int32_t, uint32_t>(
+ rv_vector, inst,
+ [](int32_t vs2, uint32_t vs1, int64_t vd) -> int64_t {
+ return VwmaccHelper(vs2, vs1, vd);
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Widening signed-unsigned multiply and add.
+void Vwmaccsu(const Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ // LMUL8 cannot be 64.
+ if (rv_vector->vector_length_multiplier() > 32) {
+ rv_vector->set_vector_exception();
+ LOG(ERROR)
+ << "Vector length multiplier out of range for widening operation";
+ return;
+ }
+ switch (sew) {
+ case 1:
+ return RiscVTernaryVectorOp<int16_t, uint8_t, int8_t>(
+ rv_vector, inst, [](uint8_t vs2, int8_t vs1, int16_t vd) -> int16_t {
+ return VwmaccHelper(vs2, vs1, vd);
+ });
+ case 2:
+ return RiscVTernaryVectorOp<int32_t, uint16_t, int16_t>(
+ rv_vector, inst,
+ [](uint16_t vs2, int16_t vs1, int32_t vd) -> int32_t {
+ return VwmaccHelper(vs2, vs1, vd);
+ });
+ case 4:
+ return RiscVTernaryVectorOp<int64_t, uint32_t, int32_t>(
+ rv_vector, inst,
+ [](uint32_t vs2, int32_t vs1, int64_t vd) -> int64_t {
+ return VwmaccHelper(vs2, vs1, vd);
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+} // namespace cheriot
+} // namespace sim
+} // namespace mpact
diff --git a/cheriot/riscv_cheriot_vector_opm_instructions.h b/cheriot/riscv_cheriot_vector_opm_instructions.h
new file mode 100644
index 0000000..9dbd61f
--- /dev/null
+++ b/cheriot/riscv_cheriot_vector_opm_instructions.h
@@ -0,0 +1,180 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef MPACT_CHERIOT_RISCV_CHERIOT_VECTOR_OPM_INSTRUCTIONS_H_
+#define MPACT_CHERIOT_RISCV_CHERIOT_VECTOR_OPM_INSTRUCTIONS_H_
+
+#include "mpact/sim/generic/instruction.h"
+
+namespace mpact {
+namespace sim {
+namespace cheriot {
+
+using Instruction = ::mpact::sim::generic::Instruction;
+
+// Integer vector operations.
+// Integer average unsigned add. This instruction takes 3 source operands and
+// one destination. Source 0 is vs2, source 1 is vs1 (or rs1), and source 2
+// is vector mask. The destination operand is a vector register group.
+void Vaaddu(const Instruction *inst);
+// Integer average signed add. This instruction takes 3 source operands and
+// one destination. Source 0 is vs2, source 1 is vs1 (or rs1), and source 2
+// is vector mask. The destination operand is a vector register group.
+void Vaadd(const Instruction *inst);
+// Integer average unsigned subtract. This instruction takes 3 source operands
+// and one destination. Source 0 is vs2, source 1 is vs1 (or rs1), and source 2
+// is vector mask. The destination operand is a vector register group.
+void Vasubu(const Instruction *inst);
+// Integer average signed subtract. This instruction takes 3 source operands
+// and one destination. Source 0 is vs2, source 1 is vs1 (or rs1), and source 2
+// is vector mask. The destination operand is a vector register group.
+void Vasub(const Instruction *inst);
+// The following instructions are vector mask logical operations. Each takes
+// two source operands, vs2 and vs1 vector registers, and one destination
+// operand, vd vector destination register.
+void Vmandnot(const Instruction *inst);
+void Vmand(const Instruction *inst);
+void Vmor(const Instruction *inst);
+void Vmxor(const Instruction *inst);
+void Vmornot(const Instruction *inst);
+void Vmnand(const Instruction *inst);
+void Vmnor(const Instruction *inst);
+void Vmxnor(const Instruction *inst);
+// Integer unsigned division. This instruction takes 3 source operands and
+// one destination. Source 0 is vs2, source 1 is vs1 (or rs1), and source 2
+// is vector mask. The destination operand is a vector register group.
+void Vdivu(const Instruction *inst);
+// Integer signed division. This instruction takes 3 source operands and
+// one destination. Source 0 is vs2, source 1 is vs1 (or rs1), and source 2
+// is vector mask. The destination operand is a vector register group.
+void Vdiv(const Instruction *inst);
+// Integer unsigned remainder. This instruction takes 3 source operands and
+// one destination. Source 0 is vs2, source 1 is vs1 (or rs1), and source 2
+// is vector mask. The destination operand is a vector register group.
+void Vremu(const Instruction *inst);
+// Integer signed remainder. This instruction takes 3 source operands and
+// one destination. Source 0 is vs2, source 1 is vs1 (or rs1), and source 2
+// is vector mask. The destination operand is a vector register group.
+void Vrem(const Instruction *inst);
+// Integer unsigned multiply high. This instruction takes 3 source operands and
+// one destination. Source 0 is vs2, source 1 is vs1 (or rs1), and source 2
+// is vector mask. The destination operand is a vector register group.
+void Vmulhu(const Instruction *inst);
+// Integer signed multiply. This instruction takes 3 source operands and
+// one destination. Source 0 is vs2, source 1 is vs1 (or rs1), and source 2
+// is vector mask. The destination operand is a vector register group.
+void Vmul(const Instruction *inst);
+// Integer signed-unsigned multiply high. This instruction takes 3 source
+// operands and one destination. Source 0 is vs2, source 1 is vs1 (or rs1), and
+// source 2 is vector mask. The destination operand is a vector register group.
+void Vmulhsu(const Instruction *inst);
+// Integer signed multiply high. This instruction takes 3 source operands and
+// one destination. Source 0 is vs2, source 1 is vs1 (or rs1), and source 2
+// is vector mask. The destination operand is a vector register group.
+void Vmulh(const Instruction *inst);
+// Integer multiply add (vs1 * vd) + vs2. This instruction takes 4 source
+// operands and one destination operand. Source 0 is vs2, source 1 is vs1 (or
+// rs1), source 2 is Vd as a source operand, and source 4 is vector mask. The
+// destination operand is the Vd register group.
+void Vmadd(const Instruction *inst);
+// Integer multiply subtract -(vs1 * vd) + vs2. This instruction takes 4
+// source operands and one destination operand. Source 0 is vs2, source 1 is vs1
+// (or rs1), source 2 is Vd as a source operand, and source 4 is vector mask.
+// The destination operand is the Vd register group.
+void Vnmsub(const Instruction *inst);
+// Integer multiply add (vs1 * vs2) + vd. This instruction takes 4 source
+// operands and one destination operand. Source 0 is vs2, source 1 is vs1 (or
+// rs1), source 2 is Vd as a source operand, and source 4 is vector mask. The
+// destination operand is the Vd register group.
+void Vmacc(const Instruction *inst);
+// Integer multiply subtract -(vs1 * vs2) + vd. This instruction takes 4 source
+// operands and one destination operand. Source 0 is vs2, source 1 is vs1 (or
+// rs1), source 2 is Vd as a source operand, and source 4 is vector mask. The
+// destination operand is the Vd register group.
+void Vnmsac(const Instruction *inst);
+// Integer widening unsigned addition. This instruction takes 3 source operands
+// and one destination. Source 0 is vs2, source 1 is vs1 (or rs1), and source 2
+// is vector mask. The destination operand is a vector register group.
+void Vwaddu(const Instruction *inst);
+// Integer widening signed addition. This instruction takes 3 source operands
+// and one destination. Source 0 is vs2, source 1 is vs1 (or rs1), and source 2
+// is vector mask. The destination operand is a vector register group.
+void Vwadd(const Instruction *inst);
+// Integer widening unsigned subtraction. This instruction takes 3 source
+// operands and one destination. Source 0 is vs2, source 1 is vs1 (or rs1), and
+// source 2 is vector mask. The destination operand is a vector register group.
+void Vwsubu(const Instruction *inst);
+// Integer widening signed subtraction. This instruction takes 3 source operands
+// and one destination. Source 0 is vs2, source 1 is vs1 (or rs1), and source 2
+// is vector mask. The destination operand is a vector register group.
+void Vwsub(const Instruction *inst);
+// Integer widening unsigned addition with one wide source operand. This
+// instruction takes 3 source operands and one destination. Source 0 is vs2
+// (wide), source 1 is vs1 (or rs1), and source 2 is vector mask. The
+// destination operand is a vector register group.
+void Vwadduw(const Instruction *inst);
+// Integer widening signed addition with one wide source operand. This
+// instruction takes 3 source operands and one destination. Source 0 is vs2
+// (wide), source 1 is vs1 (or rs1), and source 2 is vector mask. The
+// destination operand is a vector register group.
+void Vwaddw(const Instruction *inst);
+// Integer widening unsigned subtraction with one wide source operand. This
+// instruction takes 3 source operands and one destination. Source 0 is vs2
+// (wide), source 1 is vs1 (or rs1), and source 2 is vector mask. The
+// destination operand is a vector register group.
+void Vwsubuw(const Instruction *inst);
+// Integer widening signed subtraction with one wide source operand. This
+// instruction takes 3 source operands and one destination. Source 0 is vs2
+// (wide), source 1 is vs1 (or rs1), and source 2 is vector mask. The
+// destination operand is a vector register group.
+void Vwsubw(const Instruction *inst);
+// Integer widening unsigned multiplication. This instruction takes 3 source
+// operands and one destination. Source 0 is vs2, source 1 is vs1 (or rs1), and
+// source 2 is vector mask. The destination operand is a vector register group.
+void Vwmulu(const Instruction *inst);
+// Integer widening signed by unsigned multiplication. This instruction takes 3
+// source operands and one destination. Source 0 is vs2, source 1 is vs1 (or
+// rs1), and source 2 is vector mask. The destination operand is a vector
+// register group.
+void Vwmulsu(const Instruction *inst);
+// Integer widening signed multiplication. This instruction takes 3 source
+// operands and one destination. Source 0 is vs2, source 1 is vs1 (or rs1), and
+// source 2 is vector mask. The destination operand is a vector register group.
+void Vwmul(const Instruction *inst);
+// Integer widening signed multiply and add (vs2 * vs1) + vd. This instruction
+// takes 4 source operands and one destination operand. Source 0 is vs2, source
+// 1 is vs1 (or rs1), source 2 is Vd as a source operand, and source 4 is vector
+// mask. The destination operand is the Vd register group.
+void Vwmaccu(const Instruction *inst);
+// Integer widening unsigned multiply and add (vs2 * vs1) + vd. This instruction
+// takes 4 source operands and one destination operand. Source 0 is vs2, source
+// 1 is vs1 (or rs1), source 2 is Vd as a source operand, and source 4 is vector
+// mask. The destination operand is the Vd register group.
+void Vwmacc(const Instruction *inst);
+// Integer widening unsigned by signed multiply and add (vs2 * vs1) + vd. This
+// instruction takes 4 source operands and one destination operand. Source 0 is
+// vs2, source 1 is vs1 (or rs1), source 2 is Vd as a source operand, and source
+// 4 is vector mask. The destination operand is the Vd register group.
+void Vwmaccus(const Instruction *inst);
+// Integer widening signed by unsigned multiply and add (vs2 * vs1) + vd. This
+// instruction takes 4 source operands and one destination operand. Source 0 is
+// vs2, source 1 is vs1 (or rs1), source 2 is Vd as a source operand, and source
+// 4 is vector mask. The destination operand is the Vd register group.
+void Vwmaccsu(const Instruction *inst);
+
+} // namespace cheriot
+} // namespace sim
+} // namespace mpact
+
+#endif // MPACT_CHERIOT_RISCV_CHERIOT_VECTOR_OPM_INSTRUCTIONS_H_
diff --git a/cheriot/riscv_cheriot_vector_permute_instructions.cc b/cheriot/riscv_cheriot_vector_permute_instructions.cc
new file mode 100644
index 0000000..b5bad6e
--- /dev/null
+++ b/cheriot/riscv_cheriot_vector_permute_instructions.cc
@@ -0,0 +1,465 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cheriot/riscv_cheriot_vector_permute_instructions.h"
+
+#include <algorithm>
+#include <cstdint>
+
+#include "absl/log/log.h"
+#include "absl/strings/str_cat.h"
+#include "absl/types/span.h"
+#include "cheriot/cheriot_register.h"
+#include "cheriot/cheriot_state.h"
+#include "cheriot/cheriot_vector_state.h"
+#include "mpact/sim/generic/data_buffer.h"
+#include "mpact/sim/generic/instruction.h"
+#include "riscv//riscv_register.h"
+
+namespace mpact {
+namespace sim {
+namespace cheriot {
+
+using ::mpact::sim::riscv::RV32VectorDestinationOperand;
+using ::mpact::sim::riscv::RV32VectorSourceOperand;
+
+// This helper function handles the vector gather operations.
+template <typename Vd, typename Vs2, typename Vs1>
+void VrgatherHelper(CheriotVectorState *rv_vector, Instruction *inst) {
+ if (rv_vector->vector_exception()) return;
+ int num_elements = rv_vector->vector_length();
+ int elements_per_vector =
+ rv_vector->vector_register_byte_length() / sizeof(Vd);
+ // Verify that the lmul is compatible with index size.
+ int index_emul =
+ rv_vector->vector_length_multiplier() * sizeof(Vs1) / sizeof(Vd);
+ if (index_emul > 64) {
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int max_regs = std::max(
+ 1, (num_elements + elements_per_vector - 1) / elements_per_vector);
+ auto *dest_op =
+ static_cast<RV32VectorDestinationOperand *>(inst->Destination(0));
+ // Verify that there are enough registers in the destination operand.
+ if (dest_op->size() < max_regs) {
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << absl::StrCat(
+ "Vector destination '", dest_op->AsString(), "' has fewer registers (",
+ dest_op->size(), ") than required by the operation (", max_regs, ")");
+ return;
+ }
+ // Get the vector mask.
+ auto *mask_op = static_cast<RV32VectorSourceOperand *>(inst->Source(2));
+ auto mask_span = mask_op->GetRegister(0)->data_buffer()->Get<uint8_t>();
+ // Get the vector start element index and compute the where to start
+ // the operation.
+ int vector_index = rv_vector->vstart();
+ int start_reg = vector_index / elements_per_vector;
+ int item_index = vector_index % elements_per_vector;
+ // Determine if it's vector-vector or vector-scalar.
+ bool vector_scalar = inst->Source(1)->shape()[0] == 1;
+ auto src0_op = static_cast<RV32VectorSourceOperand *>(inst->Source(0));
+ int max_index = src0_op->size() * elements_per_vector;
+ // Iterate over the number of registers to write.
+ for (int reg = start_reg; (reg < max_regs) && (vector_index < num_elements);
+ reg++) {
+ // Allocate data buffer for the new register data.
+ auto *dest_db = dest_op->CopyDataBuffer(reg);
+ auto dest_span = dest_db->Get<Vd>();
+ // Write data into register subject to masking.
+ int element_count = std::min(elements_per_vector, num_elements);
+ for (int i = item_index;
+ (i < element_count) && (vector_index < num_elements); i++) {
+ // Get the mask value.
+ int mask_index = i >> 3;
+ int mask_offset = i & 0b111;
+ bool mask_value = ((mask_span[mask_index] >> mask_offset) & 0b1) != 0;
+ if (mask_value) {
+ // Compute result.
+ CheriotRegister::ValueType vs1;
+ if (vector_scalar) {
+ vs1 = generic::GetInstructionSource<CheriotRegister::ValueType>(inst,
+ 1, 0);
+ } else {
+ vs1 = generic::GetInstructionSource<Vs1>(inst, 1, vector_index);
+ }
+ Vs2 vs2 = 0;
+ if (vs1 < max_index) {
+ vs2 = generic::GetInstructionSource<Vs2>(inst, 0, vs1);
+ }
+ dest_span[i] = vs2;
+ }
+ vector_index++;
+ }
+ // Submit the destination db .
+ dest_db->Submit();
+ item_index = 0;
+ }
+ rv_vector->clear_vstart();
+}
+
+// Vector register gather.
+void Vrgather(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return VrgatherHelper<uint8_t, uint8_t, uint8_t>(rv_vector, inst);
+ case 2:
+ return VrgatherHelper<uint16_t, uint16_t, uint16_t>(rv_vector, inst);
+ case 4:
+ return VrgatherHelper<uint32_t, uint32_t, uint32_t>(rv_vector, inst);
+ case 8:
+ return VrgatherHelper<uint64_t, uint64_t, uint64_t>(rv_vector, inst);
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Vector register gather with 16 bit indices.
+void Vrgatherei16(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return VrgatherHelper<uint8_t, uint8_t, uint16_t>(rv_vector, inst);
+ case 2:
+ return VrgatherHelper<uint16_t, uint16_t, uint16_t>(rv_vector, inst);
+ case 4:
+ return VrgatherHelper<uint32_t, uint32_t, uint16_t>(rv_vector, inst);
+ case 8:
+ return VrgatherHelper<uint64_t, uint64_t, uint16_t>(rv_vector, inst);
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// This helper function handles the vector slide up/down instructions.
+template <typename Vd>
+void VSlideHelper(CheriotVectorState *rv_vector, Instruction *inst,
+ int offset) {
+ if (rv_vector->vector_exception()) return;
+ int num_elements = rv_vector->vector_length();
+ int elements_per_vector =
+ rv_vector->vector_register_byte_length() / sizeof(Vd);
+ int max_regs = std::max(
+ 1, (num_elements + elements_per_vector - 1) / elements_per_vector);
+ auto *dest_op =
+ static_cast<RV32VectorDestinationOperand *>(inst->Destination(0));
+ // Verify that there are enough registers in the destination operand.
+ if (dest_op->size() < max_regs) {
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << absl::StrCat(
+ "Vector destination '", dest_op->AsString(), "' has fewer registers (",
+ dest_op->size(), ") than required by the operation (", max_regs, ")");
+ return;
+ }
+ // Get the vector mask.
+ auto *mask_op = static_cast<RV32VectorSourceOperand *>(inst->Source(2));
+ auto mask_span = mask_op->GetRegister(0)->data_buffer()->Get<uint8_t>();
+ // Get the vector start element index and compute the where to start
+ // the operation.
+ int vector_index = rv_vector->vstart();
+ int start_reg = vector_index / elements_per_vector;
+ int item_index = vector_index % elements_per_vector;
+ // Iterate over the number of registers to write.
+ for (int reg = start_reg; (reg < max_regs) && (vector_index < num_elements);
+ reg++) {
+ // Allocate data buffer for the new register data.
+ auto *dest_db = dest_op->CopyDataBuffer(reg);
+ auto dest_span = dest_db->Get<Vd>();
+ // Write data into register subject to masking.
+ int element_count = std::min(elements_per_vector, num_elements);
+ for (int i = item_index;
+ (i < element_count) && (vector_index < num_elements); i++) {
+ // Get the mask value.
+ int mask_index = i >> 3;
+ int mask_offset = i & 0b111;
+ bool mask_value = ((mask_span[mask_index] >> mask_offset) & 0b1);
+ int src_index = vector_index - offset;
+ if ((src_index >= 0) && (mask_value)) {
+ // Compute result.
+ Vd src_value = 0;
+ if (src_index < rv_vector->max_vector_length()) {
+ src_value = generic::GetInstructionSource<Vd>(inst, 0, src_index);
+ }
+ dest_span[i] = src_value;
+ }
+ vector_index++;
+ }
+ // Submit the destination db .
+ dest_db->Submit();
+ item_index = 0;
+ }
+ rv_vector->clear_vstart();
+}
+
+void Vslideup(Instruction *inst) {
+ using ValueType = CheriotRegister::ValueType;
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ auto offset = generic::GetInstructionSource<ValueType>(inst, 1, 0);
+ int int_offset = static_cast<int>(offset);
+ if (offset > rv_vector->max_vector_length()) return;
+ // Slide up amount is positive.
+ switch (sew) {
+ case 1:
+ return VSlideHelper<uint8_t>(rv_vector, inst, int_offset);
+ case 2:
+ return VSlideHelper<uint16_t>(rv_vector, inst, int_offset);
+ case 4:
+ return VSlideHelper<uint32_t>(rv_vector, inst, int_offset);
+ case 8:
+ return VSlideHelper<uint64_t>(rv_vector, inst, int_offset);
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+void Vslidedown(Instruction *inst) {
+ using ValueType = CheriotRegister::ValueType;
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ auto offset = generic::GetInstructionSource<ValueType>(inst, 1, 0);
+ // Slide down amount is negative.
+ int int_offset = -static_cast<int>(offset);
+ switch (sew) {
+ case 1:
+ return VSlideHelper<uint8_t>(rv_vector, inst, int_offset);
+ case 2:
+ return VSlideHelper<uint16_t>(rv_vector, inst, int_offset);
+ case 4:
+ return VSlideHelper<uint32_t>(rv_vector, inst, int_offset);
+ case 8:
+ return VSlideHelper<uint64_t>(rv_vector, inst, int_offset);
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// This helper function handles the vector slide up/down 1 instructions.
+template <typename Vd>
+void VSlide1Helper(CheriotVectorState *rv_vector, Instruction *inst,
+ int offset) {
+ if (rv_vector->vector_exception()) return;
+ int num_elements = rv_vector->vector_length();
+ int elements_per_vector =
+ rv_vector->vector_register_byte_length() / sizeof(Vd);
+ int max_regs = std::max(
+ 1, (num_elements + elements_per_vector - 1) / elements_per_vector);
+ auto *dest_op =
+ static_cast<RV32VectorDestinationOperand *>(inst->Destination(0));
+ // Verify that there are enough registers in the destination operand.
+ if (dest_op->size() < max_regs) {
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << absl::StrCat(
+ "Vector destination '", dest_op->AsString(), "' has fewer registers (",
+ dest_op->size(), ") than required by the operation (", max_regs, ")");
+ return;
+ }
+ // Get the vector mask.
+ auto *mask_op = static_cast<RV32VectorSourceOperand *>(inst->Source(2));
+ auto mask_span = mask_op->GetRegister(0)->data_buffer()->Get<uint8_t>();
+ // Get the vector start element index and compute the where to start
+ // the operation.
+ int vector_index = rv_vector->vstart();
+ int start_reg = vector_index / elements_per_vector;
+ int item_index = vector_index % elements_per_vector;
+ auto slide_value = generic::GetInstructionSource<Vd>(inst, 1, 0);
+ // Iterate over the number of registers to write.
+ for (int reg = start_reg; (reg < max_regs) && (vector_index < num_elements);
+ reg++) {
+ // Allocate data buffer for the new register data.
+ auto *dest_db = dest_op->CopyDataBuffer(reg);
+ auto dest_span = dest_db->Get<Vd>();
+ // Write data into register subject to masking.
+ int element_count = std::min(elements_per_vector, num_elements);
+ for (int i = item_index;
+ (i < element_count) && (vector_index < num_elements); i++) {
+ // Get the mask value.
+ int mask_index = i >> 3;
+ int mask_offset = i & 0b111;
+ bool mask_value = ((mask_span[mask_index] >> mask_offset) & 0b1) != 0;
+ if (mask_value) {
+ // Compute result.
+ Vd src_value = slide_value;
+ int src_index = vector_index - offset;
+ if ((src_index > 0) && (src_index < rv_vector->max_vector_length())) {
+ src_value = generic::GetInstructionSource<Vd>(inst, 0, src_index);
+ }
+ dest_span[i] = src_value;
+ }
+ vector_index++;
+ }
+ // Submit the destination db .
+ dest_db->Submit();
+ item_index = 0;
+ }
+ rv_vector->clear_vstart();
+}
+
+void Vslide1up(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return VSlide1Helper<uint8_t>(rv_vector, inst, 1);
+ case 2:
+ return VSlide1Helper<uint16_t>(rv_vector, inst, 1);
+ case 4:
+ return VSlide1Helper<uint32_t>(rv_vector, inst, 1);
+ case 8:
+ return VSlide1Helper<uint64_t>(rv_vector, inst, 1);
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+void Vslide1down(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return VSlide1Helper<uint8_t>(rv_vector, inst, -1);
+ case 2:
+ return VSlide1Helper<uint16_t>(rv_vector, inst, -1);
+ case 4:
+ return VSlide1Helper<uint32_t>(rv_vector, inst, -1);
+ case 8:
+ return VSlide1Helper<uint64_t>(rv_vector, inst, -1);
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+void Vfslide1up(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 4:
+ return VSlide1Helper<uint32_t>(rv_vector, inst, 1);
+ case 8:
+ return VSlide1Helper<uint64_t>(rv_vector, inst, 1);
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+void Vfslide1down(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 4:
+ return VSlide1Helper<uint32_t>(rv_vector, inst, -1);
+ case 8:
+ return VSlide1Helper<uint64_t>(rv_vector, inst, -1);
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+template <typename Vd>
+void VCompressHelper(CheriotVectorState *rv_vector, Instruction *inst) {
+ if (rv_vector->vector_exception()) return;
+ int num_elements = rv_vector->vector_length();
+ int elements_per_vector =
+ rv_vector->vector_register_byte_length() / sizeof(Vd);
+ int max_regs = std::max(
+ 1, (num_elements + elements_per_vector - 1) / elements_per_vector);
+ auto *dest_op =
+ static_cast<RV32VectorDestinationOperand *>(inst->Destination(0));
+ // Verify that there are enough registers in the destination operand.
+ if (dest_op->size() < max_regs) {
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << absl::StrCat(
+ "Vector destination '", dest_op->AsString(), "' has fewer registers (",
+ dest_op->size(), ") than required by the operation (", max_regs, ")");
+ return;
+ }
+ // Get the vector mask.
+ auto *mask_op = static_cast<RV32VectorSourceOperand *>(inst->Source(1));
+ auto mask_span = mask_op->GetRegister(0)->data_buffer()->Get<uint8_t>();
+ // Get the vector start element index and compute the where to start
+ // the operation.
+ int vector_index = rv_vector->vstart();
+ int dest_index = 0;
+ int prev_reg = -1;
+ absl::Span<Vd> dest_span;
+ generic::DataBuffer *dest_db = nullptr;
+ // Iterate over the input elements.
+ for (int i = vector_index; i < num_elements; i++) {
+ // Get mask value.
+ int mask_index = i >> 3;
+ int mask_offset = i & 0b111;
+ bool mask_value = (mask_span[mask_index] >> mask_offset) & 0b1;
+ if (mask_value) {
+ // Compute destination register.
+ int reg = dest_index / elements_per_vector;
+ if (prev_reg != reg) {
+ // Submit previous data buffer if needed.
+ if (dest_db != nullptr) dest_db->Submit();
+ // Allocate a data buffer.
+ dest_db = dest_op->CopyDataBuffer(reg);
+ dest_span = dest_db->Get<Vd>();
+ prev_reg = reg;
+ }
+ // Copy the source value to the dest_index.
+ Vd src_value = generic::GetInstructionSource<Vd>(inst, 0, i);
+ dest_span[dest_index % elements_per_vector] = src_value;
+ ++dest_index;
+ }
+ }
+ if (dest_db != nullptr) dest_db->Submit();
+ rv_vector->clear_vstart();
+}
+
+void Vcompress(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return VCompressHelper<uint8_t>(rv_vector, inst);
+ case 2:
+ return VCompressHelper<uint16_t>(rv_vector, inst);
+ case 4:
+ return VCompressHelper<uint32_t>(rv_vector, inst);
+ case 8:
+ return VCompressHelper<uint64_t>(rv_vector, inst);
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+} // namespace cheriot
+} // namespace sim
+} // namespace mpact
diff --git a/cheriot/riscv_cheriot_vector_permute_instructions.h b/cheriot/riscv_cheriot_vector_permute_instructions.h
new file mode 100644
index 0000000..9ccc612
--- /dev/null
+++ b/cheriot/riscv_cheriot_vector_permute_instructions.h
@@ -0,0 +1,89 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef MPACT_CHERIOT_RISCV_CHERIOT_VECTOR_PERMUTE_INSTRUCTIONS_H_
+#define MPACT_CHERIOT_RISCV_CHERIOT_VECTOR_PERMUTE_INSTRUCTIONS_H_
+
+#include "mpact/sim/generic/instruction.h"
+
+namespace mpact {
+namespace sim {
+namespace cheriot {
+
+using Instruction = ::mpact::sim::generic::Instruction;
+
+// Vector register gather instruction. This instruction takes three source
+// operands and one destination operand. Source 0 is the vector from which
+// elements are gathered, source 1 is the index vector, and source 2 is the
+// vector mask register. The destination operand is the target vector register.
+void Vrgather(Instruction *inst);
+// Vector register gather instruction with 16 bit indices. This instruction
+// takes three source operands and one destination operand. Source 0 is the
+// vector from which elements are gathered, source 1 is the index vector, and
+// source 2 is the vector mask register. The destination operand is the target
+// vector register.
+void Vrgatherei16(Instruction *inst);
+// Vector slide up instruction. This instruction takes three source operands
+// and one destination operand. Source 0 is the vector source register that
+// contains the values that are 'slid' up. Source 1 is a scalar register or
+// immediate that specifies the number of 'entries' by which source 0 values
+// are slid up. Source 2 is the vector mask register. The destination operand
+// is the target vector register.
+void Vslideup(Instruction *inst);
+// Vector slide down instruction. This instruction takes three source operands
+// and one destination operand. Source 0 is the vector source register that
+// contains the values that are 'slid' down. Source 1 is a scalar register or
+// immediate that specifies the number of 'entries' by which source 0 values
+// are slid down. Source 2 is the vector mask register. The destination operand
+// is the target vector register.
+void Vslidedown(Instruction *inst);
+// Vector slide up instruction. This instruction takes three source operands
+// and one destination operand. Source 0 is the vector source register that
+// contains the values that are 'slid' up by 1. Source 1 is a scalar register or
+// immediate that specifies the value written into the 'empty' slot. Source 2 is
+// the vector mask register. The destination operand is the target vector
+// register.
+void Vslide1up(Instruction *inst);
+// Vector slide down instruction. This instruction takes three source operands
+// and one destination operand. Source 0 is the vector source register that
+// contains the values that are 'slid' down. Source 1 is a scalar register or
+// immediate that specifies the value written into the 'empty' slot. Source 2 is
+// the vector mask register. The destination operand is the target vector
+// register.
+void Vslide1down(Instruction *inst);
+// Vector fp slide up instruction. This instruction takes three source operands
+// and one destination operand. Source 0 is the vector source register that
+// contains the values that are 'slid' up by 1. Source 1 is a floating point
+// register or immediate that specifies the value written into the 'empty' slot.
+// Source 2 is the vector mask register. The destination operand is the target
+// vector register.
+void Vfslide1up(Instruction *inst);
+// Vector fp slide down instruction. This instruction takes three source
+// operands and one destination operand. Source 0 is the vector source register
+// that contains the values that are 'slid' down. Source 1 is a floating point
+// register or immediate that specifies the value written into the 'empty' slot.
+// Source 2 is the vector mask register. The destination operand is the target
+// vector register.
+void Vfslide1down(Instruction *inst);
+// Vector compress instruction. This instruction takes two source operands and
+// one destination operand. Source 0 is the source value vector register. Source
+// 1 is a mask register, with specifies which elements of source 0 should be
+// selected and packed into the destination register.
+void Vcompress(Instruction *inst);
+
+} // namespace cheriot
+} // namespace sim
+} // namespace mpact
+
+#endif // MPACT_CHERIOT_RISCV_CHERIOT_VECTOR_PERMUTE_INSTRUCTIONS_H_
diff --git a/cheriot/riscv_cheriot_vector_reduction_instructions.cc b/cheriot/riscv_cheriot_vector_reduction_instructions.cc
new file mode 100644
index 0000000..8be7f68
--- /dev/null
+++ b/cheriot/riscv_cheriot_vector_reduction_instructions.cc
@@ -0,0 +1,355 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cheriot/riscv_cheriot_vector_reduction_instructions.h"
+
+#include <algorithm>
+#include <cstdint>
+
+#include "absl/log/log.h"
+#include "cheriot/cheriot_state.h"
+#include "cheriot/riscv_cheriot_vector_instruction_helpers.h"
+#include "mpact/sim/generic/instruction.h"
+
+namespace mpact {
+namespace sim {
+namespace cheriot {
+
+using ::mpact::sim::generic::Instruction;
+
+// Sum reduction.
+void Vredsum(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryReductionVectorOp<uint8_t, uint8_t, uint8_t>(
+ rv_vector, inst,
+ [](uint8_t acc, uint8_t vs2) -> uint8_t { return acc + vs2; });
+ case 2:
+ return RiscVBinaryReductionVectorOp<uint16_t, uint16_t, uint16_t>(
+ rv_vector, inst,
+ [](uint16_t acc, uint16_t vs2) -> uint16_t { return acc + vs2; });
+ return;
+ case 4:
+ return RiscVBinaryReductionVectorOp<uint32_t, uint32_t, uint32_t>(
+ rv_vector, inst,
+ [](uint32_t acc, uint32_t vs2) -> uint32_t { return acc + vs2; });
+ return;
+ case 8:
+ return RiscVBinaryReductionVectorOp<uint64_t, uint64_t, uint64_t>(
+ rv_vector, inst,
+ [](uint64_t acc, uint64_t vs2) -> uint64_t { return acc + vs2; });
+ return;
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// And reduction.
+void Vredand(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryReductionVectorOp<uint8_t, uint8_t, uint8_t>(
+ rv_vector, inst,
+ [](uint8_t acc, uint8_t vs2) -> uint8_t { return acc & vs2; });
+ case 2:
+ return RiscVBinaryReductionVectorOp<uint16_t, uint16_t, uint16_t>(
+ rv_vector, inst,
+ [](uint16_t acc, uint16_t vs2) -> uint16_t { return acc & vs2; });
+ return;
+ case 4:
+ return RiscVBinaryReductionVectorOp<uint32_t, uint32_t, uint32_t>(
+ rv_vector, inst,
+ [](uint32_t acc, uint32_t vs2) -> uint32_t { return acc & vs2; });
+ return;
+ case 8:
+ return RiscVBinaryReductionVectorOp<uint64_t, uint64_t, uint64_t>(
+ rv_vector, inst,
+ [](uint64_t acc, uint64_t vs2) -> uint64_t { return acc & vs2; });
+ return;
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Or reduction.
+void Vredor(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryReductionVectorOp<uint8_t, uint8_t, uint8_t>(
+ rv_vector, inst,
+ [](uint8_t acc, uint8_t vs2) -> uint8_t { return acc | vs2; });
+ case 2:
+ return RiscVBinaryReductionVectorOp<uint16_t, uint16_t, uint16_t>(
+ rv_vector, inst,
+ [](uint16_t acc, uint16_t vs2) -> uint16_t { return acc | vs2; });
+ return;
+ case 4:
+ return RiscVBinaryReductionVectorOp<uint32_t, uint32_t, uint32_t>(
+ rv_vector, inst,
+ [](uint32_t acc, uint32_t vs2) -> uint32_t { return acc | vs2; });
+ return;
+ case 8:
+ return RiscVBinaryReductionVectorOp<uint64_t, uint64_t, uint64_t>(
+ rv_vector, inst,
+ [](uint64_t acc, uint64_t vs2) -> uint64_t { return acc | vs2; });
+ return;
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Xor reduction.
+void Vredxor(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryReductionVectorOp<uint8_t, uint8_t, uint8_t>(
+ rv_vector, inst,
+ [](uint8_t acc, uint8_t vs2) -> uint8_t { return acc ^ vs2; });
+ case 2:
+ return RiscVBinaryReductionVectorOp<uint16_t, uint16_t, uint16_t>(
+ rv_vector, inst,
+ [](uint16_t acc, uint16_t vs2) -> uint16_t { return acc ^ vs2; });
+ return;
+ case 4:
+ return RiscVBinaryReductionVectorOp<uint32_t, uint32_t, uint32_t>(
+ rv_vector, inst,
+ [](uint32_t acc, uint32_t vs2) -> uint32_t { return acc ^ vs2; });
+ return;
+ case 8:
+ return RiscVBinaryReductionVectorOp<uint64_t, uint64_t, uint64_t>(
+ rv_vector, inst,
+ [](uint64_t acc, uint64_t vs2) -> uint64_t { return acc ^ vs2; });
+ return;
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Unsigned min reduction.
+void Vredminu(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryReductionVectorOp<uint8_t, uint8_t, uint8_t>(
+ rv_vector, inst, [](uint8_t acc, uint8_t vs2) -> uint8_t {
+ return std::min(acc, vs2);
+ });
+ case 2:
+ return RiscVBinaryReductionVectorOp<uint16_t, uint16_t, uint16_t>(
+ rv_vector, inst, [](uint16_t acc, uint16_t vs2) -> uint16_t {
+ return std::min(acc, vs2);
+ });
+ return;
+ case 4:
+ return RiscVBinaryReductionVectorOp<uint32_t, uint32_t, uint32_t>(
+ rv_vector, inst, [](uint32_t acc, uint32_t vs2) -> uint32_t {
+ return std::min(acc, vs2);
+ });
+ return;
+ case 8:
+ return RiscVBinaryReductionVectorOp<uint64_t, uint64_t, uint64_t>(
+ rv_vector, inst, [](uint64_t acc, uint64_t vs2) -> uint64_t {
+ return std::min(acc, vs2);
+ });
+ return;
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Signed min reduction.
+void Vredmin(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryReductionVectorOp<int8_t, int8_t, int8_t>(
+ rv_vector, inst,
+ [](int8_t acc, int8_t vs2) -> int8_t { return std::min(acc, vs2); });
+ case 2:
+ return RiscVBinaryReductionVectorOp<int16_t, int16_t, int16_t>(
+ rv_vector, inst, [](int16_t acc, int16_t vs2) -> int16_t {
+ return std::min(acc, vs2);
+ });
+ return;
+ case 4:
+ return RiscVBinaryReductionVectorOp<int32_t, int32_t, int32_t>(
+ rv_vector, inst, [](int32_t acc, int32_t vs2) -> int32_t {
+ return std::min(acc, vs2);
+ });
+ return;
+ case 8:
+ return RiscVBinaryReductionVectorOp<int64_t, int64_t, int64_t>(
+ rv_vector, inst, [](int64_t acc, int64_t vs2) -> int64_t {
+ return std::min(acc, vs2);
+ });
+ return;
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Unsigned max reduction.
+void Vredmaxu(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryReductionVectorOp<uint8_t, uint8_t, uint8_t>(
+ rv_vector, inst, [](uint8_t acc, uint8_t vs2) -> uint8_t {
+ return std::max(acc, vs2);
+ });
+ case 2:
+ return RiscVBinaryReductionVectorOp<uint16_t, uint16_t, uint16_t>(
+ rv_vector, inst, [](uint16_t acc, uint16_t vs2) -> uint16_t {
+ return std::max(acc, vs2);
+ });
+ return;
+ case 4:
+ return RiscVBinaryReductionVectorOp<uint32_t, uint32_t, uint32_t>(
+ rv_vector, inst, [](uint32_t acc, uint32_t vs2) -> uint32_t {
+ return std::max(acc, vs2);
+ });
+ return;
+ case 8:
+ return RiscVBinaryReductionVectorOp<uint64_t, uint64_t, uint64_t>(
+ rv_vector, inst, [](uint64_t acc, uint64_t vs2) -> uint64_t {
+ return std::max(acc, vs2);
+ });
+ return;
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Signed max reduction.
+void Vredmax(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryReductionVectorOp<int8_t, int8_t, int8_t>(
+ rv_vector, inst,
+ [](int8_t acc, int8_t vs2) -> int8_t { return std::max(acc, vs2); });
+ case 2:
+ return RiscVBinaryReductionVectorOp<int16_t, int16_t, int16_t>(
+ rv_vector, inst, [](int16_t acc, int16_t vs2) -> int16_t {
+ return std::max(acc, vs2);
+ });
+ return;
+ case 4:
+ return RiscVBinaryReductionVectorOp<int32_t, int32_t, int32_t>(
+ rv_vector, inst, [](int32_t acc, int32_t vs2) -> int32_t {
+ return std::max(acc, vs2);
+ });
+ return;
+ case 8:
+ return RiscVBinaryReductionVectorOp<int64_t, int64_t, int64_t>(
+ rv_vector, inst, [](int64_t acc, int64_t vs2) -> int64_t {
+ return std::max(acc, vs2);
+ });
+ return;
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Unsigned widening (SEW->SEW * 2) reduction.
+void Vwredsumu(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryReductionVectorOp<uint16_t, uint8_t, uint8_t>(
+ rv_vector, inst, [](uint16_t acc, uint8_t vs2) -> uint16_t {
+ return acc + static_cast<uint16_t>(vs2);
+ });
+ case 2:
+ return RiscVBinaryReductionVectorOp<uint32_t, uint16_t, uint16_t>(
+ rv_vector, inst, [](uint32_t acc, uint16_t vs2) -> uint32_t {
+ return acc + static_cast<uint32_t>(vs2);
+ });
+ return;
+ case 4:
+ return RiscVBinaryReductionVectorOp<uint64_t, uint32_t, uint32_t>(
+ rv_vector, inst, [](uint64_t acc, uint32_t vs2) -> uint64_t {
+ return acc + static_cast<uint64_t>(vs2);
+ });
+ return;
+ case 8:
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Signed widening (SEW->SEW * 2) reduction.
+void Vwredsum(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 1:
+ return RiscVBinaryReductionVectorOp<int16_t, int8_t, int8_t>(
+ rv_vector, inst, [](int16_t acc, int8_t vs2) -> int16_t {
+ return acc + static_cast<int16_t>(vs2);
+ });
+ case 2:
+ return RiscVBinaryReductionVectorOp<int32_t, int16_t, int16_t>(
+ rv_vector, inst, [](int32_t acc, int16_t vs2) -> int32_t {
+ return acc + static_cast<int32_t>(vs2);
+ });
+ return;
+ case 4:
+ return RiscVBinaryReductionVectorOp<int64_t, int32_t, int32_t>(
+ rv_vector, inst, [](int64_t acc, int32_t vs2) -> int64_t {
+ return acc + static_cast<int64_t>(vs2);
+ });
+ return;
+ case 8:
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+} // namespace cheriot
+} // namespace sim
+} // namespace mpact
diff --git a/cheriot/riscv_cheriot_vector_reduction_instructions.h b/cheriot/riscv_cheriot_vector_reduction_instructions.h
new file mode 100644
index 0000000..58c93ae
--- /dev/null
+++ b/cheriot/riscv_cheriot_vector_reduction_instructions.h
@@ -0,0 +1,58 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef MPACT_CHERIOT_RISCV_CHERIOT_VECTOR_REDUCTION_INSTRUCTIONS_H_
+#define MPACT_CHERIOT_RISCV_CHERIOT_VECTOR_REDUCTION_INSTRUCTIONS_H_
+
+#include "mpact/sim/generic/instruction.h"
+
+namespace mpact {
+namespace sim {
+namespace cheriot {
+
+using Instruction = ::mpact::sim::generic::Instruction;
+
+// Each of these instruction semantic functions take 3 sources. Source 0 is
+// vector register vs2, source 1 is vector register vs1, and source 2 is a
+// vector mask operand. There is a single vector destination operand.
+// Each reduction applies the reduction operation to the 0 element of source
+// operand 1 (vs1), and all unmasked elements of source operand 0 (vs2). The
+// result is written to the 0 element of destination operand vd.
+
+// Vector sum reduction.
+void Vredsum(Instruction *inst);
+// Vector and reduction.
+void Vredand(Instruction *inst);
+// Vector or reduction.
+void Vredor(Instruction *inst);
+// Vector xor reduction.
+void Vredxor(Instruction *inst);
+// Vector unsigned min reduction.
+void Vredminu(Instruction *inst);
+// Vector signed min reduction.
+void Vredmin(Instruction *inst);
+// Vector unsigned max reduction.
+void Vredmaxu(Instruction *inst);
+// Vector signed max reduction.
+void Vredmax(Instruction *inst);
+// Vector unsigned widening sum reduction. The result is 2 * SEW.
+void Vwredsumu(Instruction *inst);
+// vector signed widening sum reduction. The result is 2 * SEW.
+void Vwredsum(Instruction *inst);
+
+} // namespace cheriot
+} // namespace sim
+} // namespace mpact
+
+#endif // MPACT_CHERIOT_RISCV_CHERIOT_VECTOR_REDUCTION_INSTRUCTIONS_H_
diff --git a/cheriot/riscv_cheriot_vector_unary_instructions.cc b/cheriot/riscv_cheriot_vector_unary_instructions.cc
new file mode 100644
index 0000000..becf09a
--- /dev/null
+++ b/cheriot/riscv_cheriot_vector_unary_instructions.cc
@@ -0,0 +1,461 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cheriot/riscv_cheriot_vector_unary_instructions.h"
+
+#include <cstdint>
+#include <cstring>
+#include <functional>
+
+#include "absl/log/log.h"
+#include "absl/strings/str_cat.h"
+#include "cheriot/cheriot_register.h"
+#include "cheriot/cheriot_state.h"
+#include "cheriot/cheriot_vector_state.h"
+#include "cheriot/riscv_cheriot_instruction_helpers.h"
+#include "cheriot/riscv_cheriot_vector_instruction_helpers.h"
+#include "mpact/sim/generic/instruction.h"
+#include "mpact/sim/generic/type_helpers.h"
+
+namespace mpact {
+namespace sim {
+namespace cheriot {
+
+using SignedXregType =
+ ::mpact::sim::generic::SameSignedType<CheriotRegister::ValueType,
+ int64_t>::type;
+
+// Move scalar to vector register.
+void VmvToScalar(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ if (rv_vector->vstart()) return;
+ if (rv_vector->vector_length() == 0) return;
+ int sew = rv_vector->selected_element_width();
+ SignedXregType value;
+ switch (sew) {
+ case 1:
+ value = static_cast<SignedXregType>(
+ generic::GetInstructionSource<int8_t>(inst, 0));
+ break;
+ case 2:
+ value = static_cast<SignedXregType>(
+ generic::GetInstructionSource<int16_t>(inst, 0));
+ break;
+ case 4:
+ value = static_cast<SignedXregType>(
+ generic::GetInstructionSource<int32_t>(inst, 0));
+ break;
+ case 8:
+ value = static_cast<SignedXregType>(
+ generic::GetInstructionSource<int64_t>(inst, 0));
+ break;
+ default:
+ LOG(ERROR) << absl::StrCat("Illegal SEW value (", sew, ") for Vmvxs");
+ rv_vector->set_vector_exception();
+ return;
+ }
+ WriteCapIntResult<SignedXregType>(inst, 0, value);
+}
+
+void VmvFromScalar(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ if (rv_vector->vstart()) return;
+ if (rv_vector->vector_length() == 0) return;
+ int sew = rv_vector->selected_element_width();
+ auto *dest_db = inst->Destination(0)->AllocateDataBuffer();
+ std::memset(dest_db->raw_ptr(), 0, dest_db->size<uint8_t>());
+ switch (sew) {
+ case 1:
+ dest_db->Set<int8_t>(0, generic::GetInstructionSource<int8_t>(inst, 0));
+ break;
+ case 2:
+ dest_db->Set<int16_t>(0, generic::GetInstructionSource<int16_t>(inst, 0));
+ break;
+ case 4:
+ dest_db->Set<int32_t>(0, generic::GetInstructionSource<int32_t>(inst, 0));
+ break;
+ case 8:
+ dest_db->Set<int64_t>(0, generic::GetInstructionSource<int64_t>(inst, 0));
+ break;
+ default:
+ LOG(ERROR) << absl::StrCat("Illegal SEW value (", sew, ") for Vmvxs");
+ rv_vector->set_vector_exception();
+ return;
+ }
+ dest_db->Submit();
+}
+
+// Population count of vector mask register. The value is written to a scalar
+// register.
+void Vcpop(Instruction *inst) {
+ auto *rv_state = static_cast<CheriotState *>(inst->state());
+ auto *rv_vector = rv_state->rv_vector();
+ if (rv_vector->vstart()) {
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int vlen = rv_vector->vector_length();
+ auto src_op = static_cast<RV32VectorSourceOperand *>(inst->Source(0));
+ auto src_span = src_op->GetRegister(0)->data_buffer()->Get<uint8_t>();
+ auto mask_op = static_cast<RV32VectorSourceOperand *>(inst->Source(1));
+ auto mask_span = mask_op->GetRegister(0)->data_buffer()->Get<uint8_t>();
+ uint64_t count = 0;
+ for (int i = 0; i < vlen; i++) {
+ int index = i >> 3;
+ int offset = i & 0b111;
+ int mask_value = (mask_span[index] >> offset);
+ int src_value = (src_span[index] >> offset);
+ count += mask_value & src_value & 0b1;
+ }
+ WriteCapIntResult<uint32_t>(inst, 0, count);
+}
+
+// Find first set of vector mask register. The value is written to a scalar
+// register.
+void Vfirst(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ if (rv_vector->vstart()) {
+ rv_vector->set_vector_exception();
+ return;
+ }
+ auto src_op = static_cast<RV32VectorSourceOperand *>(inst->Source(0));
+ auto src_span = src_op->GetRegister(0)->data_buffer()->Get<uint8_t>();
+ auto mask_op = static_cast<RV32VectorSourceOperand *>(inst->Source(1));
+ auto mask_span = mask_op->GetRegister(0)->data_buffer()->Get<uint8_t>();
+ // Initialize the element index to -1.
+ uint64_t element_index = -1LL;
+ int vlen = rv_vector->vector_length();
+ for (int i = 0; i < vlen; i++) {
+ int index = i >> 3;
+ int offset = i & 0b111;
+ int mask_value = (mask_span[index] >> offset);
+ int src_value = (src_span[index] >> offset);
+ if (mask_value & src_value & 0b1) {
+ element_index = i;
+ break;
+ }
+ }
+ WriteCapIntResult<uint32_t>(inst, 0, element_index);
+}
+
+// Vector integer sign and zero extension instructions.
+void Vzext2(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 2:
+ return RiscVUnaryVectorOp<uint16_t, uint8_t>(
+ rv_vector, inst,
+ [](uint8_t vs2) -> uint16_t { return static_cast<uint16_t>(vs2); });
+ case 4:
+ return RiscVUnaryVectorOp<uint32_t, uint16_t>(
+ rv_vector, inst,
+ [](uint16_t vs2) -> uint32_t { return static_cast<uint32_t>(vs2); });
+ case 8:
+ return RiscVUnaryVectorOp<uint64_t, uint32_t>(
+ rv_vector, inst,
+ [](uint32_t vs2) -> uint64_t { return static_cast<uint64_t>(vs2); });
+ default:
+ LOG(ERROR) << absl::StrCat("Illegal SEW value (", sew, ") for Vzext2");
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+void Vsext2(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 2:
+ return RiscVUnaryVectorOp<int16_t, int8_t>(
+ rv_vector, inst,
+ [](int8_t vs2) -> int16_t { return static_cast<int16_t>(vs2); });
+ case 4:
+ return RiscVUnaryVectorOp<uint32_t, uint16_t>(
+ rv_vector, inst,
+ [](int16_t vs2) -> int32_t { return static_cast<int32_t>(vs2); });
+ case 8:
+ return RiscVUnaryVectorOp<int64_t, int32_t>(
+ rv_vector, inst,
+ [](int32_t vs2) -> int64_t { return static_cast<int64_t>(vs2); });
+ default:
+ LOG(ERROR) << absl::StrCat("Illegal SEW value (", sew, ") for Vsext2");
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+void Vzext4(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 4:
+ return RiscVUnaryVectorOp<uint32_t, uint8_t>(
+ rv_vector, inst,
+ [](uint8_t vs2) -> uint32_t { return static_cast<uint32_t>(vs2); });
+ case 8:
+ return RiscVUnaryVectorOp<uint64_t, uint16_t>(
+ rv_vector, inst,
+ [](uint16_t vs2) -> uint64_t { return static_cast<uint64_t>(vs2); });
+ default:
+ LOG(ERROR) << absl::StrCat("Illegal SEW value (", sew, ") for Vzext4");
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+void Vsext4(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 4:
+ return RiscVUnaryVectorOp<uint32_t, uint8_t>(
+ rv_vector, inst,
+ [](int8_t vs2) -> int32_t { return static_cast<int32_t>(vs2); });
+ case 8:
+ return RiscVUnaryVectorOp<int64_t, int16_t>(
+ rv_vector, inst,
+ [](int16_t vs2) -> int64_t { return static_cast<int64_t>(vs2); });
+ default:
+ LOG(ERROR) << absl::StrCat("Illegal SEW value (", sew, ") for Vzext4");
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+void Vzext8(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 8:
+ return RiscVUnaryVectorOp<uint64_t, uint8_t>(
+ rv_vector, inst,
+ [](uint8_t vs2) -> uint64_t { return static_cast<uint64_t>(vs2); });
+ default:
+ LOG(ERROR) << absl::StrCat("Illegal SEW value (", sew, ") for Vzext8");
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+void Vsext8(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ switch (sew) {
+ case 8:
+ return RiscVUnaryVectorOp<int64_t, int8_t>(
+ rv_vector, inst,
+ [](int8_t vs2) -> int64_t { return static_cast<int64_t>(vs2); });
+ default:
+ LOG(ERROR) << absl::StrCat("Illegal SEW value (", sew, ") for Vzext8");
+ rv_vector->set_vector_exception();
+ return;
+ }
+}
+
+// Vector mask set-before-first mask bit.
+void Vmsbf(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ if (rv_vector->vstart()) {
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int vlen = rv_vector->vector_length();
+ auto src_op = static_cast<RV32VectorSourceOperand *>(inst->Source(0));
+ auto src_span = src_op->GetRegister(0)->data_buffer()->Get<uint8_t>();
+ auto mask_op = static_cast<RV32VectorSourceOperand *>(inst->Source(1));
+ auto mask_span = mask_op->GetRegister(0)->data_buffer()->Get<uint8_t>();
+ auto dest_op =
+ static_cast<RV32VectorDestinationOperand *>(inst->Destination(0));
+ auto *dest_db = dest_op->CopyDataBuffer(0);
+ auto dest_span = dest_db->Get<uint8_t>();
+ bool before_first = true;
+ int last = 0;
+ // Set the bits before the first active 1.
+ for (int i = 0; i < vlen; i++) {
+ last = i;
+ int index = i >> 3;
+ int offset = i & 0b111;
+ int mask_value = (mask_span[index] >> offset) & 0b1;
+ int src_value = (src_span[index] >> offset) & 0b1;
+ if (mask_value) {
+ before_first = before_first && (src_value == 0);
+ if (!before_first) break;
+
+ dest_span[index] |= 1 << offset;
+ }
+ }
+ // Clear the remaining bits.
+ for (int i = last; !before_first && (i < vlen); i++) {
+ int index = i >> 3;
+ int offset = i & 0b111;
+ dest_span[index] &= ~(1 << offset);
+ }
+ dest_db->Submit();
+ rv_vector->clear_vstart();
+}
+
+// Vector mask set-including-first mask bit.
+void Vmsif(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ if (rv_vector->vstart()) {
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int vlen = rv_vector->vector_length();
+ auto src_op = static_cast<RV32VectorSourceOperand *>(inst->Source(0));
+ auto src_span = src_op->GetRegister(0)->data_buffer()->Get<uint8_t>();
+ auto mask_op = static_cast<RV32VectorSourceOperand *>(inst->Source(1));
+ auto mask_span = mask_op->GetRegister(0)->data_buffer()->Get<uint8_t>();
+ auto dest_op =
+ static_cast<RV32VectorDestinationOperand *>(inst->Destination(0));
+ auto *dest_db = dest_op->CopyDataBuffer(0);
+ auto dest_span = dest_db->Get<uint8_t>();
+ uint8_t value = 1;
+ for (int i = 0; i < vlen; i++) {
+ int index = i >> 3;
+ int offset = i & 0b111;
+ int mask_value = (mask_span[index] >> offset) & 0b1;
+ int src_value = (src_span[index] >> offset) & 0b1;
+ if (mask_value) {
+ if (value) {
+ dest_span[index] |= 1 << offset;
+ } else {
+ dest_span[index] &= ~(1 << offset);
+ }
+ if (src_value) {
+ value = 0;
+ }
+ }
+ }
+ dest_db->Submit();
+ rv_vector->clear_vstart();
+}
+
+// Vector maks set-only-first mask bit.
+void Vmsof(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ if (rv_vector->vstart()) {
+ rv_vector->set_vector_exception();
+ return;
+ }
+ int vlen = rv_vector->vector_length();
+ auto src_op = static_cast<RV32VectorSourceOperand *>(inst->Source(0));
+ auto src_span = src_op->GetRegister(0)->data_buffer()->Get<uint8_t>();
+ auto mask_op = static_cast<RV32VectorSourceOperand *>(inst->Source(1));
+ auto mask_span = mask_op->GetRegister(0)->data_buffer()->Get<uint8_t>();
+ auto dest_op =
+ static_cast<RV32VectorDestinationOperand *>(inst->Destination(0));
+ auto *dest_db = dest_op->CopyDataBuffer(0);
+ auto dest_span = dest_db->Get<uint8_t>();
+ bool first = true;
+ for (int i = 0; i < vlen; i++) {
+ int index = i >> 3;
+ int offset = i & 0b111;
+ int mask_value = (mask_span[index] >> offset) & 0b1;
+ int src_value = (src_span[index] >> offset) & 0b1;
+ if (mask_value) {
+ if (first & src_value) {
+ dest_span[index] |= (1 << offset);
+ first = false;
+ } else {
+ dest_span[index] &= ~(1 << offset);
+ }
+ }
+ }
+ dest_db->Submit();
+ rv_vector->clear_vstart();
+}
+
+// Vector iota. This instruction reads a source vector mask register and
+// writes to each element of the destination vector register group the sum
+// of all bits of elements in the mask register whose index is less than the
+// element. This is subject to masking.
+void Viota(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ int count = 0;
+ switch (sew) {
+ case 1:
+ return RiscVMaskNullaryVectorOp<uint8_t>(
+ rv_vector, inst, [&count](bool mask) -> uint8_t {
+ return mask ? static_cast<uint8_t>(count++)
+ : static_cast<uint8_t>(count);
+ });
+ case 2:
+ return RiscVMaskNullaryVectorOp<uint16_t>(
+ rv_vector, inst, [&count](bool mask) -> uint16_t {
+ return mask ? static_cast<uint16_t>(count++)
+ : static_cast<uint16_t>(count);
+ });
+ case 4:
+ return RiscVMaskNullaryVectorOp<uint32_t>(
+ rv_vector, inst, [&count](bool mask) -> uint32_t {
+ return mask ? static_cast<uint32_t>(count++)
+ : static_cast<uint32_t>(count);
+ });
+ case 8:
+ return RiscVMaskNullaryVectorOp<uint64_t>(
+ rv_vector, inst, [&count](bool mask) -> uint64_t {
+ return mask ? static_cast<uint64_t>(count++)
+ : static_cast<uint64_t>(count);
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+// Writes the index of each active (mask true) element to the destination
+// vector elements.
+void Vid(Instruction *inst) {
+ auto *rv_vector = static_cast<CheriotState *>(inst->state())->rv_vector();
+ int sew = rv_vector->selected_element_width();
+ int index = 0;
+ switch (sew) {
+ case 1:
+ return RiscVMaskNullaryVectorOp<uint8_t>(
+ rv_vector, inst, [&index](bool mask) -> uint8_t {
+ uint64_t ret = index++;
+ return static_cast<uint8_t>(ret);
+ });
+ case 2:
+ return RiscVMaskNullaryVectorOp<uint16_t>(
+ rv_vector, inst, [&index](bool mask) -> uint16_t {
+ uint64_t ret = index++;
+ return static_cast<uint16_t>(ret);
+ });
+ case 4:
+ return RiscVMaskNullaryVectorOp<uint32_t>(
+ rv_vector, inst, [&index](bool mask) -> uint32_t {
+ uint64_t ret = index++;
+ return static_cast<uint32_t>(ret);
+ });
+ case 8:
+ return RiscVMaskNullaryVectorOp<uint64_t>(
+ rv_vector, inst, [&index](bool mask) -> uint64_t {
+ uint64_t ret = index++;
+ return static_cast<uint64_t>(ret);
+ });
+ default:
+ rv_vector->set_vector_exception();
+ LOG(ERROR) << "Illegal SEW value";
+ return;
+ }
+}
+
+} // namespace cheriot
+} // namespace sim
+} // namespace mpact
diff --git a/cheriot/riscv_cheriot_vector_unary_instructions.h b/cheriot/riscv_cheriot_vector_unary_instructions.h
new file mode 100644
index 0000000..4043a11
--- /dev/null
+++ b/cheriot/riscv_cheriot_vector_unary_instructions.h
@@ -0,0 +1,109 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef MPACT_CHERIOT_RISCV_CHERIOT_VECTOR_UNARY_INSTRUCTIONS_H_
+#define MPACT_CHERIOT_RISCV_CHERIOT_VECTOR_UNARY_INSTRUCTIONS_H_
+
+#include "mpact/sim/generic/instruction.h"
+
+// This file declares the vector instruction semantic functions for the integer
+// unary vector instructions.
+namespace mpact {
+namespace sim {
+namespace cheriot {
+
+using Instruction = ::mpact::sim::generic::Instruction;
+
+// VWXUNARY0
+// Moves a value from index 0 element of a vector register to a scalar register.
+// This instruction takes 1 source and 1 destination. One is a vector register,
+// the other is a scalar (x) register.
+void VmvToScalar(Instruction *inst);
+// Moves a scalar to index 0 element of a vector register. This instruction
+// takes 1 source and 1 destination. One is a vector register, the other is
+// a scalar (x) register.
+void VmvFromScalar(Instruction *inst);
+// Does a population count on the vector mask in the source operand 0 (subject
+// to masking), and writes the result to the scalar register in the destination
+// operand. Operand 1 is a mask register. Only vlen bits are considered.
+void Vcpop(Instruction *inst);
+// Computes the index of the first set bit of the vector mask in the source
+// operand 0 (subject ot masking), and writes the result to the scalar register
+// in the destination operand. Operand 1 is a mask register. Only vlen bits
+// are considered.
+void Vfirst(Instruction *inst);
+
+// VXUNARY0
+// Element wide zero extend from SEW/2 to SEW. This instruction takes two source
+// operands, and a vector destination operand. Source 0 is the vs2 vector
+// source, and source 1 is a vector mask operand.
+void Vzext2(Instruction *inst);
+// Element wide sign extend from SEW/2 to SEW. This instruction takes two source
+// operands, and a vector destination operand. Source 0 is the vs2 vector
+// source, and source 1 is a vector mask operand.
+void Vsext2(Instruction *inst);
+// Element wide zero extend from SEW/4 to SEW. This instruction takes two source
+// operands, and a vector destination operand. Source 0 is the vs2 vector
+// source, and source 1 is a vector mask operand.
+void Vzext4(Instruction *inst);
+// Element wide sign extend from SEW/4 to SEW. This instruction takes two source
+// operands, and a vector destination operand. Source 0 is the vs2 vector
+// source, and source 1 is a vector mask operand.
+void Vsext4(Instruction *inst);
+// Element wide zero extend from SEW/8 to SEW. This instruction takes two source
+// operands, and a vector destination operand. Source 0 is the vs2 vector
+// source, and source 1 is a vector mask operand.
+void Vzext8(Instruction *inst);
+// Element wide sign extend from SEW/8 to SEW. This instruction takes two source
+// operands, and a vector destination operand. Source 0 is the vs2 vector
+// source, and source 1 is a vector mask operand.
+void Vsext8(Instruction *inst);
+
+// VMUNARY0
+// Set before first mask bit. Takes a vector mask stored in a vector register
+// and produces a mask register with all active bits before the first set active
+// bit in the source mask set to 1. This instruction takes one vector source
+// operands, a mask register, and a vector destination operand. Source 0 is the
+// vs2 register source, source 1 is the vector mask operand.
+void Vmsbf(Instruction *inst);
+// Set only first mask bit. Takes a vector mask stored in a vector register
+// and produces a mask register with only the bit set that corresponds to the
+// the first set active bit in the source mask set to 1. This instruction takes
+// one vector source operands, a mask register, and a vector destination
+// operand. Source 0 is the vs2 register source, source 1 is the vector mask
+// operand.
+void Vmsof(Instruction *inst);
+// Set including first mask bit. Takes a vector mask stored in a vector register
+// and produces a mask register with all active bits before and including the
+// first set active bit in the source mask set to 1. This instruction takes one
+// vector source operands, a mask register, and a vector destination operand.
+// Source 0 is the vs2 register source, source 1 is the vector mask operand.
+void Vmsif(Instruction *inst);
+// Vector Iota instruction. Takes a vector mask stored in a vector register
+// and writes to each element of the destination vector register group the sum
+// of all bits of elements in the mask register whose index is less than the
+// element (parallel prefix sum). This instruction takes two sources and one
+// destination. Source 0 is the vs2 register source, source 1 is the vector
+// mask operand.
+void Viota(Instruction *inst);
+// Vector element index instruction. Writes the element index to the destination
+// vector element group (masking does not change the value written to active
+// elements, only which elements are written to). This instruction takes 1
+// source (mask register) and one destination.
+void Vid(Instruction *inst);
+} // namespace cheriot
+} // namespace sim
+} // namespace mpact
+
+#endif // MPACT_CHERIOT_RISCV_CHERIOT_VECTOR_UNARY_INSTRUCTIONS_H_
diff --git a/cheriot/test/BUILD b/cheriot/test/BUILD
index c4920c9..ebaa5e5 100644
--- a/cheriot/test/BUILD
+++ b/cheriot/test/BUILD
@@ -16,12 +16,17 @@
package(default_applicable_licenses = ["//:license"])
+config_setting(
+ name = "darwin_arm64_cpu",
+ values = {"cpu": "darwin_arm64"},
+)
+
cc_test(
name = "cheriot_register_test",
size = "small",
srcs = ["cheriot_register_test.cc"],
deps = [
- "//cheriot:riscv_cheriot",
+ "//cheriot:cheriot_state",
"@com_google_absl//absl/log:check",
"@com_google_absl//absl/random",
"@com_google_absl//absl/status",
@@ -37,7 +42,7 @@
size = "small",
srcs = ["cheriot_state_test.cc"],
deps = [
- "//cheriot:riscv_cheriot",
+ "//cheriot:cheriot_state",
"@com_google_absl//absl/log:check",
"@com_google_absl//absl/strings:str_format",
"@com_google_googletest//:gtest_main",
@@ -54,7 +59,8 @@
"riscv_cheriot_instructions_test.cc",
],
deps = [
- "//cheriot:riscv_cheriot",
+ "//cheriot:cheriot_state",
+ "//cheriot:riscv_cheriot_instructions",
"@com_google_absl//absl/log:check",
"@com_google_absl//absl/random",
"@com_google_absl//absl/strings:str_format",
@@ -75,7 +81,8 @@
"riscv_cheriot_i_instructions_test.cc",
],
deps = [
- "//cheriot:riscv_cheriot",
+ "//cheriot:cheriot_state",
+ "//cheriot:riscv_cheriot_instructions",
"@com_google_absl//absl/types:span",
"@com_google_googletest//:gtest_main",
"@com_google_mpact-sim//mpact/sim/generic:core",
@@ -92,7 +99,8 @@
"riscv_cheriot_m_instructions_test.cc",
],
deps = [
- "//cheriot:riscv_cheriot",
+ "//cheriot:cheriot_state",
+ "//cheriot:riscv_cheriot_instructions",
"@com_google_absl//absl/random",
"@com_google_googletest//:gtest_main",
"@com_google_mpact-sim//mpact/sim/generic:core",
@@ -110,7 +118,8 @@
],
tags = ["not_run:arm"],
deps = [
- "//cheriot:riscv_cheriot",
+ "//cheriot:cheriot_state",
+ "//cheriot:riscv_cheriot_instructions",
"@com_google_absl//absl/log:check",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/types:span",
@@ -129,7 +138,7 @@
"riscv_cheriot_encoding_test.cc",
],
deps = [
- "//cheriot:riscv_cheriot",
+ "//cheriot:cheriot_state",
"//cheriot:riscv_cheriot_decoder",
"//cheriot:riscv_cheriot_isa",
"@com_google_googletest//:gtest_main",
@@ -145,7 +154,8 @@
"riscv_cheriot_a_instructions_test.cc",
],
deps = [
- "//cheriot:riscv_cheriot",
+ "//cheriot:cheriot_state",
+ "//cheriot:riscv_cheriot_instructions",
"@com_google_absl//absl/log:check",
"@com_google_absl//absl/strings:string_view",
"@com_google_googletest//:gtest_main",
@@ -163,7 +173,7 @@
],
deps = [
"//cheriot:cheriot_load_filter",
- "//cheriot:riscv_cheriot",
+ "//cheriot:cheriot_state",
"@com_google_absl//absl/log:check",
"@com_google_absl//absl/strings",
"@com_google_googletest//:gtest_main",
@@ -199,3 +209,314 @@
"@com_google_googletest//:gtest_main",
],
)
+
+cc_test(
+ name = "cheriot_rvv_fp_decoder_test",
+ size = "small",
+ srcs = ["cheriot_rvv_fp_decoder_test.cc"],
+ deps = [
+ "//cheriot:riscv_cheriot_rvv_fp_decoder",
+ "@com_google_absl//absl/log:log_sink_registry",
+ "@com_google_googletest//:gtest_main",
+ "@com_google_mpact-sim//mpact/sim/util/other:log_sink",
+ ],
+)
+
+cc_test(
+ name = "cheriot_rvv_decoder_test",
+ size = "small",
+ srcs = ["cheriot_rvv_decoder_test.cc"],
+ deps = [
+ "//cheriot:riscv_cheriot_rvv_decoder",
+ "@com_google_absl//absl/log:log_sink_registry",
+ "@com_google_googletest//:gtest_main",
+ "@com_google_mpact-sim//mpact/sim/util/other:log_sink",
+ ],
+)
+
+cc_library(
+ name = "riscv_cheriot_vector_instructions_test_base",
+ testonly = True,
+ hdrs = ["riscv_cheriot_vector_instructions_test_base.h"],
+ deps = [
+ "//cheriot:cheriot_state",
+ "//cheriot:cheriot_vector_state",
+ "//cheriot:riscv_cheriot_vector",
+ "@com_google_absl//absl/functional:bind_front",
+ "@com_google_absl//absl/random",
+ "@com_google_absl//absl/strings",
+ "@com_google_absl//absl/types:span",
+ "@com_google_mpact-riscv//riscv:riscv_state",
+ "@com_google_mpact-sim//mpact/sim/generic:arch_state",
+ "@com_google_mpact-sim//mpact/sim/generic:core",
+ "@com_google_mpact-sim//mpact/sim/generic:instruction",
+ "@com_google_mpact-sim//mpact/sim/generic:type_helpers",
+ "@com_google_mpact-sim//mpact/sim/util/memory",
+ ],
+)
+
+cc_library(
+ name = "riscv_vector_fp_test_utilities",
+ testonly = True,
+ hdrs = ["riscv_cheriot_vector_fp_test_utilities.h"],
+ deps = [
+ ":riscv_cheriot_vector_instructions_test_base",
+ "//cheriot:cheriot_state",
+ "@com_google_absl//absl/random",
+ "@com_google_absl//absl/strings",
+ "@com_google_absl//absl/types:span",
+ "@com_google_googletest//:gtest_for_library_testonly",
+ "@com_google_mpact-riscv//riscv:riscv_fp_state",
+ "@com_google_mpact-riscv//riscv:riscv_state",
+ "@com_google_mpact-sim//mpact/sim/generic:type_helpers",
+ ],
+)
+
+cc_test(
+ name = "riscv_cheriot_vector_true_test",
+ size = "small",
+ srcs = [
+ "riscv_cheriot_vector_true_test.cc",
+ ],
+ deps = [
+ "//cheriot:cheriot_state",
+ "//cheriot:cheriot_vector_state",
+ "@com_google_googletest//:gtest_main",
+ "@com_google_mpact-riscv//riscv:riscv_state",
+ "@com_google_mpact-sim//mpact/sim/util/memory",
+ ],
+)
+
+cc_test(
+ name = "riscv_cheriot_vector_memory_instructions_test",
+ size = "small",
+ srcs = [
+ "riscv_cheriot_vector_memory_instructions_test.cc",
+ ],
+ deps = [
+ "//cheriot:cheriot_state",
+ "//cheriot:cheriot_vector_state",
+ "//cheriot:riscv_cheriot_vector",
+ "@com_google_absl//absl/functional:bind_front",
+ "@com_google_absl//absl/log",
+ "@com_google_absl//absl/numeric:bits",
+ "@com_google_absl//absl/strings",
+ "@com_google_absl//absl/types:span",
+ "@com_google_googletest//:gtest_main",
+ "@com_google_mpact-riscv//riscv:riscv_state",
+ "@com_google_mpact-sim//mpact/sim/generic:arch_state",
+ "@com_google_mpact-sim//mpact/sim/generic:core",
+ "@com_google_mpact-sim//mpact/sim/generic:instruction",
+ "@com_google_mpact-sim//mpact/sim/util/memory",
+ ],
+)
+
+cc_test(
+ name = "riscv_cheriot_vector_opi_instructions_test",
+ size = "small",
+ srcs = [
+ "riscv_cheriot_vector_opi_instructions_test.cc",
+ ],
+ deps = [
+ ":riscv_cheriot_vector_instructions_test_base",
+ "//cheriot:cheriot_state",
+ "//cheriot:cheriot_vector_state",
+ "//cheriot:riscv_cheriot_vector",
+ "@com_google_absl//absl/functional:bind_front",
+ "@com_google_absl//absl/strings",
+ "@com_google_googletest//:gtest_main",
+ "@com_google_mpact-riscv//riscv:riscv_state",
+ "@com_google_mpact-sim//mpact/sim/generic:instruction",
+ ],
+)
+
+cc_test(
+ name = "riscv_cheriot_vector_opm_instructions_test",
+ size = "small",
+ srcs = [
+ "riscv_cheriot_vector_opm_instructions_test.cc",
+ ],
+ deps = [
+ ":riscv_cheriot_vector_instructions_test_base",
+ "//cheriot:cheriot_state",
+ "//cheriot:cheriot_vector_state",
+ "//cheriot:riscv_cheriot_vector",
+ "@com_google_absl//absl/base",
+ "@com_google_absl//absl/log:check",
+ "@com_google_absl//absl/numeric:int128",
+ "@com_google_absl//absl/random",
+ "@com_google_absl//absl/strings",
+ "@com_google_googletest//:gtest_main",
+ "@com_google_mpact-sim//mpact/sim/generic:instruction",
+ ],
+)
+
+cc_test(
+ name = "riscv_cheriot_vector_reduction_instructions_test",
+ size = "small",
+ srcs = [
+ "riscv_cheriot_vector_reduction_instructions_test.cc",
+ ],
+ deps = [
+ ":riscv_cheriot_vector_instructions_test_base",
+ "//cheriot:cheriot_state",
+ "//cheriot:cheriot_vector_state",
+ "//cheriot:riscv_cheriot_vector",
+ "@com_google_absl//absl/random",
+ "@com_google_absl//absl/strings",
+ "@com_google_googletest//:gtest_main",
+ "@com_google_mpact-riscv//riscv:riscv_state",
+ "@com_google_mpact-sim//mpact/sim/generic:instruction",
+ "@com_google_mpact-sim//mpact/sim/generic:type_helpers",
+ ],
+)
+
+cc_test(
+ name = "riscv_cheriot_vector_unary_instructions_test",
+ size = "small",
+ srcs = [
+ "riscv_cheriot_vector_unary_instructions_test.cc",
+ ],
+ deps = [
+ ":riscv_cheriot_vector_instructions_test_base",
+ "//cheriot:cheriot_state",
+ "//cheriot:riscv_cheriot_vector",
+ "@com_google_absl//absl/random",
+ "@com_google_absl//absl/types:span",
+ "@com_google_googletest//:gtest_main",
+ "@com_google_mpact-riscv//riscv:riscv_state",
+ "@com_google_mpact-sim//mpact/sim/generic:instruction",
+ "@com_google_mpact-sim//mpact/sim/generic:type_helpers",
+ ],
+)
+
+cc_test(
+ name = "riscv_cheriot_vector_fp_unary_instructions_test",
+ size = "small",
+ srcs = [
+ "riscv_cheriot_vector_fp_unary_instructions_test.cc",
+ ],
+ copts = [
+ "-ffp-model=strict",
+ ] + select({
+ "darwin_arm64_cpu": [],
+ "//conditions:default": ["-fprotect-parens"],
+ }),
+ deps = [
+ ":riscv_cheriot_vector_instructions_test_base",
+ ":riscv_vector_fp_test_utilities",
+ "//cheriot:cheriot_state",
+ "//cheriot:cheriot_vector_state",
+ "//cheriot:riscv_cheriot_vector_fp",
+ "@com_google_absl//absl/random",
+ "@com_google_absl//absl/strings",
+ "@com_google_absl//absl/types:span",
+ "@com_google_googletest//:gtest_main",
+ "@com_google_mpact-riscv//riscv:riscv_fp_state",
+ "@com_google_mpact-riscv//riscv:riscv_state",
+ "@com_google_mpact-sim//mpact/sim/generic:core",
+ "@com_google_mpact-sim//mpact/sim/generic:instruction",
+ "@com_google_mpact-sim//mpact/sim/generic:type_helpers",
+ ],
+)
+
+cc_test(
+ name = "riscv_cheriot_vector_fp_instructions_test",
+ size = "small",
+ srcs = [
+ "riscv_cheriot_vector_fp_instructions_test.cc",
+ ],
+ copts = [
+ "-ffp-model=strict",
+ ] + select({
+ "darwin_arm64_cpu": [],
+ "//conditions:default": ["-fprotect-parens"],
+ }),
+ deps = [
+ ":riscv_cheriot_vector_instructions_test_base",
+ ":riscv_vector_fp_test_utilities",
+ "//cheriot:cheriot_state",
+ "//cheriot:cheriot_vector_state",
+ "//cheriot:riscv_cheriot_vector_fp",
+ "@com_google_absl//absl/log",
+ "@com_google_absl//absl/random",
+ "@com_google_absl//absl/strings",
+ "@com_google_absl//absl/types:span",
+ "@com_google_googletest//:gtest_main",
+ "@com_google_mpact-riscv//riscv:riscv_fp_state",
+ "@com_google_mpact-riscv//riscv:riscv_state",
+ "@com_google_mpact-sim//mpact/sim/generic:instruction",
+ "@com_google_mpact-sim//mpact/sim/generic:type_helpers",
+ ],
+)
+
+cc_test(
+ name = "riscv_cheriot_vector_fp_compare_instructions_test",
+ size = "small",
+ srcs = [
+ "riscv_cheriot_vector_fp_compare_instructions_test.cc",
+ ],
+ copts = [
+ "-ffp-model=strict",
+ ] + select({
+ "darwin_arm64_cpu": [],
+ "//conditions:default": ["-fprotect-parens"],
+ }),
+ deps = [
+ ":riscv_cheriot_vector_instructions_test_base",
+ ":riscv_vector_fp_test_utilities",
+ "//cheriot:cheriot_state",
+ "//cheriot:riscv_cheriot_vector_fp",
+ "@com_google_absl//absl/random",
+ "@com_google_absl//absl/strings",
+ "@com_google_absl//absl/types:span",
+ "@com_google_googletest//:gtest_main",
+ "@com_google_mpact-riscv//riscv:riscv_fp_state",
+ "@com_google_mpact-riscv//riscv:riscv_state",
+ "@com_google_mpact-sim//mpact/sim/generic:instruction",
+ ],
+)
+
+cc_test(
+ name = "riscv_cheriot_vector_fp_reduction_instructions_test",
+ size = "small",
+ srcs = [
+ "riscv_cheriot_vector_fp_reduction_instructions_test.cc",
+ ],
+ copts = [
+ "-ffp-model=strict",
+ ] + select({
+ "darwin_arm64_cpu": [],
+ "//conditions:default": ["-fprotect-parens"],
+ }),
+ deps = [
+ ":riscv_cheriot_vector_instructions_test_base",
+ ":riscv_vector_fp_test_utilities",
+ "//cheriot:cheriot_state",
+ "//cheriot:riscv_cheriot_vector_fp",
+ "@com_google_absl//absl/random",
+ "@com_google_absl//absl/strings",
+ "@com_google_absl//absl/types:span",
+ "@com_google_googletest//:gtest_main",
+ "@com_google_mpact-riscv//riscv:riscv_fp_state",
+ "@com_google_mpact-riscv//riscv:riscv_state",
+ "@com_google_mpact-sim//mpact/sim/generic:instruction",
+ ],
+)
+
+cc_test(
+ name = "riscv_cheriot_vector_permute_instructions_test",
+ size = "small",
+ srcs = [
+ "riscv_cheriot_vector_permute_instructions_test.cc",
+ ],
+ deps = [
+ ":riscv_cheriot_vector_instructions_test_base",
+ "//cheriot:cheriot_state",
+ "//cheriot:riscv_cheriot_vector",
+ "@com_google_absl//absl/random",
+ "@com_google_googletest//:gtest_main",
+ "@com_google_mpact-riscv//riscv:riscv_state",
+ "@com_google_mpact-sim//mpact/sim/generic:instruction",
+ ],
+)
diff --git a/cheriot/test/cheriot_rvv_decoder_test.cc b/cheriot/test/cheriot_rvv_decoder_test.cc
new file mode 100644
index 0000000..d3aa53d
--- /dev/null
+++ b/cheriot/test/cheriot_rvv_decoder_test.cc
@@ -0,0 +1,20 @@
+#include "cheriot/cheriot_rvv_decoder.h"
+
+#include "absl/log/log_sink_registry.h"
+#include "googlemock/include/gmock/gmock.h"
+#include "mpact/sim/util/other/log_sink.h"
+
+namespace {
+
+using ::mpact::sim::cheriot::CheriotRVVDecoder;
+using ::mpact::sim::util::LogSink;
+
+TEST(RiscvCheriotRvvDecoderTest, Instantiation) {
+ LogSink log_sink;
+ absl::AddLogSink(&log_sink);
+ CheriotRVVDecoder decoder(nullptr, nullptr);
+ EXPECT_EQ(log_sink.num_error(), 0);
+ absl::RemoveLogSink(&log_sink);
+}
+
+} // namespace
diff --git a/cheriot/test/cheriot_rvv_fp_decoder_test.cc b/cheriot/test/cheriot_rvv_fp_decoder_test.cc
new file mode 100644
index 0000000..68cfce7
--- /dev/null
+++ b/cheriot/test/cheriot_rvv_fp_decoder_test.cc
@@ -0,0 +1,20 @@
+#include "cheriot/cheriot_rvv_fp_decoder.h"
+
+#include "absl/log/log_sink_registry.h"
+#include "googlemock/include/gmock/gmock.h"
+#include "mpact/sim/util/other/log_sink.h"
+
+namespace {
+
+using ::mpact::sim::cheriot::CheriotRVVFPDecoder;
+using ::mpact::sim::util::LogSink;
+
+TEST(RiscvCheriotRvvFpDecoderTest, Instantiation) {
+ LogSink log_sink;
+ absl::AddLogSink(&log_sink);
+ CheriotRVVFPDecoder decoder(nullptr, nullptr);
+ EXPECT_EQ(log_sink.num_error(), 0);
+ absl::RemoveLogSink(&log_sink);
+}
+
+} // namespace
diff --git a/cheriot/test/riscv_cheriot_encoding_test.cc b/cheriot/test/riscv_cheriot_encoding_test.cc
index 31604ba..4226c71 100644
--- a/cheriot/test/riscv_cheriot_encoding_test.cc
+++ b/cheriot/test/riscv_cheriot_encoding_test.cc
@@ -36,8 +36,6 @@
// RV32I
constexpr uint32_t kLui = 0b0000000000000000000000000'0110111;
-constexpr uint32_t kJal = 0b00000000000000000000'00000'1101111;
-constexpr uint32_t kJalr = 0b00000000000'00000'000'00000'1100111;
constexpr uint32_t kBeq = 0b0000000'00000'00000'000'00000'1100011;
constexpr uint32_t kBne = 0b0000000'00000'00000'001'00000'1100011;
constexpr uint32_t kBlt = 0b0000000'00000'00000'100'00000'1100011;
@@ -204,8 +202,6 @@
};
constexpr int kRdValue = 1;
-constexpr int kSuccValue = 0xf;
-constexpr int kPredValue = 0xf;
static uint32_t SetRd(uint32_t iword, uint32_t rdval) {
return (iword | ((rdval & 0x1f) << 7));
@@ -219,14 +215,6 @@
return (iword | ((rsval & 0x1f) << 20));
}
-static uint32_t SetPred(uint32_t iword, uint32_t pred) {
- return (iword | ((pred & 0xf) << 24));
-}
-
-static uint32_t SetSucc(uint32_t iword, uint32_t succ) {
- return (iword | ((succ & 0xf) << 20));
-}
-
static uint32_t Set16Rd(uint32_t iword, uint32_t val) {
return (iword | ((val & 0x1f) << 7));
}
@@ -412,25 +400,25 @@
enc_->ParseInstruction(Set16Rd(kClwsp, 1));
EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscv32Cheriot, 0), OpcodeEnum::kClwsp);
enc_->ParseInstruction(Set16Rd(kCldsp, 1));
- EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscv32Cheriot, 0), OpcodeEnum::kCldsp);
+ EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscv32Cheriot, 0), OpcodeEnum::kClcsp);
// enc_->ParseInstruction(kCdldsp);
// EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscv32Cheriot, 0),
// OpcodeEnum::kCfldsp);
enc_->ParseInstruction(kCswsp);
EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscv32Cheriot, 0), OpcodeEnum::kCswsp);
enc_->ParseInstruction(kCsdsp);
- EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscv32Cheriot, 0), OpcodeEnum::kCsdsp);
+ EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscv32Cheriot, 0), OpcodeEnum::kCscsp);
// enc_->ParseInstruction(kCdsdsp);
// EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscv32Cheriot, 0),
// OpcodeEnum::kCfsdsp);
enc_->ParseInstruction(kClw);
EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscv32Cheriot, 0), OpcodeEnum::kClw);
enc_->ParseInstruction(kCld);
- EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscv32Cheriot, 0), OpcodeEnum::kCld);
+ EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscv32Cheriot, 0), OpcodeEnum::kClc);
enc_->ParseInstruction(kCsw);
EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscv32Cheriot, 0), OpcodeEnum::kCsw);
enc_->ParseInstruction(kCsd);
- EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscv32Cheriot, 0), OpcodeEnum::kCsd);
+ EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscv32Cheriot, 0), OpcodeEnum::kCsc);
// enc_->ParseInstruction(kCdsd);
// EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscv32Cheriot, 0),
// OpcodeEnum::kCdsd);
diff --git a/cheriot/test/riscv_cheriot_vector_fp_compare_instructions_test.cc b/cheriot/test/riscv_cheriot_vector_fp_compare_instructions_test.cc
new file mode 100644
index 0000000..1fd6a2b
--- /dev/null
+++ b/cheriot/test/riscv_cheriot_vector_fp_compare_instructions_test.cc
@@ -0,0 +1,444 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cheriot/riscv_cheriot_vector_fp_compare_instructions.h"
+
+#include <algorithm>
+#include <cstdint>
+#include <functional>
+
+#include "absl/random/random.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
+#include "absl/types/span.h"
+#include "cheriot/test/riscv_cheriot_vector_fp_test_utilities.h"
+#include "cheriot/test/riscv_cheriot_vector_instructions_test_base.h"
+#include "googlemock/include/gmock/gmock.h"
+#include "mpact/sim/generic/instruction.h"
+#include "riscv//riscv_fp_host.h"
+#include "riscv//riscv_fp_state.h"
+#include "riscv//riscv_register.h"
+
+// This file contains the tests of the instruction semantic functions for
+// RiscV vector floating point compare instructions.
+namespace {
+
+using Instruction = ::mpact::sim::generic::Instruction;
+
+// The semantic functions.
+using ::mpact::sim::cheriot::Vmfeq;
+using ::mpact::sim::cheriot::Vmfge;
+using ::mpact::sim::cheriot::Vmfgt;
+using ::mpact::sim::cheriot::Vmfle;
+using ::mpact::sim::cheriot::Vmflt;
+using ::mpact::sim::cheriot::Vmfne;
+
+// Needed types.
+using ::absl::Span;
+using ::mpact::sim::riscv::RVFpRegister;
+using ::mpact::sim::riscv::ScopedFPStatus;
+
+class RiscVCheriotFPCompareInstructionsTest
+ : public RiscVCheriotFPInstructionsTestBase {
+ public:
+ // Helper function for testing binary mask vector-vector instructions that
+ // use the mask bit.
+ template <typename Vs2, typename Vs1>
+ void BinaryMaskFPOpWithMaskTestHelperVV(
+ absl::string_view name, int sew, Instruction *inst,
+ std::function<uint8_t(Vs2, Vs1, bool)> operation) {
+ int byte_sew = sew / 8;
+ if (byte_sew != sizeof(Vs2) && byte_sew != sizeof(Vs1)) {
+ FAIL() << name << ": selected element width != any operand types"
+ << "sew: " << sew << " Vs2: " << sizeof(Vs2)
+ << " Vs1: " << sizeof(Vs1);
+ return;
+ }
+ // Number of elements per vector register.
+ constexpr int vs2_size = kVectorLengthInBytes / sizeof(Vs2);
+ constexpr int vs1_size = kVectorLengthInBytes / sizeof(Vs1);
+ // Input values for 8 registers.
+ Vs2 vs2_value[vs2_size * 8];
+ auto vs2_span = Span<Vs2>(vs2_value);
+ Vs1 vs1_value[vs1_size * 8];
+ auto vs1_span = Span<Vs1>(vs1_value);
+ AppendVectorRegisterOperands({kVs2, kVs1, kVmask}, {kVd});
+ // Initialize input values.
+ FillArrayWithRandomFPValues<Vs2>(vs2_span);
+ FillArrayWithRandomFPValues<Vs1>(vs1_span);
+ // Overwrite the first few values of the input data with infinities,
+ // zeros, denormals and NaNs.
+ using Vs2Int = typename FPTypeInfo<Vs2>::IntType;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[0]) = FPTypeInfo<Vs2>::kQNaN;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[1]) = FPTypeInfo<Vs2>::kSNaN;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[2]) = FPTypeInfo<Vs2>::kPosInf;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[3]) = FPTypeInfo<Vs2>::kNegInf;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[4]) = FPTypeInfo<Vs2>::kPosZero;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[5]) = FPTypeInfo<Vs2>::kNegZero;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[6]) = FPTypeInfo<Vs2>::kPosDenorm;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[7]) = FPTypeInfo<Vs2>::kNegDenorm;
+ // Make every third value the same (at least if the types are same sized).
+ for (int i = 0; i < std::min(vs1_size, vs2_size); i += 3) {
+ vs1_span[i] = static_cast<Vs1>(vs2_span[i]);
+ }
+
+ SetVectorRegisterValues<uint8_t>(
+ {{kVmaskName, Span<const uint8_t>(kA5Mask)}});
+ // Modify the first mask bits to use each of the special floating point
+ // values.
+ vreg_[kVmask]->data_buffer()->Set<uint8_t>(0, 0xff);
+
+ for (int i = 0; i < 8; i++) {
+ auto vs2_name = absl::StrCat("v", kVs2 + i);
+ SetVectorRegisterValues<Vs2>(
+ {{vs2_name, vs2_span.subspan(vs2_size * i, vs2_size)}});
+ auto vs1_name = absl::StrCat("v", kVs1 + i);
+ SetVectorRegisterValues<Vs1>(
+ {{vs1_name, vs1_span.subspan(vs1_size * i, vs1_size)}});
+ }
+ for (int lmul_index = 0; lmul_index < 7; lmul_index++) {
+ for (int vstart_count = 0; vstart_count < 4; vstart_count++) {
+ for (int vlen_count = 0; vlen_count < 4; vlen_count++) {
+ ClearVectorRegisterGroup(kVd, 8);
+ int lmul8 = kLmul8Values[lmul_index];
+ int lmul8_vs2 = lmul8 * sizeof(Vs2) / byte_sew;
+ int num_values = lmul8 * kVectorLengthInBytes / (8 * byte_sew);
+ int vstart = 0;
+ if (vstart_count > 0) {
+ vstart = absl::Uniform(absl::IntervalOpen, bitgen_, 0, num_values);
+ }
+ // Set vlen, but leave vlen high at least once.
+ int vlen = 1024;
+ if (vlen_count > 0) {
+ vlen = absl::Uniform(absl::IntervalOpenClosed, bitgen_, vstart,
+ num_values);
+ }
+ num_values = std::min(num_values, vlen);
+ ASSERT_TRUE(vlen > vstart);
+ // Configure vector unit for different lmul settings.
+ uint32_t vtype = (kSewSettingsByByteSize[byte_sew] << 3) |
+ kLmulSettings[lmul_index];
+ ConfigureVectorUnit(vtype, vlen);
+ rv_vector_->set_vstart(vstart);
+
+ inst->Execute();
+ if ((lmul8_vs2 < 1) || (lmul8_vs2 > 64)) {
+ EXPECT_TRUE(rv_vector_->vector_exception());
+ rv_vector_->clear_vector_exception();
+ continue;
+ }
+
+ EXPECT_FALSE(rv_vector_->vector_exception());
+ EXPECT_EQ(rv_vector_->vstart(), 0);
+ auto dest_span = vreg_[kVd]->data_buffer()->Get<uint8_t>();
+ for (int i = 0; i < kVectorLengthInBytes * 8; i++) {
+ int mask_index = i >> 3;
+ int mask_offset = i & 0b111;
+ bool mask_value = true;
+ if (mask_index > 0) {
+ mask_value = ((kA5Mask[mask_index] >> mask_offset) & 0b1) != 0;
+ }
+ uint8_t inst_value = dest_span[i >> 3];
+ inst_value = (inst_value >> mask_offset) & 0b1;
+ if ((i >= vstart) && (i < num_values)) {
+ // Set rounding mode and perform the computation.
+ ScopedFPStatus set_fpstatus(rv_fp_->host_fp_interface());
+ uint8_t expected_value =
+ operation(vs2_value[i], vs1_value[i], mask_value);
+ auto int_vs2_val =
+ *reinterpret_cast<typename FPTypeInfo<Vs2>::IntType *>(
+ &vs2_value[i]);
+ auto int_vs1_val =
+ *reinterpret_cast<typename FPTypeInfo<Vs1>::IntType *>(
+ &vs1_value[i]);
+ EXPECT_EQ(expected_value, inst_value)
+ << absl::StrCat(name, "[", i, "] op(", vs2_value[i], "[0x",
+ absl::Hex(int_vs2_val), "], ", vs1_value[i],
+ "[0x", absl::Hex(int_vs1_val), "])");
+ } else {
+ EXPECT_EQ(0, inst_value) << absl::StrCat(
+ name, "[", i, "] 0 != reg[][", i, "] lmul8(", lmul8,
+ ") vstart(", vstart, ") num_values(", num_values, ")");
+ }
+ }
+ if (HasFailure()) return;
+ }
+ }
+ }
+ }
+
+ // Helper function for testing binary mask vector-vector instructions that do
+ // not use the mask bit.
+ template <typename Vs2, typename Vs1>
+ void BinaryMaskFPOpTestHelperVV(absl::string_view name, int sew,
+ Instruction *inst,
+ std::function<uint8_t(Vs2, Vs1)> operation) {
+ BinaryMaskFPOpWithMaskTestHelperVV<Vs2, Vs1>(
+ name, sew, inst,
+ [operation](Vs2 vs2, Vs1 vs1, bool mask_value) -> uint8_t {
+ if (mask_value) {
+ return operation(vs2, vs1);
+ }
+ return 0;
+ });
+ }
+
+ // Helper function for testing mask vector-scalar/immediate instructions that
+ // use the mask bit.
+ template <typename Vs2, typename Fs1>
+ void BinaryMaskFPOpWithMaskTestHelperVX(
+ absl::string_view name, int sew, Instruction *inst,
+ std::function<uint8_t(Vs2, Fs1, bool)> operation) {
+ int byte_sew = sew / 8;
+ if (byte_sew != sizeof(Vs2) && byte_sew != sizeof(Fs1)) {
+ FAIL() << name << ": selected element width != any operand types"
+ << "sew: " << sew << " Vs2: " << sizeof(Vs2)
+ << " Rs1: " << sizeof(Fs1);
+ return;
+ }
+ // Number of elements per vector register.
+ constexpr int vs2_size = kVectorLengthInBytes / sizeof(Vs2);
+ // Input values for 8 registers.
+ Vs2 vs2_value[vs2_size * 8];
+ auto vs2_span = Span<Vs2>(vs2_value);
+ AppendVectorRegisterOperands({kVs2}, {});
+ AppendRegisterOperands({kFs1Name}, {});
+ AppendVectorRegisterOperands({kVmask}, {kVd});
+ // Initialize input values.
+ FillArrayWithRandomValues<Vs2>(vs2_span);
+ SetVectorRegisterValues<uint8_t>(
+ {{kVmaskName, Span<const uint8_t>(kA5Mask)}});
+ for (int i = 0; i < 8; i++) {
+ auto vs2_name = absl::StrCat("v", kVs2 + i);
+ SetVectorRegisterValues<Vs2>(
+ {{vs2_name, vs2_span.subspan(vs2_size * i, vs2_size)}});
+ }
+ for (int lmul_index = 0; lmul_index < 7; lmul_index++) {
+ for (int vstart_count = 0; vstart_count < 4; vstart_count++) {
+ for (int vlen_count = 0; vlen_count < 4; vlen_count++) {
+ ClearVectorRegisterGroup(kVd, 8);
+ int lmul8 = kLmul8Values[lmul_index];
+ int lmul8_vs2 = lmul8 * sizeof(Vs2) / byte_sew;
+ int num_values = lmul8 * kVectorLengthInBytes / (8 * byte_sew);
+ int vstart = 0;
+ if (vstart_count > 0) {
+ vstart = absl::Uniform(absl::IntervalOpen, bitgen_, 0, num_values);
+ }
+ // Set vlen, but leave vlen high at least once.
+ int vlen = 1024;
+ if (vlen_count > 0) {
+ vlen = absl::Uniform(absl::IntervalOpenClosed, bitgen_, vstart,
+ num_values);
+ }
+ num_values = std::min(num_values, vlen);
+ ASSERT_TRUE(vlen > vstart);
+ // Configure vector unit for different lmul settings.
+ uint32_t vtype = (kSewSettingsByByteSize[byte_sew] << 3) |
+ kLmulSettings[lmul_index];
+ ConfigureVectorUnit(vtype, vlen);
+ rv_vector_->set_vstart(vstart);
+
+ // Generate a new rs1 value.
+ Fs1 fs1_value = RandomFPValue<Fs1>();
+ // Need to NaN box the value, that is, if the register value type is
+ // wider than the data type for a floating point value, the upper bits
+ // are all set to 1's.
+ typename RVFpRegister::ValueType fs1_reg_value =
+ NaNBox<Fs1, typename RVFpRegister::ValueType>(fs1_value);
+ SetRegisterValues<typename RVFpRegister::ValueType, RVFpRegister>(
+ {{kFs1Name, fs1_reg_value}});
+
+ inst->Execute();
+ if ((lmul8_vs2 < 1) || (lmul8_vs2 > 64)) {
+ EXPECT_TRUE(rv_vector_->vector_exception());
+ rv_vector_->clear_vector_exception();
+ continue;
+ }
+
+ EXPECT_FALSE(rv_vector_->vector_exception());
+ EXPECT_EQ(rv_vector_->vstart(), 0);
+ auto dest_span = vreg_[kVd]->data_buffer()->Get<uint8_t>();
+ for (int i = 0; i < kVectorLengthInBytes * 8; i++) {
+ int mask_index = i >> 3;
+ int mask_offset = i & 0b111;
+ bool mask_value = ((kA5Mask[mask_index] >> mask_offset) & 0b1) != 0;
+ uint8_t inst_value = dest_span[i >> 3];
+ inst_value = (inst_value >> mask_offset) & 0b1;
+ if ((i >= vstart) && (i < num_values)) {
+ // Set rounding mode and perform the computation.
+
+ ScopedFPStatus set_fpstatus(rv_fp_->host_fp_interface());
+ uint8_t expected_value =
+ operation(vs2_value[i], fs1_value, mask_value);
+ auto int_vs2_val =
+ *reinterpret_cast<typename FPTypeInfo<Vs2>::IntType *>(
+ &vs2_value[i]);
+ auto int_fs1_val =
+ *reinterpret_cast<typename FPTypeInfo<Fs1>::IntType *>(
+ &fs1_value);
+ EXPECT_EQ(expected_value, inst_value)
+ << absl::StrCat(name, "[", i, "] op(", vs2_value[i], "[0x",
+ absl::Hex(int_vs2_val), "], ", fs1_value,
+ "[0x", absl::Hex(int_fs1_val), "])");
+ } else {
+ EXPECT_EQ(0, inst_value) << absl::StrCat(
+ name, " 0 != reg[0][", i, "] lmul8(", lmul8, ")");
+ }
+ }
+ if (HasFailure()) return;
+ }
+ }
+ }
+ }
+
+ // Helper function for testing mask vector-vector instructions that do not
+ // use the mask bit.
+ template <typename Vs2, typename Fs1>
+ void BinaryMaskFPOpTestHelperVX(absl::string_view name, int sew,
+ Instruction *inst,
+ std::function<uint8_t(Vs2, Fs1)> operation) {
+ BinaryMaskFPOpWithMaskTestHelperVX<Vs2, Fs1>(
+ name, sew, inst,
+ [operation](Vs2 vs2, Fs1 fs1, bool mask_value) -> uint8_t {
+ if (mask_value) {
+ return operation(vs2, fs1);
+ }
+ return 0;
+ });
+ }
+};
+
+// Testing vector floating point compare instructions.
+
+// Vector floating point compare equal.
+TEST_F(RiscVCheriotFPCompareInstructionsTest, Vmfeq) {
+ SetSemanticFunction(&Vmfeq);
+ BinaryMaskFPOpTestHelperVV<float, float>(
+ "Vmfeq_vv32", /*sew*/ 32, instruction_,
+ [](float vs2, float vs1) -> uint8_t { return (vs2 == vs1) ? 1 : 0; });
+ ResetInstruction();
+ SetSemanticFunction(&Vmfeq);
+ BinaryMaskFPOpTestHelperVX<float, float>(
+ "Vmfeq_vx32", /*sew*/ 32, instruction_,
+ [](float vs2, float vs1) -> uint8_t { return (vs2 == vs1) ? 1 : 0; });
+ ResetInstruction();
+ SetSemanticFunction(&Vmfeq);
+ BinaryMaskFPOpTestHelperVV<double, double>(
+ "Vmfeq_vv64", /*sew*/ 64, instruction_,
+ [](double vs2, double vs1) -> uint8_t { return (vs2 == vs1) ? 1 : 0; });
+ ResetInstruction();
+ SetSemanticFunction(&Vmfeq);
+ BinaryMaskFPOpTestHelperVX<double, double>(
+ "Vmfeq_vx64", /*sew*/ 64, instruction_,
+ [](double vs2, double vs1) -> uint8_t { return (vs2 == vs1) ? 1 : 0; });
+}
+
+// Vector floating point compare less than or equal.
+TEST_F(RiscVCheriotFPCompareInstructionsTest, Vmfle) {
+ SetSemanticFunction(&Vmfle);
+ BinaryMaskFPOpTestHelperVV<float, float>(
+ "Vmfle_vv32", /*sew*/ 32, instruction_,
+ [](float vs2, float vs1) -> uint8_t { return (vs2 <= vs1) ? 1 : 0; });
+ ResetInstruction();
+ SetSemanticFunction(&Vmfle);
+ BinaryMaskFPOpTestHelperVX<float, float>(
+ "Vmfle_vx32", /*sew*/ 32, instruction_,
+ [](float vs2, float vs1) -> uint8_t { return (vs2 <= vs1) ? 1 : 0; });
+ ResetInstruction();
+ SetSemanticFunction(&Vmfle);
+ BinaryMaskFPOpTestHelperVV<double, double>(
+ "Vmfle_vv64", /*sew*/ 64, instruction_,
+ [](double vs2, double vs1) -> uint8_t { return (vs2 <= vs1) ? 1 : 0; });
+ ResetInstruction();
+ SetSemanticFunction(&Vmfle);
+ BinaryMaskFPOpTestHelperVX<double, double>(
+ "Vmfle_vx64", /*sew*/ 64, instruction_,
+ [](double vs2, double vs1) -> uint8_t { return (vs2 <= vs1) ? 1 : 0; });
+}
+
+// Vector floating point compare less than.
+TEST_F(RiscVCheriotFPCompareInstructionsTest, Vmflt) {
+ SetSemanticFunction(&Vmflt);
+ BinaryMaskFPOpTestHelperVV<float, float>(
+ "Vmflt_vv32", /*sew*/ 32, instruction_,
+ [](float vs2, float vs1) -> uint8_t { return (vs2 < vs1) ? 1 : 0; });
+ ResetInstruction();
+ SetSemanticFunction(&Vmflt);
+ BinaryMaskFPOpTestHelperVX<float, float>(
+ "Vmflt_vx32", /*sew*/ 32, instruction_,
+ [](float vs2, float vs1) -> uint8_t { return (vs2 < vs1) ? 1 : 0; });
+ ResetInstruction();
+ SetSemanticFunction(&Vmflt);
+ BinaryMaskFPOpTestHelperVV<double, double>(
+ "Vmflt_vv64", /*sew*/ 64, instruction_,
+ [](double vs2, double vs1) -> uint8_t { return (vs2 < vs1) ? 1 : 0; });
+ ResetInstruction();
+ SetSemanticFunction(&Vmflt);
+ BinaryMaskFPOpTestHelperVX<double, double>(
+ "Vmflt_vx64", /*sew*/ 64, instruction_,
+ [](double vs2, double vs1) -> uint8_t { return (vs2 < vs1) ? 1 : 0; });
+}
+
+// Vector floating point compare not equal.
+TEST_F(RiscVCheriotFPCompareInstructionsTest, Vmfne) {
+ SetSemanticFunction(&Vmfne);
+ BinaryMaskFPOpTestHelperVV<float, float>(
+ "Vmfne_vv32", /*sew*/ 32, instruction_,
+ [](float vs2, float vs1) -> uint8_t { return (vs2 != vs1) ? 1 : 0; });
+ ResetInstruction();
+ SetSemanticFunction(&Vmfne);
+ BinaryMaskFPOpTestHelperVX<float, float>(
+ "Vmfne_vx32", /*sew*/ 32, instruction_,
+ [](float vs2, float vs1) -> uint8_t { return (vs2 != vs1) ? 1 : 0; });
+ ResetInstruction();
+ SetSemanticFunction(&Vmfne);
+ BinaryMaskFPOpTestHelperVV<double, double>(
+ "Vmfne_vv64", /*sew*/ 64, instruction_,
+ [](double vs2, double vs1) -> uint8_t { return (vs2 != vs1) ? 1 : 0; });
+ ResetInstruction();
+ SetSemanticFunction(&Vmfne);
+ BinaryMaskFPOpTestHelperVX<double, double>(
+ "Vmfne_vx64", /*sew*/ 64, instruction_,
+ [](double vs2, double vs1) -> uint8_t { return (vs2 != vs1) ? 1 : 0; });
+}
+
+// Vector floating point compare greater than (used for Vector-Scalar
+// comparisons).
+TEST_F(RiscVCheriotFPCompareInstructionsTest, Vmfgt) {
+ SetSemanticFunction(&Vmfgt);
+ BinaryMaskFPOpTestHelperVX<float, float>(
+ "Vmfgt_vx32", /*sew*/ 32, instruction_,
+ [](float vs2, float vs1) -> uint8_t { return (vs2 > vs1) ? 1 : 0; });
+ ResetInstruction();
+ SetSemanticFunction(&Vmfgt);
+ BinaryMaskFPOpTestHelperVX<double, double>(
+ "Vmfgt_vx64", /*sew*/ 64, instruction_,
+ [](double vs2, double vs1) -> uint8_t { return (vs2 > vs1) ? 1 : 0; });
+}
+
+// Vector floating point compare greater than or equal (used for Vector-Scalar
+// comparisons).
+TEST_F(RiscVCheriotFPCompareInstructionsTest, Vmfge) {
+ SetSemanticFunction(&Vmfge);
+ BinaryMaskFPOpTestHelperVX<float, float>(
+ "Vmfge_vx32", /*sew*/ 32, instruction_,
+ [](float vs2, float vs1) -> uint8_t { return (vs2 >= vs1) ? 1 : 0; });
+ ResetInstruction();
+ SetSemanticFunction(&Vmfge);
+ BinaryMaskFPOpTestHelperVX<double, double>(
+ "Vmfge_vx64", /*sew*/ 64, instruction_,
+ [](double vs2, double vs1) -> uint8_t { return (vs2 >= vs1) ? 1 : 0; });
+}
+
+} // namespace
diff --git a/cheriot/test/riscv_cheriot_vector_fp_instructions_test.cc b/cheriot/test/riscv_cheriot_vector_fp_instructions_test.cc
new file mode 100644
index 0000000..00f78c5
--- /dev/null
+++ b/cheriot/test/riscv_cheriot_vector_fp_instructions_test.cc
@@ -0,0 +1,1376 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cheriot/riscv_cheriot_vector_fp_instructions.h"
+
+#include <algorithm>
+#include <cmath>
+#include <cstdint>
+#include <functional>
+#include <tuple>
+#include <vector>
+
+#include "absl/random/random.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
+#include "absl/types/span.h"
+#include "cheriot/test/riscv_cheriot_vector_fp_test_utilities.h"
+#include "cheriot/test/riscv_cheriot_vector_instructions_test_base.h"
+#include "googlemock/include/gmock/gmock.h"
+#include "mpact/sim/generic/instruction.h"
+#include "mpact/sim/generic/type_helpers.h"
+#include "riscv//riscv_fp_host.h"
+#include "riscv//riscv_fp_info.h"
+#include "riscv//riscv_fp_state.h"
+#include "riscv//riscv_register.h"
+
+namespace {
+
+using Instruction = ::mpact::sim::generic::Instruction;
+using ::mpact::sim::generic::operator*; // NOLINT: used below.
+
+// Functions to test.
+using ::mpact::sim::cheriot::Vfadd;
+using ::mpact::sim::cheriot::Vfdiv;
+using ::mpact::sim::cheriot::Vfmacc;
+using ::mpact::sim::cheriot::Vfmadd;
+using ::mpact::sim::cheriot::Vfmax;
+using ::mpact::sim::cheriot::Vfmerge;
+using ::mpact::sim::cheriot::Vfmin;
+using ::mpact::sim::cheriot::Vfmsac;
+using ::mpact::sim::cheriot::Vfmsub;
+using ::mpact::sim::cheriot::Vfmul;
+using ::mpact::sim::cheriot::Vfnmacc;
+using ::mpact::sim::cheriot::Vfnmadd;
+using ::mpact::sim::cheriot::Vfnmsac;
+using ::mpact::sim::cheriot::Vfnmsub;
+using ::mpact::sim::cheriot::Vfrdiv;
+using ::mpact::sim::cheriot::Vfrsub;
+using ::mpact::sim::cheriot::Vfsgnj;
+using ::mpact::sim::cheriot::Vfsgnjn;
+using ::mpact::sim::cheriot::Vfsgnjx;
+using ::mpact::sim::cheriot::Vfsub;
+using ::mpact::sim::cheriot::Vfwadd;
+using ::mpact::sim::cheriot::Vfwaddw;
+using ::mpact::sim::cheriot::Vfwmacc;
+using ::mpact::sim::cheriot::Vfwmsac;
+using ::mpact::sim::cheriot::Vfwmul;
+using ::mpact::sim::cheriot::Vfwnmacc;
+using ::mpact::sim::cheriot::Vfwnmsac;
+using ::mpact::sim::cheriot::Vfwsub;
+using ::mpact::sim::cheriot::Vfwsubw;
+
+using ::absl::Span;
+using ::mpact::sim::riscv::FPExceptions;
+using ::mpact::sim::riscv::FPRoundingMode;
+using ::mpact::sim::riscv::RVFpRegister;
+using ::mpact::sim::riscv::ScopedFPStatus;
+
+// Test fixture for binary fp instructions.
+class RiscVCheriotFPInstructionsTest
+ : public RiscVCheriotFPInstructionsTestBase {
+ public:
+ // Floating point test needs to ensure to use the fp special values (inf, NaN
+ // etc.) during testing, not just random values.
+ template <typename Vd, typename Vs2, typename Vs1>
+ void TernaryOpFPTestHelperVV(absl::string_view name, int sew,
+ Instruction *inst, int delta_position,
+ std::function<Vd(Vs2, Vs1, Vd)> operation) {
+ int byte_sew = sew / 8;
+ if (byte_sew != sizeof(Vd) && byte_sew != sizeof(Vs2) &&
+ byte_sew != sizeof(Vs1)) {
+ FAIL() << name << ": selected element width != any operand types"
+ << " sew: " << sew << " Vd: " << sizeof(Vd)
+ << " Vs2: " << sizeof(Vs2) << " Vs1: " << sizeof(Vs1);
+ return;
+ }
+ // Number of elements per vector register.
+ constexpr int vs2_size = kVectorLengthInBytes / sizeof(Vs2);
+ constexpr int vs1_size = kVectorLengthInBytes / sizeof(Vs1);
+ constexpr int vd_size = kVectorLengthInBytes / sizeof(Vd);
+ // Input values for 8 registers.
+ Vs2 vs2_value[vs2_size * 8];
+ Vs1 vs1_value[vs1_size * 8];
+ Vd vd_value[vd_size * 8];
+ auto vs2_span = Span<Vs2>(vs2_value);
+ auto vs1_span = Span<Vs1>(vs1_value);
+ auto vd_span = Span<Vd>(vd_value);
+ AppendVectorRegisterOperands({kVs2, kVs1, kVd, kVmask}, {kVd});
+ SetVectorRegisterValues<uint8_t>(
+ {{kVmaskName, Span<const uint8_t>(kA5Mask)}});
+ // Iterate across different lmul values.
+ for (int lmul_index = 0; lmul_index < 7; lmul_index++) {
+ // Initialize input values.
+ FillArrayWithRandomFPValues<Vs2>(vs2_span);
+ FillArrayWithRandomFPValues<Vs1>(vs1_span);
+ FillArrayWithRandomFPValues<Vd>(vd_span);
+ using Vs2Int = typename FPTypeInfo<Vs2>::IntType;
+ using Vs1Int = typename FPTypeInfo<Vs1>::IntType;
+ using VdInt = typename FPTypeInfo<Vd>::IntType;
+ // Overwrite the first few values of the input data with infinities,
+ // zeros, denormals and NaNs.
+ *reinterpret_cast<Vs2Int *>(&vs2_span[0]) = FPTypeInfo<Vs2>::kQNaN;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[1]) = FPTypeInfo<Vs2>::kSNaN;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[2]) = FPTypeInfo<Vs2>::kPosInf;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[3]) = FPTypeInfo<Vs2>::kNegInf;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[4]) = FPTypeInfo<Vs2>::kPosZero;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[5]) = FPTypeInfo<Vs2>::kNegZero;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[6]) = FPTypeInfo<Vs2>::kPosDenorm;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[7]) = FPTypeInfo<Vs2>::kNegDenorm;
+ *reinterpret_cast<VdInt *>(&vd_span[0]) = FPTypeInfo<Vd>::kQNaN;
+ *reinterpret_cast<VdInt *>(&vd_span[1]) = FPTypeInfo<Vd>::kSNaN;
+ *reinterpret_cast<VdInt *>(&vd_span[2]) = FPTypeInfo<Vd>::kPosInf;
+ *reinterpret_cast<VdInt *>(&vd_span[3]) = FPTypeInfo<Vd>::kNegInf;
+ *reinterpret_cast<VdInt *>(&vd_span[4]) = FPTypeInfo<Vd>::kPosZero;
+ *reinterpret_cast<VdInt *>(&vd_span[5]) = FPTypeInfo<Vd>::kNegZero;
+ *reinterpret_cast<VdInt *>(&vd_span[6]) = FPTypeInfo<Vd>::kPosDenorm;
+ *reinterpret_cast<VdInt *>(&vd_span[7]) = FPTypeInfo<Vd>::kNegDenorm;
+ if (lmul_index == 4) {
+ *reinterpret_cast<Vs1Int *>(&vs1_span[0]) = FPTypeInfo<Vs1>::kQNaN;
+ *reinterpret_cast<Vs1Int *>(&vs1_span[1]) = FPTypeInfo<Vs1>::kSNaN;
+ *reinterpret_cast<Vs1Int *>(&vs1_span[2]) = FPTypeInfo<Vs1>::kPosInf;
+ *reinterpret_cast<Vs1Int *>(&vs1_span[3]) = FPTypeInfo<Vs1>::kNegInf;
+ *reinterpret_cast<Vs1Int *>(&vs1_span[4]) = FPTypeInfo<Vs1>::kPosZero;
+ *reinterpret_cast<Vs1Int *>(&vs1_span[5]) = FPTypeInfo<Vs1>::kNegZero;
+ *reinterpret_cast<Vs1Int *>(&vs1_span[6]) = FPTypeInfo<Vs1>::kPosDenorm;
+ *reinterpret_cast<Vs1Int *>(&vs1_span[7]) = FPTypeInfo<Vs1>::kNegDenorm;
+ } else if (lmul_index == 5) {
+ *reinterpret_cast<Vs1Int *>(&vs1_span[7]) = FPTypeInfo<Vs1>::kQNaN;
+ *reinterpret_cast<Vs1Int *>(&vs1_span[6]) = FPTypeInfo<Vs1>::kSNaN;
+ *reinterpret_cast<Vs1Int *>(&vs1_span[5]) = FPTypeInfo<Vs1>::kPosInf;
+ *reinterpret_cast<Vs1Int *>(&vs1_span[4]) = FPTypeInfo<Vs1>::kNegInf;
+ *reinterpret_cast<Vs1Int *>(&vs1_span[3]) = FPTypeInfo<Vs1>::kPosZero;
+ *reinterpret_cast<Vs1Int *>(&vs1_span[2]) = FPTypeInfo<Vs1>::kNegZero;
+ *reinterpret_cast<Vs1Int *>(&vs1_span[1]) = FPTypeInfo<Vs1>::kPosDenorm;
+ *reinterpret_cast<Vs1Int *>(&vs1_span[0]) = FPTypeInfo<Vs1>::kNegDenorm;
+ } else if (lmul_index == 6) {
+ *reinterpret_cast<Vs1Int *>(&vs1_span[0]) = FPTypeInfo<Vs1>::kQNaN;
+ *reinterpret_cast<Vs1Int *>(&vs1_span[1]) = FPTypeInfo<Vs1>::kSNaN;
+ *reinterpret_cast<Vs1Int *>(&vs1_span[2]) = FPTypeInfo<Vs1>::kNegInf;
+ *reinterpret_cast<Vs1Int *>(&vs1_span[3]) = FPTypeInfo<Vs1>::kPosInf;
+ *reinterpret_cast<Vs1Int *>(&vs1_span[4]) = FPTypeInfo<Vs1>::kNegZero;
+ *reinterpret_cast<Vs1Int *>(&vs1_span[5]) = FPTypeInfo<Vs1>::kPosZero;
+ *reinterpret_cast<Vs1Int *>(&vs1_span[6]) = FPTypeInfo<Vs1>::kNegDenorm;
+ *reinterpret_cast<Vs1Int *>(&vs1_span[7]) = FPTypeInfo<Vs1>::kPosDenorm;
+ }
+ // Modify the first mask bits to use each of the special floating point
+ // values.
+ vreg_[kVmask]->data_buffer()->Set<uint8_t>(0, 0xff);
+ // Set values for all 8 vector registers in the vector register group.
+ for (int i = 0; i < 8; i++) {
+ auto vs2_name = absl::StrCat("v", kVs2 + i);
+ auto vs1_name = absl::StrCat("v", kVs1 + i);
+ SetVectorRegisterValues<Vs2>(
+ {{vs2_name, vs2_span.subspan(vs2_size * i, vs2_size)}});
+ SetVectorRegisterValues<Vs1>(
+ {{vs1_name, vs1_span.subspan(vs1_size * i, vs1_size)}});
+ }
+ int lmul8 = kLmul8Values[lmul_index];
+ int lmul8_vd = lmul8 * sizeof(Vd) / byte_sew;
+ int lmul8_vs2 = lmul8 * sizeof(Vs2) / byte_sew;
+ int lmul8_vs1 = lmul8 * sizeof(Vs1) / byte_sew;
+ int num_reg_values = lmul8 * kVectorLengthInBytes / (8 * byte_sew);
+ // Configure vector unit for different lmul settings.
+ uint32_t vtype =
+ (kSewSettingsByByteSize[byte_sew] << 3) | kLmulSettings[lmul_index];
+ int vstart = 0;
+ // Try different vstart values (updated at the bottom of the loop).
+ for (int vstart_count = 0; vstart_count < 4; vstart_count++) {
+ int vlen = 1024;
+ // Try different vector lengths (updated at the bottom of the loop).
+ for (int vlen_count = 0; vlen_count < 4; vlen_count++) {
+ ASSERT_TRUE(vlen > vstart);
+ int num_values = std::min(num_reg_values, vlen);
+ ConfigureVectorUnit(vtype, vlen);
+ // Iterate across rounding modes.
+ for (int rm : {0, 1, 2, 3, 4}) {
+ rv_fp_->SetRoundingMode(static_cast<FPRoundingMode>(rm));
+ rv_vector_->set_vstart(vstart);
+
+ // Reset Vd values, since the previous instruction execution
+ // overwrites them.
+ for (int i = 0; i < 8; i++) {
+ auto vd_name = absl::StrCat("v", kVd + i);
+ SetVectorRegisterValues<Vd>(
+ {{vd_name, vd_span.subspan(vd_size * i, vd_size)}});
+ }
+
+ inst->Execute();
+ if (lmul8_vd < 1 || lmul8_vd > 64) {
+ EXPECT_TRUE(rv_vector_->vector_exception())
+ << "lmul8: vd: " << lmul8_vd;
+ rv_vector_->clear_vector_exception();
+ continue;
+ }
+
+ if (lmul8_vs2 < 1 || lmul8_vs2 > 64) {
+ EXPECT_TRUE(rv_vector_->vector_exception())
+ << "lmul8: vs2: " << lmul8_vs2;
+ rv_vector_->clear_vector_exception();
+ continue;
+ }
+
+ if (lmul8_vs1 < 1 || lmul8_vs1 > 64) {
+ EXPECT_TRUE(rv_vector_->vector_exception())
+ << "lmul8: vs1: " << lmul8_vs1;
+ rv_vector_->clear_vector_exception();
+ continue;
+ }
+ EXPECT_FALSE(rv_vector_->vector_exception());
+ EXPECT_EQ(rv_vector_->vstart(), 0);
+ int count = 0;
+ for (int reg = kVd; reg < kVd + 8; reg++) {
+ for (int i = 0; i < kVectorLengthInBytes / sizeof(Vd); i++) {
+ int mask_index = count >> 3;
+ int mask_offset = count & 0b111;
+ bool mask_value = true;
+ // The first 8 bits of the mask are set to true above, so only
+ // read the mask value after the first byte.
+ if (mask_index > 0) {
+ mask_value =
+ ((kA5Mask[mask_index] >> mask_offset) & 0b1) != 0;
+ }
+ auto reg_val = vreg_[reg]->data_buffer()->Get<Vd>(i);
+ auto int_reg_val =
+ *reinterpret_cast<typename FPTypeInfo<Vd>::IntType *>(
+ ®_val);
+ auto int_vd_val =
+ *reinterpret_cast<typename FPTypeInfo<Vd>::IntType *>(
+ &vd_value[count]);
+ if ((count >= vstart) && mask_value && (count < num_values)) {
+ ScopedFPStatus set_fpstatus(rv_fp_->host_fp_interface());
+ auto op_val = operation(vs2_value[count], vs1_value[count],
+ vd_value[count]);
+ auto int_op_val =
+ *reinterpret_cast<typename FPTypeInfo<Vd>::IntType *>(
+ &op_val);
+ auto int_vs2_val =
+ *reinterpret_cast<typename FPTypeInfo<Vs2>::IntType *>(
+ &vs2_value[count]);
+ auto int_vs1_val =
+ *reinterpret_cast<typename FPTypeInfo<Vs1>::IntType *>(
+ &vs1_value[count]);
+ FPCompare<Vd>(
+ op_val, reg_val, delta_position,
+ absl::StrCat(
+ name, "[", count, "] op(", vs2_value[count], "[0x",
+ absl::Hex(int_vs2_val), "], ", vs1_value[count],
+ "[0x", absl::Hex(int_vs1_val), "], ", vd_value[count],
+ "[0x", absl::Hex(int_vd_val),
+ "]) = ", absl::Hex(int_op_val), " != reg[", reg, "][",
+ i, "] (", reg_val, " [0x", absl::Hex(int_reg_val),
+ "]) lmul8(", lmul8,
+ ") rm = ", *(rv_fp_->GetRoundingMode())));
+ } else {
+ EXPECT_THAT(int_vd_val, int_reg_val) << absl::StrCat(
+ name, " 0 != reg[", reg, "][", i, "] (", reg_val,
+ " [0x", absl::Hex(int_reg_val), "]) lmul8(", lmul8, ")");
+ }
+ count++;
+ }
+ if (HasFailure()) return;
+ }
+ }
+ vlen = absl::Uniform(absl::IntervalOpenClosed, bitgen_, vstart,
+ num_reg_values);
+ }
+ vstart = absl::Uniform(absl::IntervalOpen, bitgen_, 0, num_reg_values);
+ }
+ }
+ }
+
+ // Floating point test needs to ensure to use the fp special values (inf, NaN
+ // etc.) during testing, not just random values. This function handles vector
+ // scalar instructions.
+ template <typename Vd, typename Vs2, typename Fs1>
+ void TernaryOpFPTestHelperVX(absl::string_view name, int sew,
+ Instruction *inst, int delta_position,
+ std::function<Vd(Vs2, Fs1, Vd)> operation) {
+ int byte_sew = sew / 8;
+ if (byte_sew != sizeof(Vd) && byte_sew != sizeof(Vs2) &&
+ byte_sew != sizeof(Fs1)) {
+ FAIL() << name << ": selected element width != any operand types"
+ << "sew: " << sew << " Vd: " << sizeof(Vd)
+ << " Vs2: " << sizeof(Vs2) << " Fs1: " << sizeof(Fs1);
+ return;
+ }
+ // Number of elements per vector register.
+ constexpr int vs2_size = kVectorLengthInBytes / sizeof(Vs2);
+ constexpr int vd_size = kVectorLengthInBytes / sizeof(Vd);
+ // Input values for 8 registers.
+ Vs2 vs2_value[vs2_size * 8];
+ Vd vd_value[vd_size * 8];
+ auto vs2_span = Span<Vs2>(vs2_value);
+ auto vd_span = Span<Vd>(vd_value);
+ AppendVectorRegisterOperands({kVs2}, {kVd});
+ AppendRegisterOperands({kFs1Name}, {});
+ AppendVectorRegisterOperands({kVd, kVmask}, {kVd});
+ SetVectorRegisterValues<uint8_t>(
+ {{kVmaskName, Span<const uint8_t>(kA5Mask)}});
+ // Iterate across different lmul values.
+ for (int lmul_index = 0; lmul_index < 7; lmul_index++) {
+ // Initialize input values.
+ FillArrayWithRandomFPValues<Vs2>(vs2_span);
+ using Vs2Int = typename FPTypeInfo<Vs2>::IntType;
+ using VdInt = typename FPTypeInfo<Vd>::IntType;
+ // Overwrite the first few values of the input data with infinities,
+ // zeros, denormals and NaNs.
+ *reinterpret_cast<Vs2Int *>(&vs2_span[0]) = FPTypeInfo<Vs2>::kQNaN;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[1]) = FPTypeInfo<Vs2>::kSNaN;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[2]) = FPTypeInfo<Vs2>::kPosInf;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[3]) = FPTypeInfo<Vs2>::kNegInf;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[4]) = FPTypeInfo<Vs2>::kPosZero;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[5]) = FPTypeInfo<Vs2>::kNegZero;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[6]) = FPTypeInfo<Vs2>::kPosDenorm;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[7]) = FPTypeInfo<Vs2>::kNegDenorm;
+ *reinterpret_cast<VdInt *>(&vd_span[0]) = FPTypeInfo<Vd>::kQNaN;
+ *reinterpret_cast<VdInt *>(&vd_span[1]) = FPTypeInfo<Vd>::kSNaN;
+ *reinterpret_cast<VdInt *>(&vd_span[2]) = FPTypeInfo<Vd>::kPosInf;
+ *reinterpret_cast<VdInt *>(&vd_span[3]) = FPTypeInfo<Vd>::kNegInf;
+ *reinterpret_cast<VdInt *>(&vd_span[4]) = FPTypeInfo<Vd>::kPosZero;
+ *reinterpret_cast<VdInt *>(&vd_span[5]) = FPTypeInfo<Vd>::kNegZero;
+ *reinterpret_cast<VdInt *>(&vd_span[6]) = FPTypeInfo<Vd>::kPosDenorm;
+ *reinterpret_cast<VdInt *>(&vd_span[7]) = FPTypeInfo<Vd>::kNegDenorm;
+ // Modify the first mask bits to use each of the special floating point
+ // values.
+ vreg_[kVmask]->data_buffer()->Set<uint8_t>(0, 0xff);
+ // Set values for all 8 vector registers in the vector register group.
+ for (int i = 0; i < 8; i++) {
+ auto vs2_name = absl::StrCat("v", kVs2 + i);
+ SetVectorRegisterValues<Vs2>(
+ {{vs2_name, vs2_span.subspan(vs2_size * i, vs2_size)}});
+ }
+ int lmul8 = kLmul8Values[lmul_index];
+ int lmul8_vd = lmul8 * sizeof(Vd) / byte_sew;
+ int lmul8_vs2 = lmul8 * sizeof(Vs2) / byte_sew;
+ int lmul8_vs1 = lmul8 * sizeof(Fs1) / byte_sew;
+ int num_reg_values = lmul8 * kVectorLengthInBytes / (8 * byte_sew);
+ // Configure vector unit for different lmul settings.
+ uint32_t vtype =
+ (kSewSettingsByByteSize[byte_sew] << 3) | kLmulSettings[lmul_index];
+ int vstart = 0;
+ // Try different vstart values (updated at the bottom of the loop).
+ for (int vstart_count = 0; vstart_count < 4; vstart_count++) {
+ int vlen = 1024;
+ // Try different vector lengths (updated at the bottom of the loop).
+ for (int vlen_count = 0; vlen_count < 4; vlen_count++) {
+ ASSERT_TRUE(vlen > vstart);
+ int num_values = std::min(num_reg_values, vlen);
+ ConfigureVectorUnit(vtype, vlen);
+ // Generate a new rs1 value.
+ Fs1 fs1_value = RandomFPValue<Fs1>();
+ // Need to NaN box the value, that is, if the register value type is
+ // wider than the data type for a floating point value, the upper bits
+ // are all set to 1's.
+ typename RVFpRegister::ValueType fs1_reg_value =
+ NaNBox<Fs1, typename RVFpRegister::ValueType>(fs1_value);
+ SetRegisterValues<typename RVFpRegister::ValueType, RVFpRegister>(
+ {{kFs1Name, fs1_reg_value}});
+ // Iterate across rounding modes.
+ for (int rm : {0, 1, 2, 3, 4}) {
+ rv_fp_->SetRoundingMode(static_cast<FPRoundingMode>(rm));
+ rv_vector_->set_vstart(vstart);
+ ClearVectorRegisterGroup(kVd, 8);
+
+ // Reset Vd values, since the previous instruction execution
+ // overwrites them.
+ for (int i = 0; i < 8; i++) {
+ auto vd_name = absl::StrCat("v", kVd + i);
+ SetVectorRegisterValues<Vd>(
+ {{vd_name, vd_span.subspan(vd_size * i, vd_size)}});
+ }
+
+ inst->Execute();
+ if (lmul8_vd < 1 || lmul8_vd > 64) {
+ EXPECT_TRUE(rv_vector_->vector_exception())
+ << "lmul8: vd: " << lmul8_vd;
+ rv_vector_->clear_vector_exception();
+ continue;
+ }
+
+ if (lmul8_vs2 < 1 || lmul8_vs2 > 64) {
+ EXPECT_TRUE(rv_vector_->vector_exception())
+ << "lmul8: vs2: " << lmul8_vs2;
+ rv_vector_->clear_vector_exception();
+ continue;
+ }
+
+ if (lmul8_vs1 < 1 || lmul8_vs1 > 64) {
+ EXPECT_TRUE(rv_vector_->vector_exception())
+ << "lmul8: vs1: " << lmul8_vs1;
+ rv_vector_->clear_vector_exception();
+ continue;
+ }
+ EXPECT_FALSE(rv_vector_->vector_exception());
+ EXPECT_EQ(rv_vector_->vstart(), 0);
+ int count = 0;
+ for (int reg = kVd; reg < kVd + 8; reg++) {
+ for (int i = 0; i < kVectorLengthInBytes / sizeof(Vd); i++) {
+ int mask_index = count >> 3;
+ int mask_offset = count & 0b111;
+ bool mask_value = true;
+ // The first 8 bits of the mask are set to true above, so only
+ // read the mask value after the first byte from the constant
+ // mask values.
+ if (mask_index > 0) {
+ mask_value =
+ ((kA5Mask[mask_index] >> mask_offset) & 0b1) != 0;
+ }
+ auto reg_val = vreg_[reg]->data_buffer()->Get<Vd>(i);
+ auto int_reg_val =
+ *reinterpret_cast<typename FPTypeInfo<Vd>::IntType *>(
+ ®_val);
+ auto int_vd_val =
+ *reinterpret_cast<typename FPTypeInfo<Vd>::IntType *>(
+ &vd_value[count]);
+ if ((count >= vstart) && mask_value && (count < num_values)) {
+ // Set rounding mode and perform the computation.
+
+ ScopedFPStatus set_fpstatus(rv_fp_->host_fp_interface());
+ auto op_val =
+ operation(vs2_value[count], fs1_value, vd_value[count]);
+ // Extract the integer view of the fp values.
+ auto int_op_val =
+ *reinterpret_cast<typename FPTypeInfo<Vd>::IntType *>(
+ &op_val);
+ auto int_vs2_val =
+ *reinterpret_cast<typename FPTypeInfo<Vs2>::IntType *>(
+ &vs2_value[count]);
+ auto int_fs1_val =
+ *reinterpret_cast<typename FPTypeInfo<Fs1>::IntType *>(
+ &fs1_value);
+ FPCompare<Vd>(
+ op_val, reg_val, delta_position,
+ absl::StrCat(
+ name, "[", count, "] op(", vs2_value[count], "[0x",
+ absl::Hex(int_vs2_val), "], ", fs1_value, "[0x",
+ absl::Hex(int_fs1_val), "], ", vd_value[count], "[0x",
+ absl::Hex(int_vd_val), "]) = ", op_val, "[0x",
+ absl::Hex(int_op_val), "] ", " != reg[", reg, "][", i,
+ "] (", reg_val, " [0x", absl::Hex(int_reg_val),
+ "]) lmul8(", lmul8,
+ ") rm = ", *(rv_fp_->GetRoundingMode())));
+ } else {
+ EXPECT_EQ(int_vd_val, int_reg_val) << absl::StrCat(
+ name, " ", vd_value[count], " [0x",
+ absl::Hex(int_vd_val), "] != reg[", reg, "][", i, "] (",
+ reg_val, " [0x", absl::Hex(int_reg_val), "]) lmul8(",
+ lmul8, ")");
+ }
+ count++;
+ }
+ if (HasFailure()) return;
+ }
+ }
+ vlen = absl::Uniform(absl::IntervalOpenClosed, bitgen_, vstart,
+ num_reg_values);
+ }
+ vstart = absl::Uniform(absl::IntervalOpen, bitgen_, 0, num_reg_values);
+ }
+ }
+ }
+};
+
+// Test fp add.
+TEST_F(RiscVCheriotFPInstructionsTest, Vfadd) {
+ // Vector-vector.
+ SetSemanticFunction(&Vfadd);
+ BinaryOpFPTestHelperVV<float, float, float>(
+ "Vfadd_vv32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [](float vs2, float vs1) -> float { return vs2 + vs1; });
+ ResetInstruction();
+ SetSemanticFunction(&Vfadd);
+ BinaryOpFPTestHelperVV<double, double, double>(
+ "Vfadd_vv64", /*sew*/ 64, instruction_, /*delta_position*/ 64,
+ [](double vs2, double vs1) -> double { return vs2 + vs1; });
+ // Vector-scalar.
+ ResetInstruction();
+ SetSemanticFunction(&Vfadd);
+ BinaryOpFPTestHelperVX<float, float, float>(
+ "Vfadd_vx32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [](float vs2, float vs1) -> float { return vs2 + vs1; });
+ ResetInstruction();
+ SetSemanticFunction(&Vfadd);
+ BinaryOpFPTestHelperVX<double, double, double>(
+ "Vfadd_vx64", /*sew*/ 64, instruction_, /*delta_position*/ 64,
+ [](double vs2, double vs1) -> double { return vs2 + vs1; });
+}
+
+// Test fp sub.
+TEST_F(RiscVCheriotFPInstructionsTest, Vfsub) {
+ // Vector-vector.
+ SetSemanticFunction(&Vfsub);
+ BinaryOpFPTestHelperVV<float, float, float>(
+ "Vfsub_vv32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [](float vs2, float vs1) -> float { return vs2 - vs1; });
+ ResetInstruction();
+ SetSemanticFunction(&Vfsub);
+ BinaryOpFPTestHelperVV<double, double, double>(
+ "Vfsub_vv64", /*sew*/ 64, instruction_, /*delta_position*/ 64,
+ [](double vs2, double vs1) -> double { return vs2 - vs1; });
+ // Vector-scalar.
+ ResetInstruction();
+ SetSemanticFunction(&Vfsub);
+ BinaryOpFPTestHelperVX<float, float, float>(
+ "Vfsub_vx32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [](float vs2, float vs1) -> float { return vs2 - vs1; });
+ ResetInstruction();
+ SetSemanticFunction(&Vfsub);
+ BinaryOpFPTestHelperVX<double, double, double>(
+ "Vfsub_vx64", /*sew*/ 64, instruction_, /*delta_position*/ 64,
+ [](double vs2, double vs1) -> double { return vs2 - vs1; });
+}
+
+// Test fp reverse sub.
+TEST_F(RiscVCheriotFPInstructionsTest, Vfrsub) {
+ // Vector-vector.
+ SetSemanticFunction(&Vfrsub);
+ BinaryOpFPTestHelperVV<float, float, float>(
+ "Vfrsub_vv32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [](float vs2, float vs1) -> float { return vs1 - vs2; });
+ ResetInstruction();
+ SetSemanticFunction(&Vfrsub);
+ BinaryOpFPTestHelperVV<double, double, double>(
+ "Vfrsub_vv64", /*sew*/ 64, instruction_, /*delta_position*/ 64,
+ [](double vs2, double vs1) -> double { return vs1 - vs2; });
+ // Vector-scalar.
+ ResetInstruction();
+ SetSemanticFunction(&Vfrsub);
+ BinaryOpFPTestHelperVX<float, float, float>(
+ "Vfrsub_vx32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [](float vs2, float vs1) -> float { return vs1 - vs2; });
+ ResetInstruction();
+ SetSemanticFunction(&Vfrsub);
+ BinaryOpFPTestHelperVX<double, double, double>(
+ "Vfrsub_vx64", /*sew*/ 64, instruction_, /*delta_position*/ 64,
+ [](double vs2, double vs1) -> double { return vs1 - vs2; });
+}
+
+// Test fp widening add.
+TEST_F(RiscVCheriotFPInstructionsTest, Vfwadd) {
+ // Vector-vector.
+ SetSemanticFunction(&Vfwadd);
+ BinaryOpFPTestHelperVV<double, float, float>(
+ "Vfwadd_vv32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [](float vs2, float vs1) -> double {
+ return static_cast<double>(vs2) + static_cast<double>(vs1);
+ });
+ // Vector-scalar.
+ ResetInstruction();
+ SetSemanticFunction(&Vfwadd);
+ BinaryOpFPTestHelperVX<double, float, float>(
+ "Vfwadd_vx32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [](float vs2, float vs1) -> double {
+ return static_cast<double>(vs2) + static_cast<double>(vs1);
+ });
+}
+
+// Test fp widening subtract.
+TEST_F(RiscVCheriotFPInstructionsTest, Vfwsub) {
+ // Vector-vector.
+ SetSemanticFunction(&Vfwsub);
+ BinaryOpFPTestHelperVV<double, float, float>(
+ "Vfwsub_vv32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [](float vs2, float vs1) -> double {
+ return static_cast<double>(vs2) - static_cast<double>(vs1);
+ });
+ // Vector-scalar.
+ ResetInstruction();
+ SetSemanticFunction(&Vfwsub);
+ BinaryOpFPTestHelperVX<double, float, float>(
+ "Vfwsub_vx32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [](float vs2, float vs1) -> double {
+ return static_cast<double>(vs2) - static_cast<double>(vs1);
+ });
+}
+
+// Test fp widening add with wide operand.
+TEST_F(RiscVCheriotFPInstructionsTest, Vfwaddw) {
+ // Vector-vector.
+ SetSemanticFunction(&Vfwaddw);
+ BinaryOpFPTestHelperVV<double, double, float>(
+ "Vfwaddw_vv32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [](double vs2, float vs1) -> double {
+ return vs2 + static_cast<double>(vs1);
+ });
+ // Vector-scalar.
+ ResetInstruction();
+ SetSemanticFunction(&Vfwaddw);
+ BinaryOpFPTestHelperVX<double, double, float>(
+ "Vfwaddw_vx32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [](double vs2, float vs1) -> double {
+ return vs2 + static_cast<double>(vs1);
+ });
+}
+
+// Test fp widening subtract with wide operand.
+TEST_F(RiscVCheriotFPInstructionsTest, Vfwsubw) {
+ // Vector-vector.
+ SetSemanticFunction(&Vfwsubw);
+ BinaryOpFPTestHelperVV<double, double, float>(
+ "Vfwsubw_vv32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [](double vs2, float vs1) -> double {
+ return vs2 - static_cast<double>(vs1);
+ });
+ // Vector-scalar.
+ ResetInstruction();
+ SetSemanticFunction(&Vfwsubw);
+ BinaryOpFPTestHelperVX<double, double, float>(
+ "Vfwsubw_vx32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [](double vs2, float vs1) -> double {
+ return vs2 - static_cast<double>(vs1);
+ });
+}
+
+// Test fp multiply.
+TEST_F(RiscVCheriotFPInstructionsTest, Vfmul) {
+ // Vector-vector.
+ SetSemanticFunction(&Vfmul);
+ BinaryOpFPTestHelperVV<float, float, float>(
+ "Vfmul_vv32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [](float vs2, float vs1) -> float { return vs2 * vs1; });
+ ResetInstruction();
+ SetSemanticFunction(&Vfmul);
+ BinaryOpFPTestHelperVV<double, double, double>(
+ "Vfmul_vv64", /*sew*/ 64, instruction_, /*delta_position*/ 64,
+ [](double vs2, double vs1) -> double { return vs2 * vs1; });
+ // Vector-scalar.
+ ResetInstruction();
+ SetSemanticFunction(&Vfmul);
+ BinaryOpFPTestHelperVX<float, float, float>(
+ "Vfmul_vx32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [](float vs2, float vs1) -> float { return vs2 * vs1; });
+ ResetInstruction();
+ SetSemanticFunction(&Vfmul);
+ BinaryOpFPTestHelperVX<double, double, double>(
+ "Vfmul_vx64", /*sew*/ 64, instruction_, /*delta_position*/ 64,
+ [](double vs2, double vs1) -> double { return vs2 * vs1; });
+}
+
+// Test fp divide.
+TEST_F(RiscVCheriotFPInstructionsTest, Vfdiv) {
+ // Vector-vector.
+ SetSemanticFunction(&Vfdiv);
+ BinaryOpFPTestHelperVV<float, float, float>(
+ "Vfdiv_vv32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [](float vs2, float vs1) -> float { return vs2 / vs1; });
+ ResetInstruction();
+ SetSemanticFunction(&Vfdiv);
+ BinaryOpFPTestHelperVV<double, double, double>(
+ "Vfdiv_vv64", /*sew*/ 64, instruction_, /*delta_position*/ 64,
+ [](double vs2, double vs1) -> double { return vs2 / vs1; });
+ // Vector-scalar.
+ ResetInstruction();
+ SetSemanticFunction(&Vfdiv);
+ BinaryOpFPTestHelperVX<float, float, float>(
+ "Vfdiv_vx32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [](float vs2, float vs1) -> float { return vs2 / vs1; });
+ ResetInstruction();
+ SetSemanticFunction(&Vfdiv);
+ BinaryOpFPTestHelperVX<double, double, double>(
+ "Vfdiv_vx64", /*sew*/ 64, instruction_, /*delta_position*/ 64,
+ [](double vs2, double vs1) -> double { return vs2 / vs1; });
+}
+
+// Test fp reverse divide.
+TEST_F(RiscVCheriotFPInstructionsTest, Vfrdiv) {
+ // Vector-vector.
+ SetSemanticFunction(&Vfrdiv);
+ BinaryOpFPTestHelperVV<float, float, float>(
+ "Vfrdiv_vv32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [](float vs2, float vs1) -> float { return vs1 / vs2; });
+ ResetInstruction();
+ SetSemanticFunction(&Vfrdiv);
+ BinaryOpFPTestHelperVV<double, double, double>(
+ "Vfrdiv_vv64", /*sew*/ 64, instruction_, /*delta_position*/ 64,
+ [](double vs2, double vs1) -> double { return vs1 / vs2; });
+ // Vector-scalar.
+ ResetInstruction();
+ SetSemanticFunction(&Vfrdiv);
+ BinaryOpFPTestHelperVX<float, float, float>(
+ "Vfrdiv_vx32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [](float vs2, float vs1) -> float { return vs1 / vs2; });
+ ResetInstruction();
+ SetSemanticFunction(&Vfrdiv);
+ BinaryOpFPTestHelperVX<double, double, double>(
+ "Vfrdiv_vx64", /*sew*/ 64, instruction_, /*delta_position*/ 64,
+ [](double vs2, double vs1) -> double { return vs1 / vs2; });
+}
+
+// Test fp widening multiply.
+TEST_F(RiscVCheriotFPInstructionsTest, Vfwmul) {
+ // Vector-vector.
+ SetSemanticFunction(&Vfwmul);
+ BinaryOpFPTestHelperVV<double, float, float>(
+ "Vfwmul_vv32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [](float vs2, float vs1) -> double {
+ return static_cast<double>(vs2) * static_cast<double>(vs1);
+ });
+ // Vector-scalar.
+ ResetInstruction();
+ SetSemanticFunction(&Vfwmul);
+ BinaryOpFPTestHelperVX<double, float, float>(
+ "Vfwmul_vx32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [](float vs2, float vs1) -> double {
+ return static_cast<double>(vs2) * static_cast<double>(vs1);
+ });
+}
+
+// Test fp multiply add.
+TEST_F(RiscVCheriotFPInstructionsTest, Vfmadd) {
+ // Vector-vector.
+ SetSemanticFunction(&Vfmadd);
+ TernaryOpFPTestHelperVV<float, float, float>(
+ "Vfmadd_vv32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [](float vs2, float vs1, float vd) -> float {
+ return std::fma(vs1, vd, vs2);
+ });
+ ResetInstruction();
+ SetSemanticFunction(&Vfmadd);
+ TernaryOpFPTestHelperVV<double, double, double>(
+ "Vfmadd_vv64", /*sew*/ 64, instruction_, /*delta_position*/ 64,
+ [](double vs2, double vs1, double vd) -> double {
+ return std::fma(vs1, vd, vs2);
+ });
+ // Vector-scalar.
+ ResetInstruction();
+ SetSemanticFunction(&Vfmadd);
+ TernaryOpFPTestHelperVX<float, float, float>(
+ "Vfmadd_vx32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [](float vs2, float vs1, float vd) -> float {
+ return std::fma(vs1, vd, vs2);
+ });
+ ResetInstruction();
+ SetSemanticFunction(&Vfmadd);
+ TernaryOpFPTestHelperVX<double, double, double>(
+ "Vfmadd_vx64", /*sew*/ 64, instruction_, /*delta_position*/ 64,
+ [](double vs2, double vs1, double vd) -> double {
+ return std::fma(vs1, vd, vs2);
+ });
+}
+
+// Test fp negated multiply add.
+TEST_F(RiscVCheriotFPInstructionsTest, Vfnmadd) {
+ // Vector-vector.
+ SetSemanticFunction(&Vfnmadd);
+ TernaryOpFPTestHelperVV<float, float, float>(
+ "Vfnmadd_vv32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [](float vs2, float vs1, float vd) -> float {
+ return OptimizationBarrier(std::fma(-vs1, vd, -vs2));
+ });
+ ResetInstruction();
+ SetSemanticFunction(&Vfnmadd);
+ TernaryOpFPTestHelperVV<double, double, double>(
+ "Vfnmadd_vv64", /*sew*/ 64, instruction_, /*delta_position*/ 64,
+ [](double vs2, double vs1, double vd) -> double {
+ return OptimizationBarrier(std::fma(-vs1, vd, -vs2));
+ });
+ // Vector-scalar.
+ ResetInstruction();
+ SetSemanticFunction(&Vfnmadd);
+ TernaryOpFPTestHelperVX<float, float, float>(
+ "Vfnmadd_vx32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [](float vs2, float vs1, float vd) -> float {
+ return OptimizationBarrier(std::fma(-vs1, vd, -vs2));
+ });
+ ResetInstruction();
+ SetSemanticFunction(&Vfnmadd);
+ TernaryOpFPTestHelperVX<double, double, double>(
+ "Vfnmadd_vx64", /*sew*/ 64, instruction_, /*delta_position*/ 64,
+ [](double vs2, double vs1, double vd) -> double {
+ return OptimizationBarrier(std::fma(-vs1, vd, -vs2));
+ });
+}
+
+// Test fp multiply subtract.
+TEST_F(RiscVCheriotFPInstructionsTest, Vfmsub) {
+ // Vector-vector.
+ SetSemanticFunction(&Vfmsub);
+ TernaryOpFPTestHelperVV<float, float, float>(
+ "Vfmsub_vv32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [](float vs2, float vs1, float vd) -> float {
+ return OptimizationBarrier(std::fma(vs1, vd, -vs2));
+ });
+ ResetInstruction();
+ SetSemanticFunction(&Vfmsub);
+ TernaryOpFPTestHelperVV<double, double, double>(
+ "Vfmsub_vv64", /*sew*/ 64, instruction_, /*delta_position*/ 64,
+ [](double vs2, double vs1, double vd) -> double {
+ return OptimizationBarrier(std::fma(vs1, vd, -vs2));
+ });
+ // Vector-scalar.
+ ResetInstruction();
+ SetSemanticFunction(&Vfmsub);
+ TernaryOpFPTestHelperVX<float, float, float>(
+ "Vfmsub_vx32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [](float vs2, float vs1, float vd) -> float {
+ return OptimizationBarrier(std::fma(vs1, vd, -vs2));
+ });
+ ResetInstruction();
+ SetSemanticFunction(&Vfmsub);
+ TernaryOpFPTestHelperVX<double, double, double>(
+ "Vfmsub_vx64", /*sew*/ 64, instruction_, /*delta_position*/ 64,
+ [](double vs2, double vs1, double vd) -> double {
+ return OptimizationBarrier(std::fma(vs1, vd, -vs2));
+ });
+}
+
+// Test fp negated multiply subtract.
+TEST_F(RiscVCheriotFPInstructionsTest, Vfnmsub) {
+ // Vector-vector.
+ SetSemanticFunction(&Vfnmsub);
+ TernaryOpFPTestHelperVV<float, float, float>(
+ "Vfnmsub_vv32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [](float vs2, float vs1, float vd) -> float {
+ return OptimizationBarrier(std::fma(-vs1, vd, vs2));
+ });
+ ResetInstruction();
+ SetSemanticFunction(&Vfnmsub);
+ TernaryOpFPTestHelperVV<double, double, double>(
+ "Vfnmsub_vv64", /*sew*/ 64, instruction_, /*delta_position*/ 64,
+ [](double vs2, double vs1, double vd) -> double {
+ return OptimizationBarrier(std::fma(-vs1, vd, vs2));
+ });
+ // Vector-scalar.
+ ResetInstruction();
+ SetSemanticFunction(&Vfnmsub);
+ TernaryOpFPTestHelperVX<float, float, float>(
+ "Vfnmsub_vx32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [](float vs2, float vs1, float vd) -> float {
+ return OptimizationBarrier(std::fma(-vs1, vd, vs2));
+ });
+ ResetInstruction();
+ SetSemanticFunction(&Vfnmsub);
+ TernaryOpFPTestHelperVX<double, double, double>(
+ "Vfnmsub_vx64", /*sew*/ 64, instruction_, /*delta_position*/ 64,
+ [](double vs2, double vs1, double vd) -> double {
+ return OptimizationBarrier(std::fma(-vs1, vd, vs2));
+ });
+}
+
+// Test fp multiply accumulate.
+TEST_F(RiscVCheriotFPInstructionsTest, Vfmacc) {
+ // Vector-vector.
+ SetSemanticFunction(&Vfmacc);
+ TernaryOpFPTestHelperVV<float, float, float>(
+ "Vfmacc_vv32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [](float vs2, float vs1, float vd) -> float {
+ return OptimizationBarrier(std::fma(vs1, vs2, vd));
+ });
+ ResetInstruction();
+ SetSemanticFunction(&Vfmacc);
+ TernaryOpFPTestHelperVV<double, double, double>(
+ "Vfmacc_vv64", /*sew*/ 64, instruction_, /*delta_position*/ 64,
+ [](double vs2, double vs1, double vd) -> double {
+ return OptimizationBarrier(std::fma(vs1, vs2, vd));
+ });
+ // Vector-scalar.
+ ResetInstruction();
+ SetSemanticFunction(&Vfmacc);
+ TernaryOpFPTestHelperVX<float, float, float>(
+ "Vfmacc_vx32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [](float vs2, float vs1, float vd) -> float {
+ return OptimizationBarrier(std::fma(vs1, vs2, vd));
+ });
+ ResetInstruction();
+ SetSemanticFunction(&Vfmacc);
+ TernaryOpFPTestHelperVX<double, double, double>(
+ "Vfmacc_vx64", /*sew*/ 64, instruction_, /*delta_position*/ 64,
+ [](double vs2, double vs1, double vd) -> double {
+ return OptimizationBarrier(std::fma(vs1, vs2, vd));
+ });
+}
+
+// Test fp negated multiply add.
+TEST_F(RiscVCheriotFPInstructionsTest, Vfnmacc) {
+ // Vector-vector.
+ SetSemanticFunction(&Vfnmacc);
+ TernaryOpFPTestHelperVV<float, float, float>(
+ "Vfnmacc_vv32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [](float vs2, float vs1, float vd) -> float {
+ return OptimizationBarrier(std::fma(-vs1, vs2, -vd));
+ });
+ ResetInstruction();
+ SetSemanticFunction(&Vfnmacc);
+ TernaryOpFPTestHelperVV<double, double, double>(
+ "Vfnmacc_vv64", /*sew*/ 64, instruction_, /*delta_position*/ 64,
+ [](double vs2, double vs1, double vd) -> double {
+ return OptimizationBarrier(std::fma(-vs1, vs2, -vd));
+ });
+ // Vector-scalar.
+ ResetInstruction();
+ SetSemanticFunction(&Vfnmacc);
+ TernaryOpFPTestHelperVX<float, float, float>(
+ "Vfnmacc_vx32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [](float vs2, float vs1, float vd) -> float {
+ return OptimizationBarrier(std::fma(-vs1, vs2, -vd));
+ });
+ ResetInstruction();
+ SetSemanticFunction(&Vfnmacc);
+ TernaryOpFPTestHelperVX<double, double, double>(
+ "Vfnmacc_vx64", /*sew*/ 64, instruction_, /*delta_position*/ 64,
+ [](double vs2, double vs1, double vd) -> double {
+ return OptimizationBarrier(std::fma(-vs1, vs2, -vd));
+ });
+}
+
+// Test fp multiply subtract accumulate.
+TEST_F(RiscVCheriotFPInstructionsTest, Vfmsac) {
+ // Vector-vector.
+ SetSemanticFunction(&Vfmsac);
+ TernaryOpFPTestHelperVV<float, float, float>(
+ "Vfmsac_vv32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [](float vs2, float vs1, float vd) -> float {
+ return OptimizationBarrier(std::fma(vs1, vs2, -vd));
+ });
+ ResetInstruction();
+ SetSemanticFunction(&Vfmsac);
+ TernaryOpFPTestHelperVV<double, double, double>(
+ "Vfmsac_vv64", /*sew*/ 64, instruction_, /*delta_position*/ 64,
+ [](double vs2, double vs1, double vd) -> double {
+ return OptimizationBarrier(std::fma(vs1, vs2, -vd));
+ });
+ // Vector-scalar.
+ ResetInstruction();
+ SetSemanticFunction(&Vfmsac);
+ TernaryOpFPTestHelperVX<float, float, float>(
+ "Vfmsac_vx32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [](float vs2, float vs1, float vd) -> float {
+ return OptimizationBarrier(std::fma(vs1, vs2, -vd));
+ });
+ ResetInstruction();
+ SetSemanticFunction(&Vfmsac);
+ TernaryOpFPTestHelperVX<double, double, double>(
+ "Vfmsac_vx64", /*sew*/ 64, instruction_, /*delta_position*/ 64,
+ [](double vs2, double vs1, double vd) -> double {
+ return OptimizationBarrier(std::fma(vs1, vs2, -vd));
+ });
+}
+
+// Test fp negated multiply subtract accumulate.
+TEST_F(RiscVCheriotFPInstructionsTest, Vfnmsac) {
+ // Vector-vector.
+ SetSemanticFunction(&Vfnmsac);
+ TernaryOpFPTestHelperVV<float, float, float>(
+ "Vfnmsac_vv32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [](float vs2, float vs1, float vd) -> float {
+ return OptimizationBarrier(std::fma(-vs1, vs2, vd));
+ });
+ ResetInstruction();
+ SetSemanticFunction(&Vfnmsac);
+ TernaryOpFPTestHelperVV<double, double, double>(
+ "Vfnmsac_vv64", /*sew*/ 64, instruction_, /*delta_position*/ 64,
+ [](double vs2, double vs1, double vd) -> double {
+ return OptimizationBarrier(std::fma(-vs1, vs2, vd));
+ });
+ // Vector-scalar.
+ ResetInstruction();
+ SetSemanticFunction(&Vfnmsac);
+ TernaryOpFPTestHelperVX<float, float, float>(
+ "Vfnmsac_vx32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [](float vs2, float vs1, float vd) -> float {
+ return OptimizationBarrier(std::fma(-vs1, vs2, vd));
+ });
+ ResetInstruction();
+ SetSemanticFunction(&Vfnmsac);
+ TernaryOpFPTestHelperVX<double, double, double>(
+ "Vfnmsac_vx64", /*sew*/ 64, instruction_, /*delta_position*/ 64,
+ [](double vs2, double vs1, double vd) -> double {
+ return OptimizationBarrier(std::fma(-vs1, vs2, vd));
+ });
+}
+
+// Test fp widening multiply accumulate.
+TEST_F(RiscVCheriotFPInstructionsTest, Vfwmacc) {
+ // Vector-vector.
+ SetSemanticFunction(&Vfwmacc);
+ TernaryOpFPTestHelperVV<double, float, float>(
+ "Vfwmacc_vv32", /*sew*/ 32, instruction_, /*delta_position*/ 64,
+ [](float vs2, float vs1, double vd) -> double {
+ double vs1d = static_cast<double>(vs1);
+ double vs2d = static_cast<double>(vs2);
+ return OptimizationBarrier(vs1d * vs2d) + vd;
+ });
+ // Vector-scalar.
+ ResetInstruction();
+ SetSemanticFunction(&Vfwmacc);
+ TernaryOpFPTestHelperVX<double, float, float>(
+ "Vfwmacc_vx32", /*sew*/ 32, instruction_, /*delta_position*/ 64,
+ [](float vs2, float vs1, double vd) -> double {
+ double vs1d = static_cast<double>(vs1);
+ double vs2d = static_cast<double>(vs2);
+ return OptimizationBarrier(vs1d * vs2d) + vd;
+ });
+}
+
+// Test fp widening negated multiply add.
+TEST_F(RiscVCheriotFPInstructionsTest, Vfwnmacc) {
+ // Vector-vector.
+ SetSemanticFunction(&Vfwnmacc);
+ TernaryOpFPTestHelperVV<double, float, float>(
+ "Vfwnmacc_vv32", /*sew*/ 32, instruction_, /*delta_position*/ 64,
+ [](float vs2, float vs1, double vd) -> double {
+ double vs1d = static_cast<double>(vs1);
+ double vs2d = static_cast<double>(vs2);
+ return -OptimizationBarrier(vs1d * vs2d) - vd;
+ });
+ // Vector-scalar.
+ ResetInstruction();
+ SetSemanticFunction(&Vfwnmacc);
+ TernaryOpFPTestHelperVX<double, float, float>(
+ "Vfwnmacc_vx32", /*sew*/ 32, instruction_, /*delta_position*/ 64,
+ [](float vs2, float vs1, double vd) -> double {
+ double vs1d = static_cast<double>(vs1);
+ double vs2d = static_cast<double>(vs2);
+ return -OptimizationBarrier(vs1d * vs2d) - vd;
+ });
+}
+
+// Test fp widening multiply subtract accumulate.
+TEST_F(RiscVCheriotFPInstructionsTest, Vfwmsac) {
+ // Vector-vector.
+ SetSemanticFunction(&Vfwmsac);
+ TernaryOpFPTestHelperVV<double, float, float>(
+ "Vfwmsac_vv32", /*sew*/ 32, instruction_, /*delta_position*/ 64,
+ [](float vs2, float vs1, double vd) -> double {
+ double vs1d = static_cast<double>(vs1);
+ double vs2d = static_cast<double>(vs2);
+ return OptimizationBarrier(vs1d * vs2d) - vd;
+ });
+ // Vector-scalar.
+ ResetInstruction();
+ SetSemanticFunction(&Vfwmsac);
+ TernaryOpFPTestHelperVX<double, float, float>(
+ "Vfwmsac_vx32", /*sew*/ 32, instruction_, /*delta_position*/ 64,
+ [](float vs2, float vs1, double vd) -> double {
+ double vs1d = static_cast<double>(vs1);
+ double vs2d = static_cast<double>(vs2);
+ return OptimizationBarrier(vs1d * vs2d) - vd;
+ });
+}
+
+// Test fp widening negated multiply subtract accumulate.
+TEST_F(RiscVCheriotFPInstructionsTest, Vfwnmsac) {
+ // Vector-vector.
+ SetSemanticFunction(&Vfwnmsac);
+ TernaryOpFPTestHelperVV<double, float, float>(
+ "Vfwnmsac_vv32", /*sew*/ 32, instruction_, /*delta_position*/ 64,
+ [](float vs2, float vs1, double vd) -> double {
+ double vs1d = static_cast<double>(vs1);
+ double vs2d = static_cast<double>(vs2);
+ return -OptimizationBarrier(vs1d * vs2d) + vd;
+ });
+ // Vector-scalar.
+ ResetInstruction();
+ SetSemanticFunction(&Vfwnmsac);
+ TernaryOpFPTestHelperVX<double, float, float>(
+ "Vfwnmsac_vx32", /*sew*/ 32, instruction_, /*delta_position*/ 64,
+ [](float vs2, float vs1, double vd) -> double {
+ double vs1d = static_cast<double>(vs1);
+ double vs2d = static_cast<double>(vs2);
+ return -OptimizationBarrier(vs1d * vs2d) + vd;
+ });
+}
+
+// Test vector floating point sign injection instructions. There are three
+// of these. vfsgnj, vfsgnjn, and vfsgnjx. The instructions take the sign
+// bit from vs1/fs1 and the other bits from vs2. The sign bit is either used
+// as is, negated (n) or xor'ed (x).
+
+template <typename T>
+inline T SignHelper(
+ T vs2, T vs1,
+ std::function<typename FPTypeInfo<T>::IntType(
+ typename FPTypeInfo<T>::IntType, typename FPTypeInfo<T>::IntType,
+ typename FPTypeInfo<T>::IntType)>
+ sign_op) {
+ using Int = typename FPTypeInfo<T>::IntType;
+ Int sign_mask = 1ULL << (FPTypeInfo<T>::kBitSize - 1);
+ Int vs2i = *reinterpret_cast<Int *>(&vs2);
+ Int vs1i = *reinterpret_cast<Int *>(&vs1);
+ Int resi = sign_op(vs2i, vs1i, sign_mask);
+ return *reinterpret_cast<T *>(&resi);
+}
+
+// The sign is that of vs1.
+TEST_F(RiscVCheriotFPInstructionsTest, Vfsgnj) {
+ // Vector-vector.
+ SetSemanticFunction(&Vfsgnj);
+ BinaryOpFPTestHelperVV<float, float, float>(
+ "Vfsgnj_vv32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [](float vs2, float vs1) -> float {
+ using Int = typename FPTypeInfo<float>::IntType;
+ return SignHelper(vs2, vs1, [](Int vs2i, Int vs1i, Int mask) -> Int {
+ return (vs2i & ~mask) | (vs1i & mask);
+ });
+ });
+ ResetInstruction();
+ SetSemanticFunction(&Vfsgnj);
+ BinaryOpFPTestHelperVV<double, double, double>(
+ "Vfsgnj_vv64", /*sew*/ 64, instruction_, /*delta_position*/ 64,
+ [](double vs2, double vs1) -> double {
+ using Int = typename FPTypeInfo<double>::IntType;
+ return SignHelper(vs2, vs1, [](Int vs2i, Int vs1i, Int mask) -> Int {
+ return (vs2i & ~mask) | (vs1i & mask);
+ });
+ });
+ // Vector-scalar.
+ ResetInstruction();
+ SetSemanticFunction(&Vfsgnj);
+ BinaryOpFPTestHelperVX<float, float, float>(
+ "Vfsgnj_vx32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [](float vs2, float vs1) -> float {
+ using Int = typename FPTypeInfo<float>::IntType;
+ return SignHelper(vs2, vs1, [](Int vs2i, Int vs1i, Int mask) -> Int {
+ return (vs2i & ~mask) | (vs1i & mask);
+ });
+ });
+ ResetInstruction();
+ SetSemanticFunction(&Vfsgnj);
+ BinaryOpFPTestHelperVX<double, double, double>(
+ "Vfsgnj_vx64", /*sew*/ 64, instruction_, /*delta_position*/ 64,
+ [](double vs2, double vs1) -> double {
+ using Int = typename FPTypeInfo<double>::IntType;
+ return SignHelper(vs2, vs1, [](Int vs2i, Int vs1i, Int mask) -> Int {
+ return (vs2i & ~mask) | (vs1i & mask);
+ });
+ });
+}
+
+// The sign is the negation of that of vs1.
+TEST_F(RiscVCheriotFPInstructionsTest, Vfsgnjn) {
+ // Vector-vector.
+ SetSemanticFunction(&Vfsgnjn);
+ BinaryOpFPTestHelperVV<float, float, float>(
+ "Vfsgnjn_vv32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [](float vs2, float vs1) -> float {
+ using Int = typename FPTypeInfo<float>::IntType;
+ return SignHelper(vs2, vs1, [](Int vs2i, Int vs1i, Int mask) -> Int {
+ return (vs2i & ~mask) | (~vs1i & mask);
+ });
+ });
+ ResetInstruction();
+ SetSemanticFunction(&Vfsgnjn);
+ BinaryOpFPTestHelperVV<double, double, double>(
+ "Vfsgnjn_vv64", /*sew*/ 64, instruction_, /*delta_position*/ 64,
+ [](double vs2, double vs1) -> double {
+ using Int = typename FPTypeInfo<double>::IntType;
+ return SignHelper(vs2, vs1, [](Int vs2i, Int vs1i, Int mask) -> Int {
+ return (vs2i & ~mask) | (~vs1i & mask);
+ });
+ });
+ // Vector-scalar.
+ ResetInstruction();
+ SetSemanticFunction(&Vfsgnjn);
+ BinaryOpFPTestHelperVX<float, float, float>(
+ "Vfsgnjn_vx32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [](float vs2, float vs1) -> float {
+ using Int = typename FPTypeInfo<float>::IntType;
+ return SignHelper(vs2, vs1, [](Int vs2i, Int vs1i, Int mask) -> Int {
+ return (vs2i & ~mask) | (~vs1i & mask);
+ });
+ });
+ ResetInstruction();
+ SetSemanticFunction(&Vfsgnjn);
+ BinaryOpFPTestHelperVX<double, double, double>(
+ "Vfsgnjn_vx64", /*sew*/ 64, instruction_, /*delta_position*/ 64,
+ [](double vs2, double vs1) -> double {
+ using Int = typename FPTypeInfo<double>::IntType;
+ return SignHelper(vs2, vs1, [](Int vs2i, Int vs1i, Int mask) -> Int {
+ return (vs2i & ~mask) | (~vs1i & mask);
+ });
+ });
+}
+
+// The sign is exclusive or of the signs of vs2 and vs1.
+TEST_F(RiscVCheriotFPInstructionsTest, Vfsgnjx) {
+ // Vector-vector.
+ SetSemanticFunction(&Vfsgnjx);
+ BinaryOpFPTestHelperVV<float, float, float>(
+ "Vfsgnjx_vv32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [](float vs2, float vs1) -> float {
+ using Int = typename FPTypeInfo<float>::IntType;
+ return SignHelper(vs2, vs1, [](Int vs2i, Int vs1i, Int mask) -> Int {
+ return (vs2i & ~mask) | ((vs1i ^ vs2i) & mask);
+ });
+ });
+ ResetInstruction();
+ SetSemanticFunction(&Vfsgnjx);
+ BinaryOpFPTestHelperVV<double, double, double>(
+ "Vfsgnjx_vv64", /*sew*/ 64, instruction_, /*delta_position*/ 64,
+ [](double vs2, double vs1) -> double {
+ using Int = typename FPTypeInfo<double>::IntType;
+ return SignHelper(vs2, vs1, [](Int vs2i, Int vs1i, Int mask) -> Int {
+ return (vs2i & ~mask) | ((vs1i ^ vs2i) & mask);
+ });
+ });
+ // Vector-scalar.
+ ResetInstruction();
+ SetSemanticFunction(&Vfsgnjx);
+ BinaryOpFPTestHelperVX<float, float, float>(
+ "Vfsgnjx_vx32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [](float vs2, float vs1) -> float {
+ using Int = typename FPTypeInfo<float>::IntType;
+ return SignHelper(vs2, vs1, [](Int vs2i, Int vs1i, Int mask) -> Int {
+ return (vs2i & ~mask) | ((vs1i ^ vs2i) & mask);
+ });
+ });
+ ResetInstruction();
+ SetSemanticFunction(&Vfsgnjx);
+ BinaryOpFPTestHelperVX<double, double, double>(
+ "Vfsgnjx_vx64", /*sew*/ 64, instruction_, /*delta_position*/ 64,
+ [](double vs2, double vs1) -> double {
+ using Int = typename FPTypeInfo<double>::IntType;
+ return SignHelper(vs2, vs1, [](Int vs2i, Int vs1i, Int mask) -> Int {
+ return (vs2i & ~mask) | ((vs1i ^ vs2i) & mask);
+ });
+ });
+}
+
+template <typename T>
+bool is_snan(T value) {
+ using IntType = typename FPTypeInfo<T>::IntType;
+ IntType int_value = *reinterpret_cast<IntType *>(&value);
+ bool signal = (int_value & (1ULL << (FPTypeInfo<T>::kSigSize - 1))) == 0;
+ return std::isnan(value) && signal;
+}
+
+template <typename T>
+std::tuple<T, uint32_t> MaxMinHelper(T vs2, T vs1,
+ std::function<T(T, T)> operation) {
+ uint32_t flag = 0;
+ if (is_snan(vs2) || is_snan(vs1)) {
+ flag = static_cast<uint32_t>(FPExceptions::kInvalidOp);
+ }
+ if (std::isnan(vs2) && std::isnan(vs1)) {
+ // Canonical NaN.
+ auto canonical = FPTypeInfo<T>::kCanonicalNaN;
+ T canonical_fp = *reinterpret_cast<T *>(&canonical);
+ return std::tie(canonical_fp, flag);
+ }
+ if (std::isnan(vs2)) return std::tie(vs1, flag);
+ if (std::isnan(vs1)) return std::tie(vs2, flag);
+ if ((vs2 == 0.0) && (vs1 == 0.0)) {
+ T tmp2 = std::signbit(vs2) ? -1.0 : 1;
+ T tmp1 = std::signbit(vs1) ? -1.0 : 1;
+ return std::make_tuple(operation(tmp2, tmp1) == tmp2 ? vs2 : vs1, 0);
+ }
+ return std::make_tuple(operation(vs2, vs1), 0);
+}
+
+TEST_F(RiscVCheriotFPInstructionsTest, Vfmax) {
+ // Vector-vector.
+ SetSemanticFunction(&Vfmax);
+ BinaryOpWithFflagsFPTestHelperVV<float, float, float>(
+ "Vfmax_vv32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [](float vs2, float vs1) -> std::tuple<float, uint32_t> {
+ using T = float;
+ auto tmp = MaxMinHelper<T>(vs2, vs1, [](T vs2, T vs1) -> T {
+ return (vs1 > vs2) ? vs1 : vs2;
+ });
+ return tmp;
+ });
+ ResetInstruction();
+ SetSemanticFunction(&Vfmax);
+ BinaryOpWithFflagsFPTestHelperVV<double, double, double>(
+ "Vfmax_vv64", /*sew*/ 64, instruction_, /*delta_position*/ 64,
+ [](double vs2, double vs1) -> std::tuple<double, uint32_t> {
+ using T = double;
+ return MaxMinHelper<T>(vs2, vs1, [](T vs2, T vs1) -> T {
+ return (vs1 > vs2) ? vs1 : vs2;
+ });
+ });
+ // Vector-scalar.
+ ResetInstruction();
+ SetSemanticFunction(&Vfmax);
+ BinaryOpWithFflagsFPTestHelperVX<float, float, float>(
+ "Vfmax_vx32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [](float vs2, float vs1) -> std::tuple<float, uint32_t> {
+ using T = float;
+ return MaxMinHelper<T>(vs2, vs1, [](T vs2, T vs1) -> T {
+ return (vs1 > vs2) ? vs1 : vs2;
+ });
+ });
+ ResetInstruction();
+ SetSemanticFunction(&Vfmax);
+ BinaryOpWithFflagsFPTestHelperVX<double, double, double>(
+ "Vfmax_vx64", /*sew*/ 64, instruction_, /*delta_position*/ 64,
+ [](double vs2, double vs1) -> std::tuple<double, uint32_t> {
+ using T = double;
+ return MaxMinHelper<T>(vs2, vs1, [](T vs2, T vs1) -> T {
+ return (vs1 > vs2) ? vs1 : vs2;
+ });
+ });
+}
+
+TEST_F(RiscVCheriotFPInstructionsTest, Vfmin) {
+ // Vector-vector.
+ SetSemanticFunction(&Vfmin);
+ BinaryOpWithFflagsFPTestHelperVV<float, float, float>(
+ "Vfmin_vv32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [](float vs2, float vs1) -> std::tuple<float, uint32_t> {
+ using T = float;
+ return MaxMinHelper<T>(vs2, vs1, [](T vs2, T vs1) -> T {
+ return (vs1 < vs2) ? vs1 : vs2;
+ });
+ });
+ ResetInstruction();
+ SetSemanticFunction(&Vfmin);
+ BinaryOpWithFflagsFPTestHelperVV<double, double, double>(
+ "Vfmin_vv64", /*sew*/ 64, instruction_, /*delta_position*/ 64,
+ [](double vs2, double vs1) -> std::tuple<double, uint32_t> {
+ using T = double;
+ return MaxMinHelper<T>(vs2, vs1, [](T vs2, T vs1) -> T {
+ return (vs1 < vs2) ? vs1 : vs2;
+ });
+ });
+ // Vector-scalar.
+ ResetInstruction();
+ SetSemanticFunction(&Vfmin);
+ BinaryOpWithFflagsFPTestHelperVX<float, float, float>(
+ "Vfmin_vx32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [](float vs2, float vs1) -> std::tuple<float, uint32_t> {
+ using T = float;
+ return MaxMinHelper<T>(vs2, vs1, [](T vs2, T vs1) -> T {
+ return (vs1 < vs2) ? vs1 : vs2;
+ });
+ });
+ ResetInstruction();
+ SetSemanticFunction(&Vfmin);
+ BinaryOpWithFflagsFPTestHelperVX<double, double, double>(
+ "Vfmin_vx64", /*sew*/ 64, instruction_, /*delta_position*/ 64,
+ [](double vs2, double vs1) -> std::tuple<double, uint32_t> {
+ using T = double;
+ return MaxMinHelper<T>(vs2, vs1, [](T vs2, T vs1) -> T {
+ return (vs1 < vs2) ? vs1 : vs2;
+ });
+ });
+}
+
+TEST_F(RiscVCheriotFPInstructionsTest, Vfmerge) {
+ // Vector-scalar.
+ SetSemanticFunction(&Vfmerge);
+ BinaryOpFPWithMaskTestHelperVX<float, float, float>(
+ "Vfmerge_vx32", /*sew*/ 32, instruction_, /*delta position*/ 32,
+ [](float vs2, float vs1, bool mask) -> float {
+ return mask ? vs1 : vs2;
+ });
+ BinaryOpFPWithMaskTestHelperVX<double, double, double>(
+ "Vfmerge_vx64", /*sew*/ 64, instruction_, /*delta position*/ 64,
+ [](double vs2, double vs1, bool mask) -> double {
+ return mask ? vs1 : vs2;
+ });
+}
+
+} // namespace
diff --git a/cheriot/test/riscv_cheriot_vector_fp_reduction_instructions_test.cc b/cheriot/test/riscv_cheriot_vector_fp_reduction_instructions_test.cc
new file mode 100644
index 0000000..ba3ad17
--- /dev/null
+++ b/cheriot/test/riscv_cheriot_vector_fp_reduction_instructions_test.cc
@@ -0,0 +1,248 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cheriot/riscv_cheriot_vector_fp_reduction_instructions.h"
+
+#include <algorithm>
+#include <cstdint>
+#include <functional>
+#include <vector>
+
+#include "absl/random/random.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
+#include "absl/types/span.h"
+#include "cheriot/test/riscv_cheriot_vector_fp_test_utilities.h"
+#include "cheriot/test/riscv_cheriot_vector_instructions_test_base.h"
+#include "googlemock/include/gmock/gmock.h"
+#include "mpact/sim/generic/instruction.h"
+#include "riscv//riscv_fp_host.h"
+#include "riscv//riscv_fp_info.h"
+#include "riscv//riscv_fp_state.h"
+#include "riscv//riscv_register.h"
+
+namespace {
+
+using Instruction = ::mpact::sim::generic::Instruction;
+
+// Functions to test.
+
+using ::mpact::sim::cheriot::Vfredmax;
+using ::mpact::sim::cheriot::Vfredmin;
+using ::mpact::sim::cheriot::Vfredosum;
+using ::mpact::sim::cheriot::Vfwredosum;
+
+using ::absl::Span;
+using ::mpact::sim::riscv::FPRoundingMode;
+using ::mpact::sim::riscv::RVFpRegister;
+using ::mpact::sim::riscv::ScopedFPStatus;
+
+// Test fixture for binary fp instructions.
+class RiscVCheriotFPReductionInstructionsTest
+ : public RiscVCheriotFPInstructionsTestBase {
+ public:
+ // Helper function for floating point reduction operations.
+ template <typename Vd, typename Vs2, typename Vs1>
+ void ReductionOpFPTestHelper(absl::string_view name, int sew,
+ Instruction *inst, int delta_position,
+ std::function<Vd(Vs1, Vs2)> operation) {
+ int byte_sew = sew / 8;
+ if (byte_sew != sizeof(Vd) && byte_sew != sizeof(Vs2) &&
+ byte_sew != sizeof(Vs1)) {
+ FAIL() << name << ": selected element width != any operand types"
+ << "sew: " << sew << " Vd: " << sizeof(Vd)
+ << " Vs2: " << sizeof(Vs2) << " Vs1: " << sizeof(Vs1);
+ return;
+ }
+ // Number of elements per vector register.
+ constexpr int vs2_size = kVectorLengthInBytes / sizeof(Vs2);
+ constexpr int vs1_size = kVectorLengthInBytes / sizeof(Vs1);
+ // Input values for 8 registers.
+ Vs2 vs2_value[vs2_size * 8];
+ auto vs2_span = Span<Vs2>(vs2_value);
+ Vs1 vs1_value[vs1_size * 8];
+ auto vs1_span = Span<Vs1>(vs1_value);
+ AppendVectorRegisterOperands({kVs2, kVs1, kVmask}, {kVd});
+ auto mask_span = Span<const uint8_t>(kA5Mask);
+ SetVectorRegisterValues<uint8_t>({{kVmaskName, mask_span}});
+ // Iterate across the different lmul values.
+ for (int lmul_index = 0; lmul_index < 7; lmul_index++) {
+ // Initialize input values.
+ FillArrayWithRandomFPValues<Vs2>(vs2_span);
+ FillArrayWithRandomFPValues<Vs1>(vs1_span);
+ for (int i = 0; i < 8; i++) {
+ auto vs2_name = absl::StrCat("v", kVs2 + i);
+ auto vs1_name = absl::StrCat("v", kVs1 + i);
+ SetVectorRegisterValues<Vs2>(
+ {{vs2_name, vs2_span.subspan(vs2_size * i, vs2_size)}});
+ SetVectorRegisterValues<Vs1>(
+ {{vs1_name, vs1_span.subspan(vs1_size * i, vs1_size)}});
+ }
+ for (int vlen_count = 0; vlen_count < 4; vlen_count++) {
+ int lmul8 = kLmul8Values[lmul_index];
+ int lmul8_vs2 = lmul8 * sizeof(Vs2) / byte_sew;
+ int lmul8_vs1 = lmul8 * sizeof(Vs1) / byte_sew;
+ int lmul8_vd = lmul8 * sizeof(Vd) / byte_sew;
+ int num_values = lmul8 * kVectorLengthInBytes / (8 * byte_sew);
+ // Set vlen, but leave vlen high at least once.
+ int vlen = 1024;
+ if (vlen_count > 0) {
+ vlen =
+ absl::Uniform(absl::IntervalOpenClosed, bitgen_, 0, num_values);
+ }
+ num_values = std::min(num_values, vlen);
+ // Configure vector unit for different lmul settings.
+ uint32_t vtype =
+ (kSewSettingsByByteSize[byte_sew] << 3) | kLmulSettings[lmul_index];
+ ConfigureVectorUnit(vtype, vlen);
+
+ // Iterate across rounding modes.
+ for (int rm : {0, 1, 2, 3, 4}) {
+ rv_fp_->SetRoundingMode(static_cast<FPRoundingMode>(rm));
+
+ ClearVectorRegisterGroup(kVd, 8);
+
+ inst->Execute();
+
+ if (lmul8_vd < 1 || lmul8_vd > 64) {
+ EXPECT_TRUE(rv_vector_->vector_exception())
+ << "lmul8: vd: " << lmul8_vd;
+ rv_vector_->clear_vector_exception();
+ continue;
+ }
+
+ if (lmul8_vs2 < 1 || lmul8_vs2 > 64) {
+ EXPECT_TRUE(rv_vector_->vector_exception())
+ << "lmul8: vs2: " << lmul8_vs2;
+ rv_vector_->clear_vector_exception();
+ continue;
+ }
+
+ if (lmul8_vs1 < 1 || lmul8_vs1 > 64) {
+ EXPECT_TRUE(rv_vector_->vector_exception())
+ << "lmul8: vs1: " << lmul8_vs1;
+ rv_vector_->clear_vector_exception();
+ continue;
+ }
+ EXPECT_FALSE(rv_vector_->vector_exception());
+ // Initialize the accumulator with the value from vs1[0].
+ Vd accumulator = static_cast<Vd>(vs1_span[0]);
+ ScopedFPStatus set_fpstatus(rv_fp_->host_fp_interface());
+ for (int i = 0; i < num_values; i++) {
+ int mask_index = i >> 3;
+ int mask_offset = i & 0b111;
+ bool mask_value = (mask_span[mask_index] >> mask_offset) & 0b1;
+ if (mask_value) {
+ accumulator = operation(accumulator, vs2_span[i]);
+ }
+ }
+ auto reg_val = vreg_[kVd]->data_buffer()->Get<Vd>(0);
+ FPCompare<Vd>(accumulator, reg_val, delta_position, "");
+ }
+ }
+ }
+ }
+};
+
+// Test vector floating point sum reduction.
+TEST_F(RiscVCheriotFPReductionInstructionsTest, Vfredosum) {
+ SetSemanticFunction(&Vfredosum);
+ ReductionOpFPTestHelper<float, float, float>(
+ "Vfredosum_32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [](float val0, float val1) -> float { return val0 + val1; });
+ ResetInstruction();
+ SetSemanticFunction(&Vfredosum);
+ ReductionOpFPTestHelper<double, double, double>(
+ "Vfredosum_64", /*sew*/ 64, instruction_, /*delta_position*/ 64,
+ [](double val0, double val1) -> double { return val0 + val1; });
+}
+
+// Test vector floating point widening sum reduction.
+TEST_F(RiscVCheriotFPReductionInstructionsTest, Vfwredosum) {
+ SetSemanticFunction(&Vfwredosum);
+ ReductionOpFPTestHelper<double, float, double>(
+ "Vfwredosum_32", /*sew*/ 32, instruction_, /*delta_position*/ 64,
+ [](double val0, float val1) -> double {
+ return val0 + static_cast<double>(val1);
+ });
+}
+
+template <typename T>
+T MaxMinHelper(T vs2, T vs1, std::function<T(T, T)> operation) {
+ using UInt = typename FPTypeInfo<T>::IntType;
+ UInt vs2_uint = *reinterpret_cast<UInt *>(&vs2);
+ UInt vs1_uint = *reinterpret_cast<UInt *>(&vs1);
+ UInt mask = 1ULL << (FPTypeInfo<T>::kSigSize - 1);
+ bool nan_vs2 = std::isnan(vs2);
+ bool nan_vs1 = std::isnan(vs1);
+ if ((nan_vs2 && ((mask & vs2_uint) == 0)) ||
+ (nan_vs1 && ((mask & vs1_uint) == 0)) || (nan_vs2 && nan_vs1)) {
+ // Canonical NaN.
+ UInt canonical = ((1ULL << (FPTypeInfo<T>::kExpSize + 1)) - 1)
+ << (FPTypeInfo<T>::kSigSize - 1);
+ T canonical_fp = *reinterpret_cast<T *>(&canonical);
+ return canonical_fp;
+ }
+ if (nan_vs2) return vs1;
+ if (nan_vs1) return vs2;
+ return operation(vs2, vs1);
+}
+
+// Test vector floating point min reduction.
+TEST_F(RiscVCheriotFPReductionInstructionsTest, Vfredmin) {
+ SetSemanticFunction(&Vfredmin);
+ ReductionOpFPTestHelper<float, float, float>(
+ "Vfredmin_32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [](float val0, float val1) -> float {
+ return MaxMinHelper<float>(val0, val1,
+ [](float val0, float val1) -> float {
+ return (val0 > val1) ? val1 : val0;
+ });
+ });
+ ResetInstruction();
+ SetSemanticFunction(&Vfredmin);
+ ReductionOpFPTestHelper<double, double, double>(
+ "Vfredmin_64", /*sew*/ 64, instruction_, /*delta_position*/ 64,
+ [](double val0, double val1) -> double {
+ return MaxMinHelper<double>(val0, val1,
+ [](double val0, double val1) -> double {
+ return (val0 > val1) ? val1 : val0;
+ });
+ });
+}
+
+// Test vector floating point max reduction.
+TEST_F(RiscVCheriotFPReductionInstructionsTest, Vfredmax) {
+ SetSemanticFunction(&Vfredmax);
+ ReductionOpFPTestHelper<float, float, float>(
+ "Vfredmin_32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [](float val0, float val1) -> float {
+ return MaxMinHelper<float>(val0, val1,
+ [](float val0, float val1) -> float {
+ return (val0 < val1) ? val1 : val0;
+ });
+ });
+ ResetInstruction();
+ SetSemanticFunction(&Vfredmax);
+ ReductionOpFPTestHelper<double, double, double>(
+ "Vfredmin_64", /*sew*/ 64, instruction_, /*delta_position*/ 64,
+ [](double val0, double val1) -> double {
+ return MaxMinHelper<double>(val0, val1,
+ [](double val0, double val1) -> double {
+ return (val0 < val1) ? val1 : val0;
+ });
+ });
+}
+
+} // namespace
diff --git a/cheriot/test/riscv_cheriot_vector_fp_test_utilities.h b/cheriot/test/riscv_cheriot_vector_fp_test_utilities.h
new file mode 100644
index 0000000..6aba98e
--- /dev/null
+++ b/cheriot/test/riscv_cheriot_vector_fp_test_utilities.h
@@ -0,0 +1,948 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef MPACT_CHERIOT_TEST_RISCV_CHERIOT_VECTOR_FP_TEST_UTILITIES_H_
+#define MPACT_CHERIOT_TEST_RISCV_CHERIOT_VECTOR_FP_TEST_UTILITIES_H_
+
+#include <algorithm>
+#include <cmath>
+#include <cstdint>
+#include <functional>
+#include <tuple>
+#include <type_traits>
+#include <vector>
+
+#include "absl/random/random.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
+#include "absl/types/span.h"
+#include "cheriot/test/riscv_cheriot_vector_instructions_test_base.h"
+#include "googlemock/include/gmock/gmock.h"
+#include "mpact/sim/generic/type_helpers.h"
+#include "riscv//riscv_fp_host.h"
+#include "riscv//riscv_fp_info.h"
+#include "riscv//riscv_fp_state.h"
+#include "riscv//riscv_state.h"
+
+using ::mpact::sim::generic::operator*;
+using ::mpact::sim::riscv::FPRoundingMode;
+using ::mpact::sim::riscv::ScopedFPStatus;
+
+constexpr char kFs1Name[] = "f4";
+constexpr int kFs1 = 4;
+
+// Templated helper structs to provide information about floating point types.
+template <typename T>
+struct FPTypeInfo {
+ using IntType = typename std::make_unsigned<T>::type;
+ static const int kBitSize = 8 * sizeof(T);
+ static const int kExpSize = 0;
+ static const int kSigSize = 0;
+ static bool IsNaN(T value) { return false; }
+};
+
+template <>
+struct FPTypeInfo<float> {
+ using T = float;
+ using IntType = uint32_t;
+ static const int kBitSize = sizeof(float) << 3;
+ static const int kExpSize = 8;
+ static const int kSigSize = kBitSize - kExpSize - 1;
+ static const IntType kExpMask = ((1ULL << kExpSize) - 1) << kSigSize;
+ static const IntType kSigMask = (1ULL << kSigSize) - 1;
+ static const IntType kQNaN = kExpMask | (1ULL << (kSigSize - 1)) | 1;
+ static const IntType kSNaN = kExpMask | 1;
+ static const IntType kPosInf = kExpMask;
+ static const IntType kNegInf = kExpMask | (1ULL << (kBitSize - 1));
+ static const IntType kPosZero = 0;
+ static const IntType kNegZero = 1ULL << (kBitSize - 1);
+ static const IntType kPosDenorm = 1ULL << (kSigSize - 2);
+ static const IntType kNegDenorm =
+ (1ULL << (kBitSize - 1)) | (1ULL << (kSigSize - 2));
+ static const IntType kCanonicalNaN = 0x7fc0'0000ULL;
+ static bool IsNaN(T value) { return std::isnan(value); }
+};
+
+template <>
+struct FPTypeInfo<double> {
+ using T = double;
+ using IntType = uint64_t;
+ static const int kBitSize = sizeof(double) << 3;
+ static const int kExpSize = 11;
+ static const int kSigSize = kBitSize - kExpSize - 1;
+ static const IntType kExpMask = ((1ULL << kExpSize) - 1) << kSigSize;
+ static const IntType kSigMask = (1ULL << kSigSize) - 1;
+ static const IntType kQNaN = kExpMask | (1ULL << (kSigSize - 1)) | 1;
+ static const IntType kSNaN = kExpMask | 1;
+ static const IntType kPosInf = kExpMask;
+ static const IntType kNegInf = kExpMask | (1ULL << (kBitSize - 1));
+ static const IntType kPosZero = 0;
+ static const IntType kNegZero = 1ULL << (kBitSize - 1);
+ static const IntType kPosDenorm = 1ULL << (kSigSize - 2);
+ static const IntType kNegDenorm =
+ (1ULL << (kBitSize - 1)) | (1ULL << (kSigSize - 2));
+ static const IntType kCanonicalNaN = 0x7ff8'0000'0000'0000ULL;
+ static bool IsNaN(T value) { return std::isnan(value); }
+};
+
+// These templated functions allow for comparison of values with a tolerance
+// given for floating point types. The tolerance is stated as the bit position
+// in the mantissa of the op, with 0 being the msb of the mantissa. If the
+// bit position is beyond the mantissa, a comparison of equal is performed.
+template <typename T>
+inline void FPCompare(T op, T reg, int, absl::string_view str) {
+ EXPECT_EQ(reg, op) << str;
+}
+
+template <>
+inline void FPCompare<float>(float op, float reg, int delta_position,
+ absl::string_view str) {
+ using T = float;
+ using UInt = typename FPTypeInfo<T>::IntType;
+ if (!std::isnan(op) && !std::isinf(op) &&
+ delta_position < FPTypeInfo<T>::kSigSize) {
+ T delta;
+ UInt exp = FPTypeInfo<T>::kExpMask >> FPTypeInfo<T>::kSigSize;
+ if (exp > delta_position) {
+ exp -= delta_position;
+ UInt udelta = exp << FPTypeInfo<T>::kSigSize;
+ delta = *reinterpret_cast<T *>(&udelta);
+ } else {
+ // Becomes a denormal
+ int diff = delta_position - exp;
+ UInt udelta = 1ULL << (FPTypeInfo<T>::kSigSize - 1 - diff);
+ delta = *reinterpret_cast<T *>(&udelta);
+ }
+ EXPECT_THAT(reg, testing::NanSensitiveFloatNear(op, delta)) << str;
+ } else {
+ EXPECT_THAT(reg, testing::NanSensitiveFloatEq(op)) << str;
+ }
+}
+
+template <>
+inline void FPCompare<double>(double op, double reg, int delta_position,
+ absl::string_view str) {
+ using T = double;
+ using UInt = typename FPTypeInfo<T>::IntType;
+ if (!std::isnan(op) && !std::isinf(op) &&
+ delta_position < FPTypeInfo<T>::kSigSize) {
+ T delta;
+ UInt exp = FPTypeInfo<T>::kExpMask >> FPTypeInfo<T>::kSigSize;
+ if (exp > delta_position) {
+ exp -= delta_position;
+ UInt udelta = exp << FPTypeInfo<T>::kSigSize;
+ delta = *reinterpret_cast<T *>(&udelta);
+ } else {
+ // Becomes a denormal
+ int diff = delta_position - exp;
+ UInt udelta = 1ULL << (FPTypeInfo<T>::kSigSize - 1 - diff);
+ delta = *reinterpret_cast<T *>(&udelta);
+ }
+ EXPECT_THAT(reg, testing::NanSensitiveDoubleNear(op, delta)) << str;
+ } else {
+ EXPECT_THAT(reg, testing::NanSensitiveDoubleEq(op)) << str;
+ }
+}
+
+template <typename FP>
+FP OptimizationBarrier(FP op) {
+ asm volatile("" : "+X"(op));
+ return op;
+}
+
+namespace internal {
+
+// These are predicates used in the following NaNBox function definitions, as
+// part of the enable_if construct.
+template <typename S, typename D>
+struct EqualSize {
+ static const bool value = sizeof(S) == sizeof(D) &&
+ std::is_floating_point<S>::value &&
+ std::is_integral<D>::value;
+};
+
+template <typename S, typename D>
+struct GreaterSize {
+ static const bool value =
+ sizeof(S) > sizeof(D) &&
+ std::is_floating_point<S>::value &&std::is_integral<D>::value;
+};
+
+template <typename S, typename D>
+struct LessSize {
+ static const bool value = sizeof(S) < sizeof(D) &&
+ std::is_floating_point<S>::value &&
+ std::is_integral<D>::value;
+};
+
+} // namespace internal
+
+// Template functions to NaN box a floating point value when being assigned
+// to a wider register. The first version places a smaller floating point value
+// in a NaN box (all upper bits in the word are set to 1).
+
+// Enable_if is used to select the proper implementation for different S and D
+// type combinations. It uses the SFINAE (substitution failure is not an error)
+// "feature" of C++ to hide the implementation that don't match the predicate
+// from being resolved.
+
+template <typename S, typename D>
+inline typename std::enable_if<internal::LessSize<S, D>::value, D>::type NaNBox(
+ S value) {
+ using SInt = typename FPTypeInfo<S>::IntType;
+ SInt sval = *reinterpret_cast<SInt *>(&value);
+ D dval = (~static_cast<D>(0) << (sizeof(S) * 8)) | sval;
+ return *reinterpret_cast<D *>(&dval);
+}
+
+// This version does a straight copy - as the data types are the same size.
+template <typename S, typename D>
+inline typename std::enable_if<internal::EqualSize<S, D>::value, D>::type
+NaNBox(S value) {
+ return *reinterpret_cast<D *>(&value);
+}
+
+// Signal error if the register is smaller than the floating point value.
+template <typename S, typename D>
+inline typename std::enable_if<internal::GreaterSize<S, D>::value, D>::type
+NaNBox(S value) {
+ // No return statement, so error will be reported.
+}
+
+// Test fixture for binary fp instructions.
+class RiscVCheriotFPInstructionsTestBase
+ : public RiscVCheriotVectorInstructionsTestBase {
+ public:
+ RiscVCheriotFPInstructionsTestBase() {
+ rv_fp_ = new mpact::sim::riscv::RiscVFPState(state_->csr_set(), state_);
+ state_->set_rv_fp(rv_fp_);
+ }
+ ~RiscVCheriotFPInstructionsTestBase() override {
+ state_->set_rv_fp(nullptr);
+ delete rv_fp_;
+ }
+
+ // Construct a random FP value by separately generating integer values for
+ // sign, exponent and mantissa.
+ template <typename T>
+ T RandomFPValue() {
+ using UInt = typename FPTypeInfo<T>::IntType;
+ UInt sign = absl::Uniform(absl::IntervalClosed, bitgen_, 0ULL, 1ULL);
+ UInt exp = absl::Uniform(absl::IntervalClosedOpen, bitgen_, 0ULL,
+ 1ULL << FPTypeInfo<T>::kExpSize);
+ UInt sig = absl::Uniform(absl::IntervalClosedOpen, bitgen_, 0ULL,
+ 1ULL << FPTypeInfo<T>::kSigSize);
+ UInt value = (sign & 1) << (FPTypeInfo<T>::kBitSize - 1) |
+ (exp << FPTypeInfo<T>::kSigSize) | sig;
+ T val = *reinterpret_cast<T *>(&value);
+ return val;
+ }
+
+ // This method uses random values for each field in the fp number.
+ template <typename T>
+ void FillArrayWithRandomFPValues(absl::Span<T> span) {
+ for (auto &val : span) {
+ val = RandomFPValue<T>();
+ }
+ }
+
+ template <typename Vs2, typename Vs1>
+ void InitializeInputs(absl::Span<Vs2> vs2_span, absl::Span<Vs1> vs1_span,
+ absl::Span<uint8_t> mask_span, int count) {
+ // Initialize input values.
+ FillArrayWithRandomFPValues<Vs2>(vs2_span);
+ FillArrayWithRandomFPValues<Vs1>(vs1_span);
+ using Vs2Int = typename FPTypeInfo<Vs2>::IntType;
+ using Vs1Int = typename FPTypeInfo<Vs1>::IntType;
+ // Overwrite the first few values of the input data with infinities,
+ // zeros, denormals and NaNs.
+ *reinterpret_cast<Vs2Int *>(&vs2_span[0]) = FPTypeInfo<Vs2>::kQNaN;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[1]) = FPTypeInfo<Vs2>::kSNaN;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[2]) = FPTypeInfo<Vs2>::kPosInf;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[3]) = FPTypeInfo<Vs2>::kNegInf;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[4]) = FPTypeInfo<Vs2>::kPosZero;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[5]) = FPTypeInfo<Vs2>::kNegZero;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[6]) = FPTypeInfo<Vs2>::kPosDenorm;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[7]) = FPTypeInfo<Vs2>::kNegDenorm;
+ if (count == 4) {
+ *reinterpret_cast<Vs1Int *>(&vs1_span[0]) = FPTypeInfo<Vs1>::kQNaN;
+ *reinterpret_cast<Vs1Int *>(&vs1_span[1]) = FPTypeInfo<Vs1>::kSNaN;
+ *reinterpret_cast<Vs1Int *>(&vs1_span[2]) = FPTypeInfo<Vs1>::kPosInf;
+ *reinterpret_cast<Vs1Int *>(&vs1_span[3]) = FPTypeInfo<Vs1>::kNegInf;
+ *reinterpret_cast<Vs1Int *>(&vs1_span[4]) = FPTypeInfo<Vs1>::kPosZero;
+ *reinterpret_cast<Vs1Int *>(&vs1_span[5]) = FPTypeInfo<Vs1>::kNegZero;
+ *reinterpret_cast<Vs1Int *>(&vs1_span[6]) = FPTypeInfo<Vs1>::kPosDenorm;
+ *reinterpret_cast<Vs1Int *>(&vs1_span[7]) = FPTypeInfo<Vs1>::kNegDenorm;
+ } else if (count == 5) {
+ *reinterpret_cast<Vs1Int *>(&vs1_span[7]) = FPTypeInfo<Vs1>::kQNaN;
+ *reinterpret_cast<Vs1Int *>(&vs1_span[6]) = FPTypeInfo<Vs1>::kSNaN;
+ *reinterpret_cast<Vs1Int *>(&vs1_span[5]) = FPTypeInfo<Vs1>::kPosInf;
+ *reinterpret_cast<Vs1Int *>(&vs1_span[4]) = FPTypeInfo<Vs1>::kNegInf;
+ *reinterpret_cast<Vs1Int *>(&vs1_span[3]) = FPTypeInfo<Vs1>::kPosZero;
+ *reinterpret_cast<Vs1Int *>(&vs1_span[2]) = FPTypeInfo<Vs1>::kNegZero;
+ *reinterpret_cast<Vs1Int *>(&vs1_span[1]) = FPTypeInfo<Vs1>::kPosDenorm;
+ *reinterpret_cast<Vs1Int *>(&vs1_span[0]) = FPTypeInfo<Vs1>::kNegDenorm;
+ } else if (count == 6) {
+ *reinterpret_cast<Vs1Int *>(&vs1_span[0]) = FPTypeInfo<Vs1>::kQNaN;
+ *reinterpret_cast<Vs1Int *>(&vs1_span[1]) = FPTypeInfo<Vs1>::kSNaN;
+ *reinterpret_cast<Vs1Int *>(&vs1_span[2]) = FPTypeInfo<Vs1>::kNegInf;
+ *reinterpret_cast<Vs1Int *>(&vs1_span[3]) = FPTypeInfo<Vs1>::kPosInf;
+ *reinterpret_cast<Vs1Int *>(&vs1_span[4]) = FPTypeInfo<Vs1>::kNegZero;
+ *reinterpret_cast<Vs1Int *>(&vs1_span[5]) = FPTypeInfo<Vs1>::kPosZero;
+ *reinterpret_cast<Vs1Int *>(&vs1_span[6]) = FPTypeInfo<Vs1>::kNegDenorm;
+ *reinterpret_cast<Vs1Int *>(&vs1_span[7]) = FPTypeInfo<Vs1>::kPosDenorm;
+ }
+ // Modify the first mask bits to use each of the special floating
+ // point values.
+ mask_span[0] = 0xff;
+ }
+
+ // Floating point test needs to ensure to use the fp special values (inf,
+ // NaN etc.) during testing, not just random values.
+ template <typename Vd, typename Vs2, typename Vs1>
+ void BinaryOpFPTestHelperVV(absl::string_view name, int sew,
+ Instruction *inst, int delta_position,
+ std::function<Vd(Vs2, Vs1)> operation) {
+ int byte_sew = sew / 8;
+ if (byte_sew != sizeof(Vd) && byte_sew != sizeof(Vs2) &&
+ byte_sew != sizeof(Vs1)) {
+ FAIL() << name << ": selected element width != any operand types"
+ << " sew: " << sew << " Vd: " << sizeof(Vd)
+ << " Vs2: " << sizeof(Vs2) << " Vs1: " << sizeof(Vs1);
+ return;
+ }
+ // Number of elements per vector register.
+ constexpr int vs2_size = kVectorLengthInBytes / sizeof(Vs2);
+ constexpr int vs1_size = kVectorLengthInBytes / sizeof(Vs1);
+ // Input values for 8 registers.
+ Vs2 vs2_value[vs2_size * 8];
+ Vs1 vs1_value[vs1_size * 8];
+ auto vs2_span = Span<Vs2>(vs2_value);
+ auto vs1_span = Span<Vs1>(vs1_value);
+ AppendVectorRegisterOperands({kVs2, kVs1, kVmask}, {kVd});
+ SetVectorRegisterValues<uint8_t>(
+ {{kVmaskName, Span<const uint8_t>(kA5Mask)}});
+ // Iterate across different lmul values.
+ for (int lmul_index = 0; lmul_index < 7; lmul_index++) {
+ InitializeInputs<Vs2, Vs1>(vs2_span, vs1_span,
+ vreg_[kVmask]->data_buffer()->Get<uint8_t>(),
+ lmul_index);
+ // Set values for all 8 vector registers in the vector register group.
+ for (int i = 0; i < 8; i++) {
+ auto vs2_name = absl::StrCat("v", kVs2 + i);
+ auto vs1_name = absl::StrCat("v", kVs1 + i);
+ SetVectorRegisterValues<Vs2>(
+ {{vs2_name, vs2_span.subspan(vs2_size * i, vs2_size)}});
+ SetVectorRegisterValues<Vs1>(
+ {{vs1_name, vs1_span.subspan(vs1_size * i, vs1_size)}});
+ }
+ int lmul8 = kLmul8Values[lmul_index];
+ int lmul8_vd = lmul8 * sizeof(Vd) / byte_sew;
+ int lmul8_vs2 = lmul8 * sizeof(Vs2) / byte_sew;
+ int lmul8_vs1 = lmul8 * sizeof(Vs1) / byte_sew;
+ int num_reg_values = lmul8 * kVectorLengthInBytes / (8 * byte_sew);
+ // Configure vector unit for different lmul settings.
+ uint32_t vtype =
+ (kSewSettingsByByteSize[byte_sew] << 3) | kLmulSettings[lmul_index];
+ int vstart = 0;
+ // Try different vstart values (updated at the bottom of the loop).
+ for (int vstart_count = 0; vstart_count < 4; vstart_count++) {
+ int vlen = 1024;
+ // Try different vector lengths (updated at the bottom of the loop).
+ for (int vlen_count = 0; vlen_count < 4; vlen_count++) {
+ ASSERT_TRUE(vlen > vstart);
+ int num_values = std::min(num_reg_values, vlen);
+ ConfigureVectorUnit(vtype, vlen);
+ // Iterate across rounding modes.
+ for (int rm : {0, 1, 2, 3, 4}) {
+ rv_fp_->SetRoundingMode(static_cast<FPRoundingMode>(rm));
+ rv_vector_->set_vstart(vstart);
+ ClearVectorRegisterGroup(kVd, 8);
+
+ inst->Execute();
+ if (lmul8_vd < 1 || lmul8_vd > 64) {
+ EXPECT_TRUE(rv_vector_->vector_exception())
+ << "lmul8: vd: " << lmul8_vd;
+ rv_vector_->clear_vector_exception();
+ continue;
+ }
+
+ if (lmul8_vs2 < 1 || lmul8_vs2 > 64) {
+ EXPECT_TRUE(rv_vector_->vector_exception())
+ << "lmul8: vs2: " << lmul8_vs2;
+ rv_vector_->clear_vector_exception();
+ continue;
+ }
+
+ if (lmul8_vs1 < 1 || lmul8_vs1 > 64) {
+ EXPECT_TRUE(rv_vector_->vector_exception())
+ << "lmul8: vs1: " << lmul8_vs1;
+ rv_vector_->clear_vector_exception();
+ continue;
+ }
+ EXPECT_FALSE(rv_vector_->vector_exception());
+ EXPECT_EQ(rv_vector_->vstart(), 0);
+ int count = 0;
+ for (int reg = kVd; reg < kVd + 8; reg++) {
+ for (int i = 0; i < kVectorLengthInBytes / sizeof(Vd); i++) {
+ int mask_index = count >> 3;
+ int mask_offset = count & 0b111;
+ bool mask_value = true;
+ // The first 8 bits of the mask are set to true above, so
+ // only read the mask value after the first byte.
+ if (mask_index > 0) {
+ mask_value =
+ ((kA5Mask[mask_index] >> mask_offset) & 0b1) != 0;
+ }
+ auto reg_val = vreg_[reg]->data_buffer()->Get<Vd>(i);
+ auto int_reg_val =
+ *reinterpret_cast<typename FPTypeInfo<Vd>::IntType *>(
+ ®_val);
+ if ((count >= vstart) && mask_value && (count < num_values)) {
+ ScopedFPStatus set_fpstatus(rv_fp_->host_fp_interface());
+ auto op_val = operation(vs2_value[count], vs1_value[count]);
+ auto int_op_val =
+ *reinterpret_cast<typename FPTypeInfo<Vd>::IntType *>(
+ &op_val);
+ auto int_vs2_val =
+ *reinterpret_cast<typename FPTypeInfo<Vs2>::IntType *>(
+ &vs2_value[count]);
+ auto int_vs1_val =
+ *reinterpret_cast<typename FPTypeInfo<Vs1>::IntType *>(
+ &vs1_value[count]);
+ FPCompare<Vd>(
+ op_val, reg_val, delta_position,
+ absl::StrCat(name, "[", count, "] op(", vs2_value[count],
+ "[0x", absl::Hex(int_vs2_val), "], ",
+ vs1_value[count], "[0x",
+ absl::Hex(int_vs1_val),
+ "]) = ", absl::Hex(int_op_val), " != reg[",
+ reg, "][", i, "] (", reg_val, " [0x",
+ absl::Hex(int_reg_val), "]) lmul8(", lmul8,
+ ") rm = ", *(rv_fp_->GetRoundingMode())));
+ } else {
+ EXPECT_EQ(0, reg_val) << absl::StrCat(
+ name, " 0 != reg[", reg, "][", i, "] (", reg_val,
+ " [0x", absl::Hex(int_reg_val), "]) lmul8(", lmul8, ")");
+ }
+ count++;
+ }
+ if (HasFailure()) return;
+ }
+ }
+ vlen = absl::Uniform(absl::IntervalOpenClosed, bitgen_, vstart,
+ num_reg_values);
+ }
+ vstart = absl::Uniform(absl::IntervalOpen, bitgen_, 0, num_reg_values);
+ }
+ }
+ }
+
+ // Floating point test needs to ensure to use the fp special values (inf,
+ // NaN etc.) during testing, not just random values.
+ template <typename Vd, typename Vs2, typename Vs1>
+ void BinaryOpWithFflagsFPTestHelperVV(
+ absl::string_view name, int sew, Instruction *inst, int delta_position,
+ std::function<std::tuple<Vd, uint32_t>(Vs2, Vs1)> operation) {
+ using VdInt = typename FPTypeInfo<Vd>::IntType;
+ using Vs2Int = typename FPTypeInfo<Vs2>::IntType;
+ using Vs1Int = typename FPTypeInfo<Vs1>::IntType;
+ int byte_sew = sew / 8;
+ if (byte_sew != sizeof(Vd) && byte_sew != sizeof(Vs2) &&
+ byte_sew != sizeof(Vs1)) {
+ FAIL() << name << ": selected element width != any operand types"
+ << " sew: " << sew << " Vd: " << sizeof(Vd)
+ << " Vs2: " << sizeof(Vs2) << " Vs1: " << sizeof(Vs1);
+ return;
+ }
+ // Number of elements per vector register.
+ constexpr int vs2_size = kVectorLengthInBytes / sizeof(Vs2);
+ constexpr int vs1_size = kVectorLengthInBytes / sizeof(Vs1);
+ // Input values for 8 registers.
+ Vs2 vs2_value[vs2_size * 8];
+ Vs1 vs1_value[vs1_size * 8];
+ auto vs2_span = Span<Vs2>(vs2_value);
+ auto vs1_span = Span<Vs1>(vs1_value);
+ AppendVectorRegisterOperands({kVs2, kVs1, kVmask}, {kVd});
+ auto *flag_op = rv_fp_->fflags()->CreateSetDestinationOperand(0, "fflags");
+ instruction_->AppendDestination(flag_op);
+ SetVectorRegisterValues<uint8_t>(
+ {{kVmaskName, Span<const uint8_t>(kA5Mask)}});
+ // Iterate across different lmul values.
+ for (int lmul_index = 0; lmul_index < 7; lmul_index++) {
+ InitializeInputs<Vs2, Vs1>(vs2_span, vs1_span,
+ vreg_[kVmask]->data_buffer()->Get<uint8_t>(),
+ lmul_index);
+
+ // Set values for all 8 vector registers in the vector register group.
+ for (int i = 0; i < 8; i++) {
+ auto vs2_name = absl::StrCat("v", kVs2 + i);
+ auto vs1_name = absl::StrCat("v", kVs1 + i);
+ SetVectorRegisterValues<Vs2>(
+ {{vs2_name, vs2_span.subspan(vs2_size * i, vs2_size)}});
+ SetVectorRegisterValues<Vs1>(
+ {{vs1_name, vs1_span.subspan(vs1_size * i, vs1_size)}});
+ }
+ int lmul8 = kLmul8Values[lmul_index];
+ int lmul8_vd = lmul8 * sizeof(Vd) / byte_sew;
+ int lmul8_vs2 = lmul8 * sizeof(Vs2) / byte_sew;
+ int lmul8_vs1 = lmul8 * sizeof(Vs1) / byte_sew;
+ int num_reg_values = lmul8 * kVectorLengthInBytes / (8 * byte_sew);
+ // Configure vector unit for different lmul settings.
+ uint32_t vtype =
+ (kSewSettingsByByteSize[byte_sew] << 3) | kLmulSettings[lmul_index];
+ int vstart = 0;
+ // Try different vstart values (updated at the bottom of the loop).
+ for (int vstart_count = 0; vstart_count < 4; vstart_count++) {
+ int vlen = 1024;
+ // Try different vector lengths (updated at the bottom of the loop).
+ for (int vlen_count = 0; vlen_count < 4; vlen_count++) {
+ ASSERT_TRUE(vlen > vstart);
+ int num_values = std::min(num_reg_values, vlen);
+ ConfigureVectorUnit(vtype, vlen);
+ // Iterate across rounding modes.
+ for (int rm : {0, 1, 2, 3, 4}) {
+ rv_fp_->SetRoundingMode(static_cast<FPRoundingMode>(rm));
+ rv_vector_->set_vstart(vstart);
+ ClearVectorRegisterGroup(kVd, 8);
+ rv_fp_->fflags()->Write(static_cast<uint32_t>(0));
+
+ inst->Execute();
+ if (lmul8_vd < 1 || lmul8_vd > 64) {
+ EXPECT_TRUE(rv_vector_->vector_exception())
+ << "lmul8: vd: " << lmul8_vd;
+ rv_vector_->clear_vector_exception();
+ continue;
+ }
+
+ if (lmul8_vs2 < 1 || lmul8_vs2 > 64) {
+ EXPECT_TRUE(rv_vector_->vector_exception())
+ << "lmul8: vs2: " << lmul8_vs2;
+ rv_vector_->clear_vector_exception();
+ continue;
+ }
+
+ if (lmul8_vs1 < 1 || lmul8_vs1 > 64) {
+ EXPECT_TRUE(rv_vector_->vector_exception())
+ << "lmul8: vs1: " << lmul8_vs1;
+ rv_vector_->clear_vector_exception();
+ continue;
+ }
+ EXPECT_FALSE(rv_vector_->vector_exception());
+ EXPECT_EQ(rv_vector_->vstart(), 0);
+ int count = 0;
+ uint32_t fflags_test = 0;
+ for (int reg = kVd; reg < kVd + 8; reg++) {
+ for (int i = 0; i < kVectorLengthInBytes / sizeof(Vd); i++) {
+ int mask_index = count >> 3;
+ int mask_offset = count & 0b111;
+ bool mask_value = true;
+ // The first 8 bits of the mask are set to true above, so
+ // only read the mask value after the first byte.
+ if (mask_index > 0) {
+ mask_value =
+ ((kA5Mask[mask_index] >> mask_offset) & 0b1) != 0;
+ }
+ auto reg_val = vreg_[reg]->data_buffer()->Get<Vd>(i);
+ auto int_reg_val = *reinterpret_cast<VdInt *>(®_val);
+ if ((count >= vstart) && mask_value && (count < num_values)) {
+ Vd op_val;
+ uint32_t flag;
+ {
+ ScopedFPStatus set_fpstatus(rv_fp_->host_fp_interface());
+ auto [op_val_tmp, flag_tmp] =
+ operation(vs2_value[count], vs1_value[count]);
+ op_val = op_val_tmp;
+ flag = flag_tmp;
+ }
+ auto int_op_val = *reinterpret_cast<VdInt *>(&op_val);
+ auto int_vs2_val =
+ *reinterpret_cast<Vs2Int *>(&vs2_value[count]);
+ auto int_vs1_val =
+ *reinterpret_cast<Vs1Int *>(&vs1_value[count]);
+ FPCompare<Vd>(
+ op_val, reg_val, delta_position,
+ absl::StrCat(name, "[", count, "] op(", vs2_value[count],
+ "[0x", absl::Hex(int_vs2_val), "], ",
+ vs1_value[count], "[0x",
+ absl::Hex(int_vs1_val),
+ "]) = ", absl::Hex(int_op_val), " != reg[",
+ reg, "][", i, "] (", reg_val, " [0x",
+ absl::Hex(int_reg_val), "]) lmul8(", lmul8,
+ ") rm = ", *(rv_fp_->GetRoundingMode())));
+ fflags_test |= flag;
+ } else {
+ EXPECT_EQ(0, reg_val) << absl::StrCat(
+ name, " 0 != reg[", reg, "][", i, "] (", reg_val,
+ " [0x", absl::Hex(int_reg_val), "]) lmul8(", lmul8, ")");
+ }
+ count++;
+ }
+ }
+ uint32_t fflags = rv_fp_->fflags()->AsUint32();
+ EXPECT_EQ(fflags, fflags_test) << name;
+ if (HasFailure()) return;
+ }
+ vlen = absl::Uniform(absl::IntervalOpenClosed, bitgen_, vstart,
+ num_reg_values);
+ }
+ vstart = absl::Uniform(absl::IntervalOpen, bitgen_, 0, num_reg_values);
+ }
+ }
+ }
+
+ // Floating point test needs to ensure to use the fp special values (inf,
+ // NaN etc.) during testing, not just random values.
+ template <typename Vd, typename Vs2, typename Fs1>
+ void BinaryOpWithFflagsFPTestHelperVX(
+ absl::string_view name, int sew, Instruction *inst, int delta_position,
+ std::function<std::tuple<Vd, uint32_t>(Vs2, Fs1)> operation) {
+ using VdInt = typename FPTypeInfo<Vd>::IntType;
+ using Vs2Int = typename FPTypeInfo<Vs2>::IntType;
+ using Fs1Int = typename FPTypeInfo<Fs1>::IntType;
+ int byte_sew = sew / 8;
+ if (byte_sew != sizeof(Vd) && byte_sew != sizeof(Vs2) &&
+ byte_sew != sizeof(Fs1)) {
+ FAIL() << name << ": selected element width != any operand types"
+ << " sew: " << sew << " Vd: " << sizeof(Vd)
+ << " Vs2: " << sizeof(Vs2) << " Fs1: " << sizeof(Fs1);
+ return;
+ }
+ // Number of elements per vector register.
+ constexpr int vs2_size = kVectorLengthInBytes / sizeof(Vs2);
+ // Input values for 8 registers.
+ Vs2 vs2_value[vs2_size * 8];
+ auto vs2_span = Span<Vs2>(vs2_value);
+ AppendVectorRegisterOperands({kVs2}, {kVd});
+ AppendRegisterOperands({kFs1Name}, {});
+ auto *flag_op = rv_fp_->fflags()->CreateSetDestinationOperand(0, "fflags");
+ instruction_->AppendDestination(flag_op);
+ AppendVectorRegisterOperands({kVmask}, {});
+ SetVectorRegisterValues<uint8_t>(
+ {{kVmaskName, Span<const uint8_t>(kA5Mask)}});
+ // Iterate across different lmul values.
+ for (int lmul_index = 0; lmul_index < 7; lmul_index++) {
+ // Initialize input values.
+ FillArrayWithRandomFPValues<Vs2>(vs2_span);
+ // Overwrite the first few values of the input data with infinities,
+ // zeros, denormals and NaNs.
+ *reinterpret_cast<Vs2Int *>(&vs2_span[0]) = FPTypeInfo<Vs2>::kQNaN;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[1]) = FPTypeInfo<Vs2>::kSNaN;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[2]) = FPTypeInfo<Vs2>::kPosInf;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[3]) = FPTypeInfo<Vs2>::kNegInf;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[4]) = FPTypeInfo<Vs2>::kPosZero;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[5]) = FPTypeInfo<Vs2>::kNegZero;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[6]) = FPTypeInfo<Vs2>::kPosDenorm;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[7]) = FPTypeInfo<Vs2>::kNegDenorm;
+ // Modify the first mask bits to use each of the special floating
+ // point values.
+ vreg_[kVmask]->data_buffer()->Set<uint8_t>(0, 0xff);
+
+ // Set values for all 8 vector registers in the vector register group.
+ for (int i = 0; i < 8; i++) {
+ auto vs2_name = absl::StrCat("v", kVs2 + i);
+ SetVectorRegisterValues<Vs2>(
+ {{vs2_name, vs2_span.subspan(vs2_size * i, vs2_size)}});
+ }
+ int lmul8 = kLmul8Values[lmul_index];
+ int lmul8_vd = lmul8 * sizeof(Vd) / byte_sew;
+ int lmul8_vs2 = lmul8 * sizeof(Vs2) / byte_sew;
+ int lmul8_vs1 = lmul8 * sizeof(Fs1) / byte_sew;
+ int num_reg_values = lmul8 * kVectorLengthInBytes / (8 * byte_sew);
+ // Configure vector unit for different lmul settings.
+ uint32_t vtype =
+ (kSewSettingsByByteSize[byte_sew] << 3) | kLmulSettings[lmul_index];
+ int vstart = 0;
+ // Try different vstart values (updated at the bottom of the loop).
+ for (int vstart_count = 0; vstart_count < 4; vstart_count++) {
+ int vlen = 1024;
+ // Try different vector lengths (updated at the bottom of the loop).
+ for (int vlen_count = 0; vlen_count < 4; vlen_count++) {
+ ASSERT_TRUE(vlen > vstart);
+ int num_values = std::min(num_reg_values, vlen);
+ ConfigureVectorUnit(vtype, vlen);
+ // Generate a new rs1 value.
+ Fs1 fs1_value = RandomFPValue<Fs1>();
+ // Need to NaN box the value, that is, if the register value type
+ // is wider than the data type for a floating point value, the
+ // upper bits are all set to 1's.
+ typename RVFpRegister::ValueType fs1_reg_value =
+ NaNBox<Fs1, typename RVFpRegister::ValueType>(fs1_value);
+ SetRegisterValues<typename RVFpRegister::ValueType, RVFpRegister>(
+ {{kFs1Name, fs1_reg_value}});
+ // Iterate across rounding modes.
+ for (int rm : {0, 1, 2, 3, 4}) {
+ rv_fp_->SetRoundingMode(static_cast<FPRoundingMode>(rm));
+ rv_vector_->set_vstart(vstart);
+ ClearVectorRegisterGroup(kVd, 8);
+ rv_fp_->fflags()->Write(static_cast<uint32_t>(0));
+
+ inst->Execute();
+ if (lmul8_vd < 1 || lmul8_vd > 64) {
+ EXPECT_TRUE(rv_vector_->vector_exception())
+ << "lmul8: vd: " << lmul8_vd;
+ rv_vector_->clear_vector_exception();
+ continue;
+ }
+
+ if (lmul8_vs2 < 1 || lmul8_vs2 > 64) {
+ EXPECT_TRUE(rv_vector_->vector_exception())
+ << "lmul8: vs2: " << lmul8_vs2;
+ rv_vector_->clear_vector_exception();
+ continue;
+ }
+
+ if (lmul8_vs1 < 1 || lmul8_vs1 > 64) {
+ EXPECT_TRUE(rv_vector_->vector_exception())
+ << "lmul8: vs1: " << lmul8_vs1;
+ rv_vector_->clear_vector_exception();
+ continue;
+ }
+ EXPECT_FALSE(rv_vector_->vector_exception());
+ EXPECT_EQ(rv_vector_->vstart(), 0);
+ int count = 0;
+ uint32_t fflags_test = 0;
+ for (int reg = kVd; reg < kVd + 8; reg++) {
+ for (int i = 0; i < kVectorLengthInBytes / sizeof(Vd); i++) {
+ int mask_index = count >> 3;
+ int mask_offset = count & 0b111;
+ bool mask_value = true;
+ // The first 8 bits of the mask are set to true above, so
+ // only read the mask value after the first byte.
+ if (mask_index > 0) {
+ mask_value =
+ ((kA5Mask[mask_index] >> mask_offset) & 0b1) != 0;
+ }
+ auto reg_val = vreg_[reg]->data_buffer()->Get<Vd>(i);
+ auto int_reg_val = *reinterpret_cast<VdInt *>(®_val);
+ if ((count >= vstart) && mask_value && (count < num_values)) {
+ Vd op_val;
+ uint32_t flag;
+ {
+ ScopedFPStatus set_fpstatus(rv_fp_->host_fp_interface());
+ auto [op_val_tmp, flag_tmp] =
+ operation(vs2_value[count], fs1_value);
+ op_val = op_val_tmp;
+ flag = flag_tmp;
+ }
+ auto int_op_val = *reinterpret_cast<VdInt *>(&op_val);
+ auto int_vs2_val =
+ *reinterpret_cast<Vs2Int *>(&vs2_value[count]);
+ auto int_fs1_val = *reinterpret_cast<Fs1Int *>(&fs1_value);
+ FPCompare<Vd>(
+ op_val, reg_val, delta_position,
+ absl::StrCat(name, "[", count, "] op(", vs2_value[count],
+ "[0x", absl::Hex(int_vs2_val), "], ",
+ fs1_value, "[0x", absl::Hex(int_fs1_val),
+ "]) = ", absl::Hex(int_op_val), " != reg[",
+ reg, "][", i, "] (", reg_val, " [0x",
+ absl::Hex(int_reg_val), "]) lmul8(", lmul8,
+ ") rm = ", *(rv_fp_->GetRoundingMode())));
+ fflags_test |= flag;
+ } else {
+ EXPECT_EQ(0, reg_val) << absl::StrCat(
+ name, " 0 != reg[", reg, "][", i, "] (", reg_val,
+ " [0x", absl::Hex(int_reg_val), "]) lmul8(", lmul8, ")");
+ }
+ count++;
+ }
+ }
+ uint32_t fflags = rv_fp_->fflags()->AsUint32();
+ EXPECT_EQ(fflags, fflags_test) << name;
+ if (HasFailure()) return;
+ }
+ vlen = absl::Uniform(absl::IntervalOpenClosed, bitgen_, vstart,
+ num_reg_values);
+ }
+ vstart = absl::Uniform(absl::IntervalOpen, bitgen_, 0, num_reg_values);
+ }
+ }
+ }
+
+ // Floating point test needs to ensure to use the fp special values (inf,
+ // NaN etc.) during testing, not just random values. This function handles
+ // vector scalar instructions.
+ template <typename Vd, typename Vs2, typename Fs1>
+ void BinaryOpFPWithMaskTestHelperVX(
+ absl::string_view name, int sew, Instruction *inst, int delta_position,
+ std::function<Vd(Vs2, Fs1, bool)> operation) {
+ using VdInt = typename FPTypeInfo<Vd>::IntType;
+ using Vs2Int = typename FPTypeInfo<Vs2>::IntType;
+ using Fs1Int = typename FPTypeInfo<Fs1>::IntType;
+ int byte_sew = sew / 8;
+ if (byte_sew != sizeof(Vd) && byte_sew != sizeof(Vs2) &&
+ byte_sew != sizeof(Fs1)) {
+ FAIL() << name << ": selected element width != any operand types"
+ << "sew: " << sew << " Vd: " << sizeof(Vd)
+ << " Vs2: " << sizeof(Vs2) << " Fs1: " << sizeof(Fs1);
+ return;
+ }
+ // Number of elements per vector register.
+ constexpr int vs2_size = kVectorLengthInBytes / sizeof(Vs2);
+ // Input values for 8 registers.
+ Vs2 vs2_value[vs2_size * 8];
+ auto vs2_span = Span<Vs2>(vs2_value);
+ AppendVectorRegisterOperands({kVs2}, {kVd});
+ AppendRegisterOperands({kFs1Name}, {});
+ AppendVectorRegisterOperands({kVmask}, {});
+ SetVectorRegisterValues<uint8_t>(
+ {{kVmaskName, Span<const uint8_t>(kA5Mask)}});
+ // Iterate across different lmul values.
+ for (int lmul_index = 0; lmul_index < 7; lmul_index++) {
+ // Initialize input values.
+ FillArrayWithRandomFPValues<Vs2>(vs2_span);
+ // Overwrite the first few values of the input data with infinities,
+ // zeros, denormals and NaNs.
+ *reinterpret_cast<Vs2Int *>(&vs2_span[0]) = FPTypeInfo<Vs2>::kQNaN;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[1]) = FPTypeInfo<Vs2>::kSNaN;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[2]) = FPTypeInfo<Vs2>::kPosInf;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[3]) = FPTypeInfo<Vs2>::kNegInf;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[4]) = FPTypeInfo<Vs2>::kPosZero;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[5]) = FPTypeInfo<Vs2>::kNegZero;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[6]) = FPTypeInfo<Vs2>::kPosDenorm;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[7]) = FPTypeInfo<Vs2>::kNegDenorm;
+ // Modify the first mask bits to use each of the special floating
+ // point values.
+ vreg_[kVmask]->data_buffer()->Set<uint8_t>(0, 0xff);
+ // Set values for all 8 vector registers in the vector register group.
+ for (int i = 0; i < 8; i++) {
+ auto vs2_name = absl::StrCat("v", kVs2 + i);
+ SetVectorRegisterValues<Vs2>(
+ {{vs2_name, vs2_span.subspan(vs2_size * i, vs2_size)}});
+ }
+ int lmul8 = kLmul8Values[lmul_index];
+ int lmul8_vd = lmul8 * sizeof(Vd) / byte_sew;
+ int lmul8_vs2 = lmul8 * sizeof(Vs2) / byte_sew;
+ int lmul8_vs1 = lmul8 * sizeof(Fs1) / byte_sew;
+ int num_reg_values = lmul8 * kVectorLengthInBytes / (8 * byte_sew);
+ // Configure vector unit for different lmul settings.
+ uint32_t vtype =
+ (kSewSettingsByByteSize[byte_sew] << 3) | kLmulSettings[lmul_index];
+ int vstart = 0;
+ // Try different vstart values (updated at the bottom of the loop).
+ for (int vstart_count = 0; vstart_count < 4; vstart_count++) {
+ int vlen = 1024;
+ // Try different vector lengths (updated at the bottom of the loop).
+ for (int vlen_count = 0; vlen_count < 4; vlen_count++) {
+ ASSERT_TRUE(vlen > vstart);
+ int num_values = std::min(num_reg_values, vlen);
+ ConfigureVectorUnit(vtype, vlen);
+ // Generate a new rs1 value.
+ Fs1 fs1_value = RandomFPValue<Fs1>();
+ // Need to NaN box the value, that is, if the register value type
+ // is wider than the data type for a floating point value, the
+ // upper bits are all set to 1's.
+ typename RVFpRegister::ValueType fs1_reg_value =
+ NaNBox<Fs1, typename RVFpRegister::ValueType>(fs1_value);
+ SetRegisterValues<typename RVFpRegister::ValueType, RVFpRegister>(
+ {{kFs1Name, fs1_reg_value}});
+ // Iterate across rounding modes.
+ for (int rm : {0, 1, 2, 3, 4}) {
+ rv_fp_->SetRoundingMode(static_cast<FPRoundingMode>(rm));
+ rv_vector_->set_vstart(vstart);
+ ClearVectorRegisterGroup(kVd, 8);
+
+ inst->Execute();
+ if (lmul8_vd < 1 || lmul8_vd > 64) {
+ EXPECT_TRUE(rv_vector_->vector_exception())
+ << "lmul8: vd: " << lmul8_vd;
+ rv_vector_->clear_vector_exception();
+ continue;
+ }
+
+ if (lmul8_vs2 < 1 || lmul8_vs2 > 64) {
+ EXPECT_TRUE(rv_vector_->vector_exception())
+ << "lmul8: vs2: " << lmul8_vs2;
+ rv_vector_->clear_vector_exception();
+ continue;
+ }
+
+ if (lmul8_vs1 < 1 || lmul8_vs1 > 64) {
+ EXPECT_TRUE(rv_vector_->vector_exception())
+ << "lmul8: vs1: " << lmul8_vs1;
+ rv_vector_->clear_vector_exception();
+ continue;
+ }
+ EXPECT_FALSE(rv_vector_->vector_exception());
+ EXPECT_EQ(rv_vector_->vstart(), 0);
+ int count = 0;
+ for (int reg = kVd; reg < kVd + 8; reg++) {
+ for (int i = 0; i < kVectorLengthInBytes / sizeof(Vd); i++) {
+ int mask_index = count >> 3;
+ int mask_offset = count & 0b111;
+ bool mask_value = true;
+ // The first 8 bits of the mask are set to true above, so
+ // only read the mask value after the first byte.
+ if (mask_index > 0) {
+ mask_value =
+ ((kA5Mask[mask_index] >> mask_offset) & 0b1) != 0;
+ }
+ auto reg_val = vreg_[reg]->data_buffer()->Get<Vd>(i);
+ auto int_reg_val = *reinterpret_cast<VdInt *>(®_val);
+ if ((count >= vstart) && (count < num_values)) {
+ ScopedFPStatus set_fpstatus(rv_fp_->host_fp_interface());
+ auto op_val =
+ operation(vs2_value[count], fs1_value, mask_value);
+ auto int_op_val = *reinterpret_cast<VdInt *>(&op_val);
+ auto int_vs2_val =
+ *reinterpret_cast<Vs2Int *>(&vs2_value[count]);
+ auto int_fs1_val = *reinterpret_cast<Fs1Int *>(&fs1_value);
+ FPCompare<Vd>(
+ op_val, reg_val, delta_position,
+ absl::StrCat(name, "[", count, "] op(", vs2_value[count],
+ "[0x", absl::Hex(int_vs2_val), "], ",
+ fs1_value, "[0x", absl::Hex(int_fs1_val),
+ "]) = ", absl::Hex(int_op_val), " != reg[",
+ reg, "][", i, "] (", reg_val, " [0x",
+ absl::Hex(int_reg_val), "]) lmul8(", lmul8,
+ ") rm = ", *(rv_fp_->GetRoundingMode())));
+ } else {
+ EXPECT_EQ(0, reg_val) << absl::StrCat(
+ name, " 0 != reg[", reg, "][", i, "] (", reg_val,
+ " [0x", absl::Hex(int_reg_val), "]) lmul8(", lmul8, ")");
+ }
+ count++;
+ }
+ if (HasFailure()) return;
+ }
+ }
+ vlen = absl::Uniform(absl::IntervalOpenClosed, bitgen_, vstart,
+ num_reg_values);
+ }
+ vstart = absl::Uniform(absl::IntervalOpen, bitgen_, 0, num_reg_values);
+ }
+ }
+ }
+
+ // Templated helper function that tests FP vector-scalar instructions that do
+ // not use the value of the mask bit.
+ template <typename Vd, typename Vs2, typename Vs1>
+ void BinaryOpFPTestHelperVX(absl::string_view name, int sew,
+ Instruction *inst, int delta_position,
+ std::function<Vd(Vs2, Vs1)> operation) {
+ BinaryOpFPWithMaskTestHelperVX<Vd, Vs2, Vs1>(
+ name, sew, inst, delta_position,
+ [operation](Vs2 vs2, Vs1 vs1, bool mask_value) -> Vd {
+ if (mask_value) {
+ return operation(vs2, vs1);
+ }
+ return 0;
+ });
+ }
+
+ protected:
+ mpact::sim::riscv::RiscVFPState *rv_fp_;
+};
+
+#endif // MPACT_CHERIOT_TEST_RISCV_CHERIOT_VECTOR_FP_TEST_UTILITIES_H_
diff --git a/cheriot/test/riscv_cheriot_vector_fp_unary_instructions_test.cc b/cheriot/test/riscv_cheriot_vector_fp_unary_instructions_test.cc
new file mode 100644
index 0000000..94315d7
--- /dev/null
+++ b/cheriot/test/riscv_cheriot_vector_fp_unary_instructions_test.cc
@@ -0,0 +1,1005 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cheriot/riscv_cheriot_vector_fp_unary_instructions.h"
+
+#include <algorithm>
+#include <cmath>
+#include <cstdint>
+#include <functional>
+#include <limits>
+#include <string>
+#include <tuple>
+#include <type_traits>
+#include <vector>
+
+#include "absl/random/random.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
+#include "absl/types/span.h"
+#include "cheriot/test/riscv_cheriot_vector_fp_test_utilities.h"
+#include "cheriot/test/riscv_cheriot_vector_instructions_test_base.h"
+#include "googlemock/include/gmock/gmock.h"
+#include "mpact/sim/generic/instruction.h"
+#include "mpact/sim/generic/type_helpers.h"
+#include "riscv//riscv_csr.h"
+#include "riscv//riscv_fp_host.h"
+#include "riscv//riscv_fp_info.h"
+#include "riscv//riscv_fp_state.h"
+#include "riscv//riscv_register.h"
+
+namespace {
+
+using Instruction = ::mpact::sim::generic::Instruction;
+using ::mpact::sim::generic::operator*; // NOLINT: used below.
+using ::mpact::sim::riscv::FPExceptions;
+
+// Functions to test.
+using ::mpact::sim::cheriot::Vfclassv;
+using ::mpact::sim::cheriot::Vfcvtfxuv;
+using ::mpact::sim::cheriot::Vfcvtfxv;
+using ::mpact::sim::cheriot::Vfcvtrtzxfv;
+using ::mpact::sim::cheriot::Vfcvtrtzxufv;
+using ::mpact::sim::cheriot::Vfcvtxfv;
+using ::mpact::sim::cheriot::Vfcvtxufv;
+using ::mpact::sim::cheriot::Vfmvfs;
+using ::mpact::sim::cheriot::Vfmvsf;
+using ::mpact::sim::cheriot::Vfncvtffw;
+using ::mpact::sim::cheriot::Vfncvtfxuw;
+using ::mpact::sim::cheriot::Vfncvtfxw;
+using ::mpact::sim::cheriot::Vfncvtrodffw;
+using ::mpact::sim::cheriot::Vfncvtrtzxfw;
+using ::mpact::sim::cheriot::Vfncvtrtzxufw;
+using ::mpact::sim::cheriot::Vfncvtxfw;
+using ::mpact::sim::cheriot::Vfncvtxufw;
+using ::mpact::sim::cheriot::Vfrec7v;
+using ::mpact::sim::cheriot::Vfrsqrt7v;
+using ::mpact::sim::cheriot::Vfsqrtv;
+using ::mpact::sim::cheriot::Vfwcvtffv;
+using ::mpact::sim::cheriot::Vfwcvtfxuv;
+using ::mpact::sim::cheriot::Vfwcvtfxv;
+using ::mpact::sim::cheriot::Vfwcvtrtzxfv;
+using ::mpact::sim::cheriot::Vfwcvtrtzxufv;
+using ::mpact::sim::cheriot::Vfwcvtxfv;
+using ::mpact::sim::cheriot::Vfwcvtxufv;
+
+using ::absl::Span;
+using ::mpact::sim::riscv::FPRoundingMode;
+using ::mpact::sim::riscv::RiscVCsrInterface;
+using ::mpact::sim::riscv::RiscVFPState;
+using ::mpact::sim::riscv::RVFpRegister;
+using ::mpact::sim::riscv::ScopedFPStatus;
+
+constexpr char kFs1Name[] = "f4";
+constexpr int kFs1 = 4;
+
+// Test fixture that extends the Base fixture for unary floating point
+// instructions.
+class RiscVCheriotFPUnaryInstructionsTest
+ : public RiscVCheriotVectorInstructionsTestBase {
+ public:
+ RiscVCheriotFPUnaryInstructionsTest() {
+ rv_fp_ = new mpact::sim::riscv::RiscVFPState(state_->csr_set(), state_);
+ state_->set_rv_fp(rv_fp_);
+ }
+ ~RiscVCheriotFPUnaryInstructionsTest() override {
+ state_->set_rv_fp(nullptr);
+ delete rv_fp_;
+ }
+
+ // This method uses random values for each field in the fp number.
+ template <typename T>
+ void FillArrayWithRandomFPValues(absl::Span<T> span) {
+ using UInt = typename FPTypeInfo<T>::IntType;
+ for (auto &val : span) {
+ UInt sign = absl::Uniform(absl::IntervalClosed, bitgen_, 0ULL, 1ULL);
+ UInt exp = absl::Uniform(absl::IntervalClosedOpen, bitgen_, 0ULL,
+ 1ULL << FPTypeInfo<T>::kExpSize);
+ UInt sig = absl::Uniform(absl::IntervalClosedOpen, bitgen_, 0ULL,
+ 1ULL << FPTypeInfo<T>::kSigSize);
+ UInt value = (sign & 1) << (FPTypeInfo<T>::kBitSize - 1) |
+ (exp << FPTypeInfo<T>::kSigSize) | sig;
+ val = *reinterpret_cast<T *>(&value);
+ }
+ }
+
+ // Floating point test needs to ensure to use the fp special values (inf, NaN
+ // etc.) during testing, not just random values.
+ template <typename Vd, typename Vs2>
+ void UnaryOpFPTestHelperV(absl::string_view name, int sew, Instruction *inst,
+ int delta_position,
+ std::function<Vd(Vs2)> operation) {
+ int byte_sew = sew / 8;
+ if (byte_sew != sizeof(Vd) && byte_sew != sizeof(Vs2)) {
+ FAIL() << name << ": selected element width != any operand types"
+ << "sew: " << sew << " Vd: " << sizeof(Vd)
+ << " Vs2: " << sizeof(Vs2);
+ return;
+ }
+ // Number of elements per vector register.
+ constexpr int vs2_size = kVectorLengthInBytes / sizeof(Vs2);
+ // Input values for 8 registers.
+ Vs2 vs2_value[vs2_size * 8];
+ auto vs2_span = Span<Vs2>(vs2_value);
+ AppendVectorRegisterOperands({kVs2, kVmask}, {kVd});
+ SetVectorRegisterValues<uint8_t>(
+ {{kVmaskName, Span<const uint8_t>(kA5Mask)}});
+ // Iterate across different lmul values.
+ for (int lmul_index = 0; lmul_index < 7; lmul_index++) {
+ // Initialize input values.
+ FillArrayWithRandomFPValues<Vs2>(vs2_span);
+ using Vs2Int = typename FPTypeInfo<Vs2>::IntType;
+ // Overwrite the first few values of the input data with infinities,
+ // zeros, denormals and NaNs.
+ *reinterpret_cast<Vs2Int *>(&vs2_span[0]) = FPTypeInfo<Vs2>::kQNaN;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[1]) = FPTypeInfo<Vs2>::kSNaN;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[2]) = FPTypeInfo<Vs2>::kPosInf;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[3]) = FPTypeInfo<Vs2>::kNegInf;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[4]) = FPTypeInfo<Vs2>::kPosZero;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[5]) = FPTypeInfo<Vs2>::kNegZero;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[6]) = FPTypeInfo<Vs2>::kPosDenorm;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[7]) = FPTypeInfo<Vs2>::kNegDenorm;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[8]) = 0x0119515e;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[9]) = 0x0007fea3;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[10]) = 0x800bc58f;
+ // Modify the first mask bits to use each of the special floating point
+ // values.
+ vreg_[kVmask]->data_buffer()->Set<uint8_t>(0, 0xff);
+ vreg_[kVmask]->data_buffer()->Set<uint8_t>(1, 0xa5 | 0x3);
+ // Set values for all 8 vector registers in the vector register group.
+ for (int i = 0; i < 8; i++) {
+ auto vs2_name = absl::StrCat("v", kVs2 + i);
+ SetVectorRegisterValues<Vs2>(
+ {{vs2_name, vs2_span.subspan(vs2_size * i, vs2_size)}});
+ }
+ int lmul8 = kLmul8Values[lmul_index];
+ int lmul8_vd = lmul8 * sizeof(Vd) / byte_sew;
+ int lmul8_vs2 = lmul8 * sizeof(Vs2) / byte_sew;
+ int num_reg_values = lmul8 * kVectorLengthInBytes / (8 * byte_sew);
+ // Configure vector unit for different lmul settings.
+ uint32_t vtype =
+ (kSewSettingsByByteSize[byte_sew] << 3) | kLmulSettings[lmul_index];
+ int vstart = 0;
+ // Try different vstart values (updated at the bottom of the loop).
+ for (int vstart_count = 0; vstart_count < 4; vstart_count++) {
+ int vlen = 1024;
+ // Try different vector lengths (updated at the bottom of the loop).
+ for (int vlen_count = 0; vlen_count < 4; vlen_count++) {
+ ASSERT_TRUE(vlen > vstart);
+ int num_values = std::min(num_reg_values, vlen);
+ ConfigureVectorUnit(vtype, vlen);
+ // Iterate across rounding modes.
+ for (int rm : {0, 1, 2, 3, 4}) {
+ rv_fp_->SetRoundingMode(static_cast<FPRoundingMode>(rm));
+ rv_vector_->set_vstart(vstart);
+ ClearVectorRegisterGroup(kVd, 8);
+
+ inst->Execute();
+ if (lmul8_vd < 1 || lmul8_vd > 64) {
+ EXPECT_TRUE(rv_vector_->vector_exception())
+ << "lmul8: vd: " << lmul8_vd;
+ rv_vector_->clear_vector_exception();
+ continue;
+ }
+
+ if (lmul8_vs2 < 1 || lmul8_vs2 > 64) {
+ EXPECT_TRUE(rv_vector_->vector_exception())
+ << "lmul8: vs2: " << lmul8_vs2;
+ rv_vector_->clear_vector_exception();
+ continue;
+ }
+
+ EXPECT_FALSE(rv_vector_->vector_exception());
+ EXPECT_EQ(rv_vector_->vstart(), 0);
+ int count = 0;
+ for (int reg = kVd; reg < kVd + 8; reg++) {
+ for (int i = 0; i < kVectorLengthInBytes / sizeof(Vd); i++) {
+ int mask_index = count >> 3;
+ int mask_offset = count & 0b111;
+ bool mask_value = true;
+ // The first 10 bits of the mask are set to true above, so only
+ // read the mask value for the entries after that.
+ if (count >= 10) {
+ mask_value =
+ ((kA5Mask[mask_index] >> mask_offset) & 0b1) != 0;
+ }
+ auto reg_val = vreg_[reg]->data_buffer()->Get<Vd>(i);
+ if ((count >= vstart) && mask_value && (count < num_values)) {
+ auto op_val = operation(vs2_value[count]);
+ // Do separate comparison if the result is a NaN.
+ auto int_reg_val =
+ *reinterpret_cast<typename FPTypeInfo<Vd>::IntType *>(
+ ®_val);
+ auto int_op_val =
+ *reinterpret_cast<typename FPTypeInfo<Vd>::IntType *>(
+ &op_val);
+ auto int_vs2_val =
+ *reinterpret_cast<typename FPTypeInfo<Vs2>::IntType *>(
+ &vs2_value[count]);
+ FPCompare<Vd>(
+ op_val, reg_val, delta_position,
+ absl::StrCat(name, "[", count, "] op(", vs2_value[count],
+ "[0x", absl::Hex(int_vs2_val),
+ "]) = ", absl::Hex(int_op_val), " != reg[",
+ reg, "][", i, "] (", reg_val, " [0x",
+ absl::Hex(int_reg_val), "]) lmul8(", lmul8,
+ ") rm = ", *(rv_fp_->GetRoundingMode())));
+ } else {
+ EXPECT_EQ(0, reg_val) << absl::StrCat(
+ name, " 0 != reg[", reg, "][", i, "] (", reg_val,
+ " [0x", absl::Hex(reg_val), "]) lmul8(", lmul8, ")");
+ }
+ count++;
+ }
+ }
+ }
+ if (HasFailure()) {
+ return;
+ }
+ vlen = absl::Uniform(absl::IntervalOpenClosed, bitgen_, vstart,
+ num_reg_values);
+ }
+ vstart = absl::Uniform(absl::IntervalOpen, bitgen_, 0, num_reg_values);
+ }
+ }
+ }
+
+ // Floating point test needs to ensure to use the fp special values (inf, NaN
+ // etc.) during testing, not just random values.
+ template <typename Vd, typename Vs2>
+ void UnaryOpWithFflagsFPTestHelperV(
+ absl::string_view name, int sew, Instruction *inst, int delta_position,
+ std::function<std::tuple<Vd, uint32_t>(Vs2)> operation) {
+ using VdInt = typename FPTypeInfo<Vd>::IntType;
+ using Vs2Int = typename FPTypeInfo<Vs2>::IntType;
+ int byte_sew = sew / 8;
+ if (byte_sew != sizeof(Vd) && byte_sew != sizeof(Vs2)) {
+ FAIL() << name << ": selected element width != any operand types"
+ << "sew: " << sew << " Vd: " << sizeof(Vd)
+ << " Vs2: " << sizeof(Vs2);
+ return;
+ }
+ // Number of elements per vector register.
+ constexpr int vs2_size = kVectorLengthInBytes / sizeof(Vs2);
+ // Input values for 8 registers.
+ Vs2 vs2_value[vs2_size * 8];
+ auto vs2_span = Span<Vs2>(vs2_value);
+ AppendVectorRegisterOperands({kVs2, kVmask}, {kVd});
+ auto *op = rv_fp_->fflags()->CreateSetDestinationOperand(0, "fflags");
+ instruction_->AppendDestination(op);
+ SetVectorRegisterValues<uint8_t>(
+ {{kVmaskName, Span<const uint8_t>(kA5Mask)}});
+ // Iterate across different lmul values.
+ for (int lmul_index = 0; lmul_index < 7; lmul_index++) {
+ // Initialize input values.
+ FillArrayWithRandomFPValues<Vs2>(vs2_span);
+ // Overwrite the first few values of the input data with infinities,
+ // zeros, denormals and NaNs.
+ *reinterpret_cast<Vs2Int *>(&vs2_span[0]) = FPTypeInfo<Vs2>::kQNaN;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[1]) = FPTypeInfo<Vs2>::kSNaN;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[2]) = FPTypeInfo<Vs2>::kPosInf;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[3]) = FPTypeInfo<Vs2>::kNegInf;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[4]) = FPTypeInfo<Vs2>::kPosZero;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[5]) = FPTypeInfo<Vs2>::kNegZero;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[6]) = FPTypeInfo<Vs2>::kPosDenorm;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[7]) = FPTypeInfo<Vs2>::kNegDenorm;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[8]) = 0x0119515e;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[9]) = 0x0007fea3;
+ *reinterpret_cast<Vs2Int *>(&vs2_span[10]) = 0x800bc58f;
+ // Modify the first mask bits to use each of the special floating point
+ // values.
+ vreg_[kVmask]->data_buffer()->Set<uint8_t>(0, 0xff);
+ vreg_[kVmask]->data_buffer()->Set<uint8_t>(1, 0xa5 | 0x3);
+ // Set values for all 8 vector registers in the vector register group.
+ for (int i = 0; i < 8; i++) {
+ auto vs2_name = absl::StrCat("v", kVs2 + i);
+ SetVectorRegisterValues<Vs2>(
+ {{vs2_name, vs2_span.subspan(vs2_size * i, vs2_size)}});
+ }
+ int lmul8 = kLmul8Values[lmul_index];
+ int lmul8_vd = lmul8 * sizeof(Vd) / byte_sew;
+ int lmul8_vs2 = lmul8 * sizeof(Vs2) / byte_sew;
+ int num_reg_values = lmul8 * kVectorLengthInBytes / (8 * byte_sew);
+ // Configure vector unit for different lmul settings.
+ uint32_t vtype =
+ (kSewSettingsByByteSize[byte_sew] << 3) | kLmulSettings[lmul_index];
+ int vstart = 0;
+ // Try different vstart values (updated at the bottom of the loop).
+ for (int vstart_count = 0; vstart_count < 4; vstart_count++) {
+ int vlen = 1024;
+ // Try different vector lengths (updated at the bottom of the loop).
+ for (int vlen_count = 0; vlen_count < 4; vlen_count++) {
+ ASSERT_TRUE(vlen > vstart);
+ int num_values = std::min(num_reg_values, vlen);
+ ConfigureVectorUnit(vtype, vlen);
+ // Iterate across rounding modes.
+ for (int rm : {0, 1, 2, 3, 4}) {
+ rv_vector_->set_vstart(vstart);
+ ClearVectorRegisterGroup(kVd, 8);
+ // Set rounding mode and clear flags.
+ rv_fp_->SetRoundingMode(static_cast<FPRoundingMode>(rm));
+ rv_fp_->fflags()->Write(0U);
+
+ inst->Execute();
+
+ // Get the flags for the instruction execution.
+ uint32_t fflags = rv_fp_->fflags()->AsUint32();
+
+ if (lmul8_vd < 1 || lmul8_vd > 64) {
+ EXPECT_TRUE(rv_vector_->vector_exception())
+ << "lmul8: vd: " << lmul8_vd;
+ rv_vector_->clear_vector_exception();
+ continue;
+ }
+
+ if (lmul8_vs2 < 1 || lmul8_vs2 > 64) {
+ EXPECT_TRUE(rv_vector_->vector_exception())
+ << "lmul8: vs2: " << lmul8_vs2;
+ rv_vector_->clear_vector_exception();
+ continue;
+ }
+
+ EXPECT_FALSE(rv_vector_->vector_exception());
+ EXPECT_EQ(rv_vector_->vstart(), 0);
+ int count = 0;
+ // Clear flags for the test execution.
+ rv_fp_->fflags()->Write(0U);
+ uint32_t fflags_test = 0;
+ for (int reg = kVd; reg < kVd + 8; reg++) {
+ for (int i = 0; i < kVectorLengthInBytes / sizeof(Vd); i++) {
+ int mask_index = count >> 3;
+ int mask_offset = count & 0b111;
+ bool mask_value = true;
+ // The first 10 bits of the mask are set to true above, so only
+ // read the mask value for the entries after that.
+ if (count >= 10) {
+ mask_value =
+ ((kA5Mask[mask_index] >> mask_offset) & 0b1) != 0;
+ }
+ auto reg_val = vreg_[reg]->data_buffer()->Get<Vd>(i);
+ if ((count >= vstart) && mask_value && (count < num_values)) {
+ Vd op_val;
+ uint32_t flag;
+ {
+ ScopedFPStatus set_fp_status(rv_fp_->host_fp_interface());
+ auto [op_val_tmp, flag_tmp] = operation(vs2_value[count]);
+ op_val = op_val_tmp;
+ flag = flag_tmp;
+ }
+ fflags_test |= (rv_fp_->fflags()->AsUint32() | flag);
+ // Do separate comparison if the result is a NaN.
+ auto int_reg_val = *reinterpret_cast<VdInt *>(®_val);
+ auto int_op_val = *reinterpret_cast<VdInt *>(&op_val);
+ auto int_vs2_val =
+ *reinterpret_cast<Vs2Int *>(&vs2_value[count]);
+ FPCompare<Vd>(
+ op_val, reg_val, delta_position,
+ absl::StrCat(name, "[", count, "] op(", vs2_value[count],
+ "[0x", absl::Hex(int_vs2_val),
+ "]) = ", absl::Hex(int_op_val), " != reg[",
+ reg, "][", i, "] (", reg_val, " [0x",
+ absl::Hex(int_reg_val), "]) lmul8(", lmul8,
+ ") rm = ", *(rv_fp_->GetRoundingMode())));
+ } else {
+ EXPECT_EQ(0, reg_val) << absl::StrCat(
+ name, " 0 != reg[", reg, "][", i, "] (", reg_val,
+ " [0x", absl::Hex(reg_val), "]) lmul8(", lmul8, ")");
+ }
+ count++;
+ }
+ }
+ EXPECT_EQ(fflags, fflags_test) << name;
+ }
+ if (HasFailure()) {
+ return;
+ }
+ vlen = absl::Uniform(absl::IntervalOpenClosed, bitgen_, vstart,
+ num_reg_values);
+ }
+ vstart = absl::Uniform(absl::IntervalOpen, bitgen_, 0, num_reg_values);
+ }
+ }
+ }
+
+ protected:
+ mpact::sim::riscv::RiscVFPState *rv_fp_ = nullptr;
+ RiscVCsrInterface *fflags_ = nullptr;
+};
+
+// Templated helper function for classifying fp numbers.
+template <typename T>
+typename FPTypeInfo<T>::IntType VfclassVHelper(T val) {
+ auto fp_class = std::fpclassify(val);
+ switch (fp_class) {
+ case FP_INFINITE:
+ return std::signbit(val) ? 1 : 1 << 7;
+ case FP_NAN: {
+ auto uint_val =
+ *reinterpret_cast<typename FPTypeInfo<T>::IntType *>(&val);
+ bool quiet_nan = (uint_val >> (FPTypeInfo<T>::kSigSize - 1)) & 1;
+ return quiet_nan ? 1 << 9 : 1 << 8;
+ }
+ case FP_ZERO:
+ return std::signbit(val) ? 1 << 3 : 1 << 4;
+ case FP_SUBNORMAL:
+ return std::signbit(val) ? 1 << 2 : 1 << 5;
+ case FP_NORMAL:
+ return std::signbit(val) ? 1 << 1 : 1 << 6;
+ }
+ return 0;
+}
+
+// Test fp classify.
+TEST_F(RiscVCheriotFPUnaryInstructionsTest, Vfclassv) {
+ SetSemanticFunction(&Vfclassv);
+ UnaryOpFPTestHelperV<uint32_t, float>(
+ "Vfclassv_32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [](float vs2) -> uint32_t { return VfclassVHelper(vs2); });
+ ResetInstruction();
+ SetSemanticFunction(&Vfclassv);
+ UnaryOpFPTestHelperV<uint64_t, double>(
+ "Vfclassv_64", /*sew*/ 64, instruction_, /*delta_position*/ 64,
+ [](double vs2) -> uint64_t { return VfclassVHelper(vs2); });
+}
+
+// Test convert from unsigned integer to fp.
+TEST_F(RiscVCheriotFPUnaryInstructionsTest, Vfcvtfxuv) {
+ SetSemanticFunction(&Vfcvtfxuv);
+ UnaryOpTestHelperV<float, uint32_t>(
+ "Vfcvt.f.xu.v_32", /*sew*/ 32, instruction_,
+ [](uint32_t vs2) -> float { return static_cast<float>(vs2); });
+ ResetInstruction();
+ SetSemanticFunction(&Vfcvtfxuv);
+ UnaryOpTestHelperV<double, uint64_t>(
+ "Vfcvt.f.xu.v_64", /*sew*/ 64, instruction_,
+ [](uint64_t vs2) -> double { return static_cast<double>(vs2); });
+}
+
+// Test convert from signed integer to fp.
+TEST_F(RiscVCheriotFPUnaryInstructionsTest, Vfcvtfxv) {
+ SetSemanticFunction(&Vfcvtfxv);
+ UnaryOpTestHelperV<float, int32_t>(
+ "Vfcvt.f.x.v_32", /*sew*/ 32, instruction_,
+ [](int32_t vs2) -> float { return static_cast<float>(vs2); });
+ ResetInstruction();
+ SetSemanticFunction(&Vfcvtfxv);
+ UnaryOpTestHelperV<double, int64_t>(
+ "Vfcvt.f.x.v_64", /*sew*/ 64, instruction_,
+ [](int64_t vs2) -> double { return static_cast<double>(vs2); });
+}
+
+// Helper function for fp to integer conversions.
+template <typename F, typename I>
+std::tuple<I, uint32_t> ConvertHelper(F value, RiscVFPState *fp_state) {
+ constexpr F kMin = static_cast<F>(std::numeric_limits<I>::min());
+ constexpr F kMax = static_cast<F>(std::numeric_limits<I>::max());
+ ScopedFPStatus status(fp_state->host_fp_interface());
+ auto fp_class = std::fpclassify(value);
+ switch (fp_class) {
+ case FP_INFINITE:
+ return std::make_tuple(std::signbit(value)
+ ? std::numeric_limits<I>::min()
+ : std::numeric_limits<I>::max(),
+ static_cast<uint32_t>(FPExceptions::kInvalidOp));
+ case FP_NAN:
+ return std::make_tuple(std::numeric_limits<I>::max(),
+ static_cast<uint32_t>(FPExceptions::kInvalidOp));
+ case FP_ZERO:
+ return std::make_tuple(0, 0);
+ case FP_SUBNORMAL:
+ case FP_NORMAL:
+ if (value > kMax) {
+ return std::make_tuple(std::numeric_limits<I>::max(),
+ static_cast<uint32_t>(FPExceptions::kInvalidOp));
+ }
+ if (value < kMin) {
+ if (std::is_unsigned<I>::value) {
+ if ((value > -1.0) &&
+ (static_cast<typename std::make_signed<I>::type>(value) == 0)) {
+ return std::make_tuple(
+ 0, static_cast<uint32_t>(FPExceptions::kInexact));
+ }
+ }
+ if (value < kMin) {
+ return std::make_tuple(
+ std::numeric_limits<I>::min(),
+ static_cast<uint32_t>(FPExceptions::kInvalidOp));
+ }
+ }
+ }
+ return std::make_tuple(static_cast<I>(value), 0);
+}
+
+// Test convert from fp to signed integer with truncation.
+TEST_F(RiscVCheriotFPUnaryInstructionsTest, Vfcvtrtzxfv) {
+ SetSemanticFunction(&Vfcvtrtzxfv);
+ rv_fp_->SetRoundingMode(FPRoundingMode::kRoundTowardsZero);
+ UnaryOpWithFflagsFPTestHelperV<int32_t, float>(
+ "Vfcvt.rtz.x.f.v_32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [this](float vs2) -> std::tuple<int32_t, uint32_t> {
+ return ConvertHelper<float, int32_t>(vs2, this->rv_fp_);
+ });
+ ResetInstruction();
+ SetSemanticFunction(&Vfcvtrtzxfv);
+ rv_fp_->SetRoundingMode(FPRoundingMode::kRoundTowardsZero);
+ UnaryOpWithFflagsFPTestHelperV<int64_t, double>(
+ "Vfcvt.rtz.x.f.v_64", /*sew*/ 64, instruction_, /*delta_position*/ 64,
+ [this](double vs2) -> std::tuple<int64_t, uint32_t> {
+ return ConvertHelper<double, int64_t>(vs2, this->rv_fp_);
+ });
+}
+
+// Test convert from fp to unsigned integer with truncation.
+TEST_F(RiscVCheriotFPUnaryInstructionsTest, Vfcvtrtzxufv) {
+ SetSemanticFunction(&Vfcvtrtzxufv);
+ rv_fp_->SetRoundingMode(FPRoundingMode::kRoundTowardsZero);
+ UnaryOpWithFflagsFPTestHelperV<uint32_t, float>(
+ "Vfcvt.rtz.xu.f.v_32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [this](float vs2) -> std::tuple<uint32_t, uint32_t> {
+ return ConvertHelper<float, uint32_t>(vs2, this->rv_fp_);
+ });
+ ResetInstruction();
+ SetSemanticFunction(&Vfcvtrtzxufv);
+ rv_fp_->SetRoundingMode(FPRoundingMode::kRoundTowardsZero);
+ UnaryOpWithFflagsFPTestHelperV<uint64_t, double>(
+ "Vfcvt.rtz.xu.f.v_64", /*sew*/ 64, instruction_, /*delta_position*/ 64,
+ [this](double vs2) -> std::tuple<uint64_t, uint32_t> {
+ return ConvertHelper<double, uint64_t>(vs2, this->rv_fp_);
+ });
+}
+
+// Test convert from fp to signed integer with rounding.
+TEST_F(RiscVCheriotFPUnaryInstructionsTest, Vfcvtxfv) {
+ SetSemanticFunction(&Vfcvtxfv);
+ UnaryOpWithFflagsFPTestHelperV<int32_t, float>(
+ "Vfcvt.x.f.v_32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [this](float vs2) -> std::tuple<int32_t, uint32_t> {
+ return ConvertHelper<float, int32_t>(vs2, this->rv_fp_);
+ });
+ ResetInstruction();
+ SetSemanticFunction(&Vfcvtxfv);
+ UnaryOpWithFflagsFPTestHelperV<int64_t, double>(
+ "Vfcvt.x.f.v_64", /*sew*/ 64, instruction_, /*delta_position*/ 64,
+ [this](double vs2) -> std::tuple<int64_t, uint32_t> {
+ return ConvertHelper<double, int64_t>(vs2, this->rv_fp_);
+ });
+}
+
+// Test convert from fp to unsigned integer with rounding.
+TEST_F(RiscVCheriotFPUnaryInstructionsTest, Vfcvtxufv) {
+ SetSemanticFunction(&Vfcvtxufv);
+ UnaryOpWithFflagsFPTestHelperV<uint32_t, float>(
+ "Vfcvt.xu.f.v_32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [this](float vs2) -> std::tuple<uint32_t, uint32_t> {
+ return ConvertHelper<float, uint32_t>(vs2, this->rv_fp_);
+ });
+ ResetInstruction();
+ SetSemanticFunction(&Vfcvtxufv);
+ UnaryOpWithFflagsFPTestHelperV<uint64_t, double>(
+ "Vfcvt.xu.f.v_64", /*sew*/ 64, instruction_, /*delta_position*/ 64,
+ [this](double vs2) -> std::tuple<uint64_t, uint32_t> {
+ return ConvertHelper<double, uint64_t>(vs2, this->rv_fp_);
+ });
+}
+
+// Test vfmv.f.s instruction - move element 0 to scalar fp register.
+TEST_F(RiscVCheriotFPUnaryInstructionsTest, VfmvToScalar) {
+ SetSemanticFunction(&Vfmvfs);
+ AppendRegisterOperands({}, {kFs1Name});
+ AppendVectorRegisterOperands({kVs2}, {});
+ for (int byte_sew : {1, 2, 4, 8}) {
+ int vlen = kVectorLengthInBytes / byte_sew;
+ uint32_t vtype =
+ (kSewSettingsByByteSize[byte_sew] << 3) | kLmulSettingByLogSize[4];
+ ConfigureVectorUnit(vtype, vlen);
+ if (byte_sew < 4) {
+ instruction_->Execute();
+ EXPECT_TRUE(rv_vector_->vector_exception());
+ continue;
+ }
+ // Test 10 different values.
+ for (int i = 0; i < 10; i++) {
+ uint64_t value;
+ switch (byte_sew) {
+ case 4: {
+ auto val32 = RandomValue<uint32_t>();
+ value = 0xffff'ffff'0000'0000ULL | static_cast<uint64_t>(val32);
+ SetVectorRegisterValues<uint32_t>(
+ {{kVs2Name, absl::Span<const uint32_t>(&val32, 1)}});
+ break;
+ }
+ case 8: {
+ value = RandomValue<uint64_t>();
+ SetVectorRegisterValues<uint64_t>(
+ {{kVs2Name, absl::Span<const uint64_t>(&value, 1)}});
+ break;
+ }
+ }
+ instruction_->Execute();
+ EXPECT_EQ(freg_[kFs1]->data_buffer()->Get<RVFpRegister::ValueType>(0),
+ static_cast<RVFpRegister::ValueType>(value));
+ }
+ }
+}
+
+// Test vfmv.f.s instruction - move scalar fp register to element 0.
+TEST_F(RiscVCheriotFPUnaryInstructionsTest, VfmvFromScalar) {
+ SetSemanticFunction(&Vfmvsf);
+ AppendRegisterOperands({kFs1Name}, {});
+ AppendVectorRegisterOperands({}, {kVd});
+ for (int byte_sew : {1, 2, 4, 8}) {
+ int vlen = kVectorLengthInBytes / byte_sew;
+ uint32_t vtype =
+ (kSewSettingsByByteSize[byte_sew] << 3) | kLmulSettingByLogSize[4];
+ ConfigureVectorUnit(vtype, vlen);
+ if (byte_sew < 4) {
+ instruction_->Execute();
+ EXPECT_TRUE(rv_vector_->vector_exception());
+ continue;
+ }
+ // Test 10 different values.
+ for (int i = 0; i < 10; i++) {
+ auto value = RandomValue<RVFpRegister::ValueType>();
+ freg_[kFs1]->data_buffer()->Set<RVFpRegister::ValueType>(0, value);
+ instruction_->Execute();
+ switch (byte_sew) {
+ case 4:
+ EXPECT_EQ(vreg_[kVd]->data_buffer()->Get<uint32_t>(0),
+ static_cast<uint32_t>(value));
+ break;
+ case 8:
+ EXPECT_EQ(vreg_[kVd]->data_buffer()->Get<uint64_t>(0),
+ static_cast<uint64_t>(value));
+ break;
+ }
+ }
+ }
+}
+
+// Test narrowing convert from unsigned integer to fp.
+TEST_F(RiscVCheriotFPUnaryInstructionsTest, Vfncvtfxuw) {
+ SetSemanticFunction(&Vfncvtfxuw);
+ UnaryOpTestHelperV<float, uint64_t>(
+ "Vfncvt.f.xu.w_64", /*sew*/ 64, instruction_,
+ [](uint64_t vs2) -> float { return static_cast<float>(vs2); });
+}
+
+// Test narrowing convert from signed integer to fp.
+TEST_F(RiscVCheriotFPUnaryInstructionsTest, Vfncvtfxw) {
+ SetSemanticFunction(&Vfncvtfxw);
+ UnaryOpTestHelperV<float, int64_t>(
+ "Vfncvt.f.x.w_64", /*sew*/ 64, instruction_,
+ [](int64_t vs2) -> float { return static_cast<float>(vs2); });
+}
+
+// Test narrowing convert fp to fp.
+TEST_F(RiscVCheriotFPUnaryInstructionsTest, Vfncvtffw) {
+ SetSemanticFunction(&Vfncvtffw);
+ UnaryOpTestHelperV<float, double>(
+ "Vfncvt.f.f.w_64", /*sew*/ 64, instruction_,
+ [](double vs2) -> float { return static_cast<float>(vs2); });
+}
+
+// Test narrowing convert fp to fp with round to odd.
+TEST_F(RiscVCheriotFPUnaryInstructionsTest, Vfncvtrodffw) {
+ SetSemanticFunction(&Vfncvtrodffw);
+ UnaryOpTestHelperV<float, double>(
+ "Vfncvt.rod.f.f.w_64", /*sew*/ 64, instruction_, [](double vs2) -> float {
+ if (std::isnan(vs2) || std::isinf(vs2)) {
+ return static_cast<float>(vs2);
+ }
+ using UIntD = typename FPTypeInfo<double>::IntType;
+ using UIntF = typename FPTypeInfo<float>::IntType;
+ UIntD uval = *reinterpret_cast<UIntD *>(&vs2);
+ int diff = FPTypeInfo<double>::kSigSize - FPTypeInfo<float>::kSigSize;
+ UIntF bit = (uval & (FPTypeInfo<double>::kSigMask >> diff)) != 0;
+ float res = static_cast<float>(vs2);
+ // The narrowing conversion may have generated an infinity, so check
+ // for infinity before doing rounding.
+ if (std::isinf(res)) return res;
+ UIntF ures = *reinterpret_cast<UIntF *>(&res) | bit;
+ return *reinterpret_cast<float *>(&ures);
+ });
+}
+
+// Test narrowing convert from fp to signed integer with truncation.
+TEST_F(RiscVCheriotFPUnaryInstructionsTest, Vfncvtrtzxfw) {
+ SetSemanticFunction(&Vfncvtrtzxfw);
+ rv_fp_->SetRoundingMode(FPRoundingMode::kRoundTowardsZero);
+ UnaryOpWithFflagsFPTestHelperV<int16_t, float>(
+ "Vfncvt.rtz.x.f.w_32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [this](float vs2) -> std::tuple<int16_t, uint32_t> {
+ return ConvertHelper<float, int16_t>(vs2, this->rv_fp_);
+ });
+ ResetInstruction();
+ SetSemanticFunction(&Vfncvtrtzxfw);
+ rv_fp_->SetRoundingMode(FPRoundingMode::kRoundTowardsZero);
+ UnaryOpWithFflagsFPTestHelperV<int32_t, double>(
+ "Vfncvt.rtz.x.f.w_64", /*sew*/ 64, instruction_, /*delta_position*/ 64,
+ [this](double vs2) -> std::tuple<int32_t, uint32_t> {
+ return ConvertHelper<double, int32_t>(vs2, this->rv_fp_);
+ });
+}
+
+// Test narrowing convert from fp to unsigned integer with truncation.
+TEST_F(RiscVCheriotFPUnaryInstructionsTest, Vfncvtrtzxufw) {
+ SetSemanticFunction(&Vfncvtrtzxufw);
+ rv_fp_->SetRoundingMode(FPRoundingMode::kRoundTowardsZero);
+ UnaryOpWithFflagsFPTestHelperV<uint16_t, float>(
+ "Vfncvt.rtz.xu.f.w_32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [this](float vs2) -> std::tuple<uint16_t, uint32_t> {
+ return ConvertHelper<float, uint16_t>(vs2, this->rv_fp_);
+ });
+ ResetInstruction();
+ SetSemanticFunction(&Vfncvtrtzxufw);
+ rv_fp_->SetRoundingMode(FPRoundingMode::kRoundTowardsZero);
+ UnaryOpWithFflagsFPTestHelperV<uint32_t, double>(
+ "Vfncvt.rtz.xu.f.w_64", /*sew*/ 64, instruction_, /*delta_position*/ 64,
+ [this](double vs2) -> std::tuple<uint32_t, uint32_t> {
+ return ConvertHelper<double, uint32_t>(vs2, this->rv_fp_);
+ });
+}
+
+// Test narrowing convert fp to signed integer with rounding.
+TEST_F(RiscVCheriotFPUnaryInstructionsTest, Vfncvtxfw) {
+ SetSemanticFunction(&Vfncvtxfw);
+ UnaryOpWithFflagsFPTestHelperV<int16_t, float>(
+ "Vfncvt.x.f.w_32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [this](float vs2) -> std::tuple<int16_t, uint32_t> {
+ return ConvertHelper<float, int16_t>(vs2, this->rv_fp_);
+ });
+ ResetInstruction();
+ SetSemanticFunction(&Vfncvtxfw);
+ UnaryOpWithFflagsFPTestHelperV<int32_t, double>(
+ "Vfncvt.x.f.w_64", /*sew*/ 64, instruction_, /*delta_position*/ 32,
+ [this](double vs2) -> std::tuple<int32_t, uint32_t> {
+ return ConvertHelper<double, int32_t>(vs2, this->rv_fp_);
+ });
+}
+
+// Test narrowing convert fp to unsigned integer with rounding.
+TEST_F(RiscVCheriotFPUnaryInstructionsTest, Vfncvtxufw) {
+ SetSemanticFunction(&Vfncvtxufw);
+ UnaryOpWithFflagsFPTestHelperV<uint16_t, float>(
+ "Vfncvt.xu.f.w_32", /*sew*/ 32, instruction_, /*delta_position*/ 32,
+ [this](float vs2) -> std::tuple<uint16_t, uint32_t> {
+ return ConvertHelper<float, uint16_t>(vs2, this->rv_fp_);
+ });
+ ResetInstruction();
+ SetSemanticFunction(&Vfncvtxufw);
+ UnaryOpWithFflagsFPTestHelperV<uint32_t, double>(
+ "Vfncvt.xu.f.w_64", /*sew*/ 64, instruction_, /*delta_position*/ 64,
+ [this](double vs2) -> std::tuple<uint32_t, uint32_t> {
+ return ConvertHelper<double, uint32_t>(vs2, this->rv_fp_);
+ });
+}
+
+// Helper function for testing approximate reciprocal instruction.
+template <typename T>
+inline T Vrecip7vTestHelper(T vs2, RiscVFPState *rv_fp) {
+ using UInt = typename FPTypeInfo<T>::IntType;
+ if (FPTypeInfo<T>::IsNaN(vs2)) {
+ auto nan_value = FPTypeInfo<T>::kCanonicalNaN;
+ return *reinterpret_cast<T *>(&nan_value);
+ }
+ if (std::isinf(vs2)) {
+ return std::signbit(vs2) ? -0.0 : 0.0;
+ }
+ if (vs2 == 0.0) {
+ UInt value =
+ std::signbit(vs2) ? FPTypeInfo<T>::kNegInf : FPTypeInfo<T>::kPosInf;
+ return *reinterpret_cast<T *>(&value);
+ }
+ UInt uint_vs2 = *reinterpret_cast<UInt *>(&vs2);
+ auto exp = (uint_vs2 & FPTypeInfo<T>::kExpMask) >> FPTypeInfo<T>::kSigSize;
+ auto sig2 =
+ (uint_vs2 & FPTypeInfo<T>::kSigMask) >> (FPTypeInfo<T>::kSigSize - 2);
+ auto rm = rv_fp->GetRoundingMode();
+ if ((exp == 0) && (sig2 == 0)) { // Denormal number.
+ if (std::signbit(vs2)) {
+ if ((rm == FPRoundingMode::kRoundTowardsZero) ||
+ (rm == FPRoundingMode::kRoundUp)) {
+ return std::numeric_limits<T>::lowest();
+ } else {
+ UInt value = FPTypeInfo<T>::kNegInf;
+ return *reinterpret_cast<T *>(&value);
+ }
+ } else {
+ if ((rm == FPRoundingMode::kRoundTowardsZero) ||
+ (rm == FPRoundingMode::kRoundDown)) {
+ return std::numeric_limits<T>::max();
+ } else {
+ UInt value = FPTypeInfo<T>::kPosInf;
+ return *reinterpret_cast<T *>(&value);
+ }
+ }
+ }
+ ScopedFPStatus status(rv_fp->host_fp_interface(),
+ FPRoundingMode::kRoundTowardsZero);
+ T value = 1.0 / vs2;
+ UInt uint_val = *reinterpret_cast<UInt *>(&value);
+ UInt mask = FPTypeInfo<T>::kSigMask >> 7;
+ uint_val = uint_val & ~mask;
+ return *reinterpret_cast<T *>(&uint_val);
+}
+
+// Test approximate reciprocal instruction.
+TEST_F(RiscVCheriotFPUnaryInstructionsTest, Vfrec7v) {
+ SetSemanticFunction(&Vfrec7v);
+ UnaryOpFPTestHelperV<float, float>(
+ "Vfrec7.v_32", /*sew*/ 32, instruction_, /*delta_position*/ 7,
+ [this](float vs2) -> float {
+ return Vrecip7vTestHelper(vs2, this->rv_fp_);
+ });
+}
+
+// Helper function for testing approximate reciprocal square root instruction.
+template <typename T>
+inline std::tuple<T, uint32_t> Vfrsqrt7vTestHelper(T vs2, RiscVFPState *rv_fp) {
+ using UInt = typename FPTypeInfo<T>::IntType;
+ T return_value;
+ uint32_t fflags = 0;
+ if (FPTypeInfo<T>::IsNaN(vs2) || (vs2 < 0.0)) {
+ auto nan_value = FPTypeInfo<T>::kCanonicalNaN;
+ return_value = *reinterpret_cast<T *>(&nan_value);
+ fflags = static_cast<uint32_t>(FPExceptions::kInvalidOp);
+ } else if (vs2 == 0.0) {
+ UInt value =
+ std::signbit(vs2) ? FPTypeInfo<T>::kNegInf : FPTypeInfo<T>::kPosInf;
+ return_value = *reinterpret_cast<T *>(&value);
+ fflags = static_cast<uint32_t>(FPExceptions::kDivByZero);
+ } else if (std::isinf(vs2)) {
+ return_value = 0.0;
+ fflags = static_cast<uint32_t>(FPExceptions::kInvalidOp);
+ } else {
+ ScopedFPStatus status(rv_fp->host_fp_interface(),
+ FPRoundingMode::kRoundTowardsZero);
+ T value = 1.0 / sqrt(vs2);
+ UInt uint_val = *reinterpret_cast<UInt *>(&value);
+ UInt mask = FPTypeInfo<T>::kSigMask >> 7;
+ uint_val = uint_val & ~mask;
+ return_value = *reinterpret_cast<T *>(&uint_val);
+ }
+ return std::make_tuple(return_value, fflags);
+}
+
+// Test approximate reciprocal square root.
+TEST_F(RiscVCheriotFPUnaryInstructionsTest, Vfrsqrt7v) {
+ SetSemanticFunction(&Vfrsqrt7v);
+ UnaryOpWithFflagsFPTestHelperV<float, float>(
+ "Vfrsqrt7.v_32", /*sew*/ 32, instruction_,
+ /*delta_position*/ 7, [this](float vs2) -> std::tuple<float, uint32_t> {
+ return Vfrsqrt7vTestHelper(vs2, this->rv_fp_);
+ });
+ ResetInstruction();
+ SetSemanticFunction(&Vfrsqrt7v);
+ UnaryOpWithFflagsFPTestHelperV<double, double>(
+ "Vfsqrt.v_64", /*sew*/ 64, instruction_, /*delta_position*/ 7,
+ [this](double vs2) -> std::tuple<double, uint32_t> {
+ return Vfrsqrt7vTestHelper(vs2, this->rv_fp_);
+ });
+}
+
+// Helper function for testing square root instruction.
+template <typename T>
+inline std::tuple<T, uint32_t> VfsqrtvTestHelper(T vs2) {
+ if (vs2 == 0.0) return std::make_tuple(vs2, 0);
+ if (std::isnan(vs2) || (vs2 < 0.0)) {
+ auto val = FPTypeInfo<T>::kCanonicalNaN;
+ uint32_t flags = 0;
+ if (!mpact::sim::generic::FPTypeInfo<T>::IsQNaN(vs2)) {
+ flags = (uint32_t)FPExceptions::kInvalidOp;
+ }
+ return std::make_tuple(*reinterpret_cast<const T *>(&val),
+ (uint32_t)FPExceptions::kInvalidOp);
+ }
+ T res = sqrt(vs2);
+
+ return std::make_tuple(res, 0);
+}
+
+// Test square root instruction.
+TEST_F(RiscVCheriotFPUnaryInstructionsTest, Vfsqrtv) {
+ SetSemanticFunction(&Vfsqrtv);
+ UnaryOpWithFflagsFPTestHelperV<float, float>(
+ "Vfsqrt.v_32", /*sew*/ 32, instruction_,
+ /*delta_position*/ 32, [](float vs2) -> std::tuple<float, uint32_t> {
+ return VfsqrtvTestHelper(vs2);
+ });
+ ResetInstruction();
+ SetSemanticFunction(&Vfsqrtv);
+ UnaryOpWithFflagsFPTestHelperV<double, double>(
+ "Vfsqrt.v_64", /*sew*/ 64, instruction_, /*delta_position*/ 64,
+ [](double vs2) -> std::tuple<double, uint32_t> {
+ return VfsqrtvTestHelper(vs2);
+ });
+}
+
+// Test widening convert fp to fp.
+TEST_F(RiscVCheriotFPUnaryInstructionsTest, Vfwcvtffv) {
+ SetSemanticFunction(&Vfwcvtffv);
+ UnaryOpTestHelperV<double, float>(
+ "Vfwcvt.f.f.v_32", /*sew*/ 32, instruction_,
+ [](float vs2) -> double { return static_cast<double>(vs2); });
+}
+
+// Test widening convert unsigned integer to fp.
+TEST_F(RiscVCheriotFPUnaryInstructionsTest, Vfwcvtfxuv) {
+ SetSemanticFunction(&Vfwcvtfxuv);
+ UnaryOpTestHelperV<float, uint16_t>(
+ "Vfwcvt.f.xu.v_16", /*sew*/ 16, instruction_,
+ [](uint16_t vs2) -> float { return static_cast<float>(vs2); });
+ ResetInstruction();
+ SetSemanticFunction(&Vfwcvtfxuv);
+ UnaryOpTestHelperV<double, uint32_t>(
+ "Vfwcvt.f.xu.v_32", /*sew*/ 32, instruction_,
+ [](uint32_t vs2) -> double { return static_cast<double>(vs2); });
+}
+
+// Test widening convert signed integer to fp.
+TEST_F(RiscVCheriotFPUnaryInstructionsTest, Vfwcvtfxv) {
+ SetSemanticFunction(&Vfwcvtfxv);
+ UnaryOpTestHelperV<float, int16_t>(
+ "Vfwcvt.f.x.v_16", /*sew*/ 16, instruction_,
+ [](int16_t vs2) -> float { return static_cast<float>(vs2); });
+ ResetInstruction();
+ SetSemanticFunction(&Vfwcvtfxv);
+ UnaryOpTestHelperV<double, int32_t>(
+ "Vfwcvt.f.x.v_32", /*sew*/ 32, instruction_,
+ [](int32_t vs2) -> double { return static_cast<double>(vs2); });
+}
+
+// Test widening convert fp to signed integer with truncation (round to zero).
+TEST_F(RiscVCheriotFPUnaryInstructionsTest, Vfwcvtrtzxfv) {
+ SetSemanticFunction(&Vfwcvtrtzxfv);
+ rv_fp_->SetRoundingMode(FPRoundingMode::kRoundTowardsZero);
+ UnaryOpWithFflagsFPTestHelperV<int64_t, float>(
+ "Vfwcvt.rtz.xu.f.v_32", /*sew*/ 32, instruction_, /*delta_position*/ 64,
+ [this](float vs2) -> std::tuple<int64_t, uint32_t> {
+ return ConvertHelper<float, int64_t>(vs2, this->rv_fp_);
+ });
+}
+
+// Test widening convert fp to unsigned integer with truncation (round to zero).
+TEST_F(RiscVCheriotFPUnaryInstructionsTest, Vfwcvtrtzxufv) {
+ SetSemanticFunction(&Vfwcvtrtzxufv);
+ rv_fp_->SetRoundingMode(FPRoundingMode::kRoundTowardsZero);
+ UnaryOpWithFflagsFPTestHelperV<uint64_t, float>(
+ "Vfwcvt.rtz.xu.f.v_32", /*sew*/ 32, instruction_, /*delta_position*/ 64,
+ [this](float vs2) -> std::tuple<uint64_t, uint32_t> {
+ return ConvertHelper<float, uint64_t>(vs2, this->rv_fp_);
+ });
+}
+
+// Test widening convert fp to signed integer with rounding.
+TEST_F(RiscVCheriotFPUnaryInstructionsTest, Vfwcvtxfv) {
+ SetSemanticFunction(&Vfwcvtxfv);
+ UnaryOpWithFflagsFPTestHelperV<int64_t, float>(
+ "Vfwcvt.rtz.xu.f.v_32", /*sew*/ 32, instruction_, /*delta_position*/ 64,
+ [this](float vs2) -> std::tuple<int64_t, uint32_t> {
+ return ConvertHelper<float, int64_t>(vs2, this->rv_fp_);
+ });
+}
+
+// Test widening convert fp to unsigned integer with rounding.
+TEST_F(RiscVCheriotFPUnaryInstructionsTest, Vfwcvtxufv) {
+ SetSemanticFunction(&Vfwcvtxufv);
+ UnaryOpWithFflagsFPTestHelperV<uint64_t, float>(
+ "Vfwcvt.rtz.xu.f.v_32", /*sew*/ 32, instruction_, /*delta_position*/ 64,
+ [this](float vs2) -> std::tuple<uint64_t, uint32_t> {
+ return ConvertHelper<float, uint64_t>(vs2, this->rv_fp_);
+ });
+}
+
+} // namespace
diff --git a/cheriot/test/riscv_cheriot_vector_instructions_test_base.h b/cheriot/test/riscv_cheriot_vector_instructions_test_base.h
new file mode 100644
index 0000000..fc074e1
--- /dev/null
+++ b/cheriot/test/riscv_cheriot_vector_instructions_test_base.h
@@ -0,0 +1,1234 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef MPACT_RISCV_RISCV_TEST_RISCV_VECTOR_INSTRUCTIONS_TEST_BASE_H_
+#define MPACT_RISCV_RISCV_TEST_RISCV_VECTOR_INSTRUCTIONS_TEST_BASE_H_
+
+#include <algorithm>
+#include <cstdint>
+#include <cstring>
+#include <functional>
+#include <ios>
+#include <limits>
+#include <string>
+#include <tuple>
+#include <vector>
+
+#include "absl/functional/bind_front.h"
+#include "absl/random/random.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
+#include "absl/types/span.h"
+#include "cheriot/cheriot_register.h"
+#include "cheriot/cheriot_state.h"
+#include "cheriot/cheriot_vector_state.h"
+#include "cheriot/riscv_cheriot_vector_memory_instructions.h"
+#include "googlemock/include/gmock/gmock.h"
+#include "mpact/sim/generic/data_buffer.h"
+#include "mpact/sim/generic/immediate_operand.h"
+#include "mpact/sim/generic/instruction.h"
+#include "mpact/sim/generic/register.h"
+#include "mpact/sim/generic/type_helpers.h"
+#include "mpact/sim/util/memory/tagged_flat_demand_memory.h"
+#include "riscv//riscv_register.h"
+
+// This file defines commonly used constants in the vector instruction tests
+// as well as a base class for the vector instruction test fixtures. This base
+// class contains methods that make it more convenient to write vector
+// instruction test cases, and provide "harnesses" to test the functionality
+// of individual instructions across different lmul values, vstart values,
+// and vector length values.
+
+using ::absl::Span;
+using ::mpact::sim::cheriot::CheriotRegister;
+using ::mpact::sim::cheriot::CheriotState;
+using ::mpact::sim::cheriot::CheriotVectorState;
+using ::mpact::sim::cheriot::Vsetvl;
+using ::mpact::sim::generic::ImmediateOperand;
+using ::mpact::sim::generic::Instruction;
+using ::mpact::sim::generic::NarrowType;
+using ::mpact::sim::generic::RegisterBase;
+using ::mpact::sim::generic::SameSignedType;
+using ::mpact::sim::generic::WideType;
+using ::mpact::sim::riscv::RiscVState;
+using ::mpact::sim::riscv::RV32VectorDestinationOperand;
+using ::mpact::sim::riscv::RV32VectorSourceOperand;
+using ::mpact::sim::riscv::RVFpRegister;
+using ::mpact::sim::riscv::RVVectorRegister;
+using ::mpact::sim::util::TaggedFlatDemandMemory;
+using ::std::tuple;
+
+// Constants used in the tests.
+constexpr int kVectorLengthInBits = 512;
+constexpr int kVectorLengthInBytes = kVectorLengthInBits / 8;
+constexpr uint32_t kInstAddress = 0x1000;
+constexpr uint32_t kDataLoadAddress = 0x1'0000;
+constexpr uint32_t kDataStoreAddress = 0x2'0000;
+constexpr char kRs1Name[] = "c1";
+constexpr int kRs1 = 1;
+constexpr char kRs2Name[] = "c2";
+constexpr char kRs3Name[] = "c3";
+constexpr char kRdName[] = "c8";
+constexpr int kRd = 8;
+constexpr int kVmask = 1;
+constexpr char kVmaskName[] = "v1";
+constexpr int kVd = 8;
+constexpr char kVdName[] = "v8";
+constexpr int kVs1 = 16;
+constexpr char kVs1Name[] = "v16";
+constexpr int kVs2 = 24;
+constexpr char kVs2Name[] = "v24";
+
+// Setting bits and corresponding values for lmul and sew.
+constexpr int kLmulSettings[7] = {0b101, 0b110, 0b111, 0b000,
+ 0b001, 0b010, 0b011};
+constexpr int kLmul8Values[7] = {1, 2, 4, 8, 16, 32, 64};
+constexpr int kLmulSettingByLogSize[] = {0, 0b101, 0b110, 0b111,
+ 0b000, 0b001, 0b010, 0b011};
+constexpr int kSewSettings[4] = {0b000, 0b001, 0b010, 0b011};
+constexpr int kSewValues[4] = {1, 2, 4, 8};
+constexpr int kSewSettingsByByteSize[] = {0, 0b000, 0b001, 0, 0b010,
+ 0, 0, 0, 0b011};
+
+// Don't need to set every byte, as only the low bits are used for mask values.
+constexpr uint8_t kA5Mask[kVectorLengthInBytes] = {
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+};
+// This is the base class for vector instruction test fixtures. It implements
+// generic methods for testing and supporting testing of the RiscV vector
+// instructions.
+class RiscVCheriotVectorInstructionsTestBase : public testing::Test {
+ public:
+ RiscVCheriotVectorInstructionsTestBase() {
+ memory_ = new TaggedFlatDemandMemory(8);
+ state_ = new CheriotState("test", memory_);
+ rv_vector_ = new CheriotVectorState(state_, kVectorLengthInBytes);
+ instruction_ = new Instruction(kInstAddress, state_);
+ instruction_->set_size(4);
+ child_instruction_ = new Instruction(kInstAddress, state_);
+ child_instruction_->set_size(4);
+ // Initialize a portion of memory with a known pattern.
+ auto *db = state_->db_factory()->Allocate(8192);
+ auto span = db->Get<uint8_t>();
+ for (int i = 0; i < 8192; i++) {
+ span[i] = i & 0xff;
+ }
+ memory_->Store(kDataLoadAddress - 4096, db);
+ db->DecRef();
+ for (int i = 1; i < 32; i++) {
+ creg_[i] =
+ state_->GetRegister<CheriotRegister>(absl::StrCat("c", i)).first;
+ }
+ for (int i = 1; i < 32; i++) {
+ freg_[i] = state_->GetRegister<RVFpRegister>(absl::StrCat("f", i)).first;
+ }
+ for (int i = 1; i < 32; i++) {
+ vreg_[i] =
+ state_->GetRegister<RVVectorRegister>(absl::StrCat("v", i)).first;
+ }
+ }
+
+ ~RiscVCheriotVectorInstructionsTestBase() override {
+ delete state_;
+ delete rv_vector_;
+ instruction_->DecRef();
+ child_instruction_->DecRef();
+ delete memory_;
+ }
+
+ // Clear the instruction instance and allocate a new one.
+ void ResetInstruction() {
+ instruction_->DecRef();
+ instruction_ = new Instruction(kInstAddress, state_);
+ instruction_->set_size(4);
+ }
+
+ // Creates immediate operands with the values from the vector and appends them
+ // to the given instruction.
+ template <typename T>
+ void AppendImmediateOperands(Instruction *inst,
+ const std::vector<T> &values) {
+ for (auto value : values) {
+ auto *src = new ImmediateOperand<T>(value);
+ inst->AppendSource(src);
+ }
+ }
+
+ // Creates immediate operands with the values from the vector and appends them
+ // to the default instruction.
+ template <typename T>
+ void AppendImmediateOperands(const std::vector<T> &values) {
+ AppendImmediateOperands<T>(instruction_, values);
+ }
+
+ // Creates source and destination scalar register operands for the registers
+ // named in the two vectors and append them to the given instruction.
+ void AppendRegisterOperands(Instruction *inst,
+ const std::vector<std::string> &sources,
+ const std::vector<std::string> &destinations) {
+ for (auto ®_name : sources) {
+ auto *reg = state_->GetRegister<CheriotRegister>(reg_name).first;
+ inst->AppendSource(reg->CreateSourceOperand());
+ }
+ for (auto ®_name : destinations) {
+ auto *reg = state_->GetRegister<CheriotRegister>(reg_name).first;
+ inst->AppendDestination(reg->CreateDestinationOperand(0));
+ }
+ }
+
+ // Creates source and destination scalar register operands for the registers
+ // named in the two vectors and append them to the default instruction.
+ void AppendRegisterOperands(const std::vector<std::string> &sources,
+ const std::vector<std::string> &destinations) {
+ AppendRegisterOperands(instruction_, sources, destinations);
+ }
+
+ // Returns the value of the named vector register.
+ template <typename T>
+ T GetRegisterValue(absl::string_view vreg_name) {
+ auto *reg = state_->GetRegister<CheriotRegister>(vreg_name).first;
+ return reg->data_buffer()->Get<T>();
+ }
+
+ // named register and sets it to the corresponding value.
+ template <typename T, typename RegisterType = CheriotRegister>
+ void SetRegisterValues(
+ const std::vector<tuple<std::string, const T>> &values) {
+ for (auto &[reg_name, value] : values) {
+ auto *reg = state_->GetRegister<RegisterType>(reg_name).first;
+ auto *db =
+ state_->db_factory()->Allocate<typename RegisterType::ValueType>(1);
+ db->template Set<T>(0, value);
+ reg->SetDataBuffer(db);
+ db->DecRef();
+ }
+ }
+
+ // Creates source and destination scalar register operands for the registers
+ // named in the two vectors and append them to the given instruction.
+ void AppendVectorRegisterOperands(Instruction *inst,
+ const std::vector<int> &sources,
+ const std::vector<int> &destinations) {
+ for (auto ®_no : sources) {
+ std::vector<RegisterBase *> reg_vec;
+ for (int i = 0; (i < 8) && (i + reg_no < 32); i++) {
+ std::string reg_name = absl::StrCat("v", i + reg_no);
+ reg_vec.push_back(
+ state_->GetRegister<RVVectorRegister>(reg_name).first);
+ }
+ auto *op = new RV32VectorSourceOperand(
+ absl::Span<RegisterBase *>(reg_vec), absl::StrCat("v", reg_no));
+ inst->AppendSource(op);
+ }
+ for (auto ®_no : destinations) {
+ std::vector<RegisterBase *> reg_vec;
+ for (int i = 0; (i < 8) && (i + reg_no < 32); i++) {
+ std::string reg_name = absl::StrCat("v", i + reg_no);
+ reg_vec.push_back(
+ state_->GetRegister<RVVectorRegister>(reg_name).first);
+ }
+ auto *op = new RV32VectorDestinationOperand(
+ absl::Span<RegisterBase *>(reg_vec), 0, absl::StrCat("v", reg_no));
+ inst->AppendDestination(op);
+ }
+ }
+ // Creates source and destination scalar register operands for the registers
+ // named in the two vectors and append them to the default instruction.
+ void AppendVectorRegisterOperands(const std::vector<int> &sources,
+ const std::vector<int> &destinations) {
+ AppendVectorRegisterOperands(instruction_, sources, destinations);
+ }
+
+ // Returns the value of the named vector register.
+ template <typename T>
+ T GetVectorRegisterValue(absl::string_view reg_name) {
+ auto *reg = state_->GetRegister<RVVectorRegister>(reg_name).first;
+ return reg->data_buffer()->Get<T>(0);
+ }
+
+ // Set a vector register value. Takes a vector of tuples of register names and
+ // spans of values, fetches each register and sets it to the corresponding
+ // value.
+ template <typename T>
+ void SetVectorRegisterValues(
+ const std::vector<tuple<std::string, Span<const T>>> &values) {
+ for (auto &[vreg_name, span] : values) {
+ auto *vreg = state_->GetRegister<RVVectorRegister>(vreg_name).first;
+ auto *db = state_->db_factory()->MakeCopyOf(vreg->data_buffer());
+ db->template Set<T>(span);
+ vreg->SetDataBuffer(db);
+ db->DecRef();
+ }
+ }
+
+ // Initializes the semantic function of the instruction object.
+ void SetSemanticFunction(Instruction *inst,
+ Instruction::SemanticFunction fcn) {
+ inst->set_semantic_function(fcn);
+ }
+
+ // Initializes the semantic function for the default instruction.
+ void SetSemanticFunction(Instruction::SemanticFunction fcn) {
+ instruction_->set_semantic_function(fcn);
+ }
+
+ // Sets the default child instruction as the child of the default instruction.
+ void SetChildInstruction() { instruction_->AppendChild(child_instruction_); }
+
+ // Initializes the semantic function for the default child instruction.
+ void SetChildSemanticFunction(Instruction::SemanticFunction fcn) {
+ child_instruction_->set_semantic_function(fcn);
+ }
+
+ // Configure the vector unit according to the vtype and vlen values.
+ void ConfigureVectorUnit(uint32_t vtype, uint32_t vlen) {
+ Instruction *inst = new Instruction(state_);
+ AppendImmediateOperands<uint32_t>(inst, {vlen, vtype});
+ SetSemanticFunction(inst, absl::bind_front(&Vsetvl, true, false));
+ inst->Execute(nullptr);
+ inst->DecRef();
+ }
+
+ // Clear count registers in the register group, starting at start.
+ void ClearVectorRegisterGroup(int start, int count) {
+ for (int reg = start; (reg < start + count) && (reg < 32); reg++) {
+ memset(vreg_[reg]->data_buffer()->raw_ptr(), 0, kVectorLengthInBytes);
+ }
+ }
+
+ // Create a random value in the valid range for the type.
+ template <typename T>
+ T RandomValue() {
+ return absl::Uniform(absl::IntervalClosed, bitgen_,
+ std::numeric_limits<T>::lowest(),
+ std::numeric_limits<T>::max());
+ }
+
+ // Fill the span with random values.
+ template <typename T>
+ void FillArrayWithRandomValues(absl::Span<T> span) {
+ for (auto &val : span) {
+ val = RandomValue<T>();
+ }
+ }
+
+ // Helper function for testing unary vector-vector instructions.
+ template <typename Vd, typename Vs2>
+ void UnaryOpTestHelperV(absl::string_view name, int sew, Instruction *inst,
+ std::function<Vd(Vs2)> operation) {
+ int byte_sew = sew / 8;
+ if (byte_sew != sizeof(Vd) && byte_sew != sizeof(Vs2)) {
+ FAIL() << name << ": selected element width != any operand types"
+ << "sew: " << sew << " Vd: " << sizeof(Vd)
+ << " Vs2: " << sizeof(Vs2);
+ return;
+ }
+ // Number of elements per vector register.
+ constexpr int vs2_size = kVectorLengthInBytes / sizeof(Vs2);
+ // Input values for 8 registers.
+ Vs2 vs2_value[vs2_size * 8];
+ auto vs2_span = Span<Vs2>(vs2_value);
+ AppendVectorRegisterOperands({kVs2, kVmask}, {kVd});
+ // Initialize input values.
+ FillArrayWithRandomValues<Vs2>(vs2_span);
+ SetVectorRegisterValues<uint8_t>(
+ {{kVmaskName, Span<const uint8_t>(kA5Mask)}});
+ for (int i = 0; i < 8; i++) {
+ auto vs2_name = absl::StrCat("v", kVs2 + i);
+ SetVectorRegisterValues<Vs2>(
+ {{vs2_name, vs2_span.subspan(vs2_size * i, vs2_size)}});
+ }
+ for (int lmul_index = 0; lmul_index < 7; lmul_index++) {
+ for (int vstart_count = 0; vstart_count < 4; vstart_count++) {
+ for (int vlen_count = 0; vlen_count < 4; vlen_count++) {
+ int lmul8 = kLmul8Values[lmul_index];
+ int lmul8_vd = lmul8 * sizeof(Vd) / byte_sew;
+ int lmul8_vs2 = lmul8 * sizeof(Vs2) / byte_sew;
+ int num_values = lmul8 * kVectorLengthInBytes / (8 * byte_sew);
+ int vstart = 0;
+ if (vstart_count > 0) {
+ vstart = absl::Uniform(absl::IntervalOpen, bitgen_, 0, num_values);
+ }
+ // Set vlen, but leave vlen high at least once.
+ int vlen = 1024;
+ if (vlen_count > 0) {
+ vlen = absl::Uniform(absl::IntervalOpenClosed, bitgen_, vstart,
+ num_values);
+ }
+ num_values = std::min(num_values, vlen);
+ ASSERT_TRUE(vlen > vstart);
+ // Configure vector unit for different lmul settings.
+ uint32_t vtype = (kSewSettingsByByteSize[byte_sew] << 3) |
+ kLmulSettings[lmul_index];
+ ConfigureVectorUnit(vtype, vlen);
+ rv_vector_->set_vstart(vstart);
+ ClearVectorRegisterGroup(kVd, 8);
+
+ inst->Execute();
+ if (lmul8_vd < 1 || lmul8_vd > 64) {
+ EXPECT_TRUE(rv_vector_->vector_exception())
+ << "lmul8: vd: " << lmul8_vd;
+ rv_vector_->clear_vector_exception();
+ continue;
+ }
+ if (lmul8_vs2 < 1 || lmul8_vs2 > 64) {
+ EXPECT_TRUE(rv_vector_->vector_exception())
+ << "lmul8: vs2: " << lmul8_vs2;
+ rv_vector_->clear_vector_exception();
+ continue;
+ }
+
+ EXPECT_FALSE(rv_vector_->vector_exception());
+ EXPECT_EQ(rv_vector_->vstart(), 0);
+ int count = 0;
+ for (int reg = kVd; reg < kVd + 8; reg++) {
+ for (int i = 0; i < kVectorLengthInBytes / sizeof(Vd); i++) {
+ int mask_index = count >> 3;
+ int mask_offset = count & 0b111;
+ bool mask_value =
+ ((kA5Mask[mask_index] >> mask_offset) & 0b1) != 0;
+ if ((count >= vstart) && mask_value && (count < num_values)) {
+ EXPECT_EQ(operation(vs2_value[count]),
+ vreg_[reg]->data_buffer()->Get<Vd>(i))
+ << absl::StrCat(name, "[", count, "] != reg[", reg, "][", i,
+ "] lmul8(", lmul8, ")");
+ } else {
+ EXPECT_EQ(0, vreg_[reg]->data_buffer()->Get<Vd>(i))
+ << absl::StrCat(name, " 0 != reg[", reg, "][", i,
+ "] lmul8(", lmul8, ")");
+ }
+ count++;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Helper function for testing vector-vector instructions that use the value
+ // of the mask bit.
+ template <typename Vd, typename Vs2, typename Vs1>
+ void BinaryOpWithMaskTestHelperVV(
+ absl::string_view name, int sew, Instruction *inst,
+ std::function<Vd(Vs2, Vs1, bool)> operation) {
+ int byte_sew = sew / 8;
+ if (byte_sew != sizeof(Vd) && byte_sew != sizeof(Vs2) &&
+ byte_sew != sizeof(Vs1)) {
+ FAIL() << name << ": selected element width != any operand types"
+ << "sew: " << sew << " Vd: " << sizeof(Vd)
+ << " Vs2: " << sizeof(Vs2) << " Vs1: " << sizeof(Vs1);
+ return;
+ }
+ // Number of elements per vector register.
+ constexpr int vs2_size = kVectorLengthInBytes / sizeof(Vs2);
+ constexpr int vs1_size = kVectorLengthInBytes / sizeof(Vs1);
+ // Input values for 8 registers.
+ Vs2 vs2_value[vs2_size * 8];
+ auto vs2_span = Span<Vs2>(vs2_value);
+ Vs1 vs1_value[vs1_size * 8];
+ auto vs1_span = Span<Vs1>(vs1_value);
+ AppendVectorRegisterOperands({kVs2, kVs1, kVmask}, {kVd});
+ // Initialize input values.
+ FillArrayWithRandomValues<Vs2>(vs2_span);
+ FillArrayWithRandomValues<Vs1>(vs1_span);
+ SetVectorRegisterValues<uint8_t>(
+ {{kVmaskName, Span<const uint8_t>(kA5Mask)}});
+ for (int i = 0; i < 8; i++) {
+ auto vs1_name = absl::StrCat("v", kVs1 + i);
+ auto vs2_name = absl::StrCat("v", kVs2 + i);
+ SetVectorRegisterValues<Vs2>(
+ {{vs2_name, vs2_span.subspan(vs2_size * i, vs2_size)}});
+ SetVectorRegisterValues<Vs1>(
+ {{vs1_name, vs1_span.subspan(vs1_size * i, vs1_size)}});
+ }
+ // Iterate across the different lmul values.
+ for (int lmul_index = 0; lmul_index < 7; lmul_index++) {
+ // Try different vstart values.
+ for (int vstart_count = 0; vstart_count < 4; vstart_count++) {
+ for (int vlen_count = 0; vlen_count < 4; vlen_count++) {
+ int lmul8 = kLmul8Values[lmul_index];
+ int lmul8_vd = lmul8 * sizeof(Vd) / byte_sew;
+ int lmul8_vs2 = lmul8 * sizeof(Vs2) / byte_sew;
+ int lmul8_vs1 = lmul8 * sizeof(Vs1) / byte_sew;
+ int num_values = lmul8 * kVectorLengthInBytes / (8 * byte_sew);
+ int vstart = 0;
+ if (vstart_count > 0) {
+ vstart = absl::Uniform(absl::IntervalOpen, bitgen_, 0, num_values);
+ }
+ // Set vlen, but leave vlen high at least once.
+ int vlen = 1024;
+ if (vlen_count > 0) {
+ vlen = absl::Uniform(absl::IntervalOpenClosed, bitgen_, vstart,
+ num_values);
+ }
+ num_values = std::min(num_values, vlen);
+ // Configure vector unit for different lmul settings.
+ uint32_t vtype = (kSewSettingsByByteSize[byte_sew] << 3) |
+ kLmulSettings[lmul_index];
+ ConfigureVectorUnit(vtype, vlen);
+ rv_vector_->set_vstart(vstart);
+ ClearVectorRegisterGroup(kVd, 8);
+
+ inst->Execute();
+
+ if ((std::min(std::min(lmul8_vs2, lmul8_vs1), lmul8_vd) < 1) ||
+ (std::max(std::max(lmul8_vs2, lmul8_vs1), lmul8_vd) > 64)) {
+ EXPECT_TRUE(rv_vector_->vector_exception());
+ rv_vector_->clear_vector_exception();
+ continue;
+ }
+
+ EXPECT_FALSE(rv_vector_->vector_exception());
+ EXPECT_EQ(rv_vector_->vstart(), 0);
+ int count = 0;
+ for (int reg = kVd; reg < kVd + 8; reg++) {
+ for (int i = 0; i < kVectorLengthInBytes / sizeof(Vd); i++) {
+ int mask_index = count >> 3;
+ int mask_offset = count & 0b111;
+ bool mask_value =
+ ((kA5Mask[mask_index] >> mask_offset) & 0b1) != 0;
+ if ((count >= vstart) && (count < num_values)) {
+ EXPECT_EQ(
+ operation(vs2_value[count], vs1_value[count], mask_value),
+ vreg_[reg]->data_buffer()->Get<Vd>(i))
+ << std::hex << (int64_t)vs2_value[count] << ", "
+ << (int64_t)vs1_value[count] << " " << std::dec
+ << (int64_t)vs2_value[count] << ", "
+ << (int64_t)vs1_value[count] << " "
+ << absl::StrCat(name, "[", count, "] != reg[", reg, "][", i,
+ "] lmul8(", lmul8, ") vstart(", vstart,
+ ")");
+ } else {
+ EXPECT_EQ(0, vreg_[reg]->data_buffer()->Get<Vd>(i))
+ << absl::StrCat(name, " 0 != reg[", reg, "][", i,
+ "] lmul8(", lmul8, ") vstart(", vstart,
+ ")");
+ }
+ count++;
+ }
+ }
+ if (HasFailure()) return;
+ }
+ }
+ }
+ }
+
+ // Helper function for testing vector-vector instructions that do not
+ // use the value of the mask bit.
+ template <typename Vd, typename Vs2, typename Vs1>
+ void BinaryOpTestHelperVV(absl::string_view name, int sew, Instruction *inst,
+ std::function<Vd(Vs2, Vs1)> operation) {
+ BinaryOpWithMaskTestHelperVV<Vd, Vs2, Vs1>(
+ name, sew, inst, [operation](Vs2 vs2, Vs1 vs1, bool mask_value) -> Vd {
+ if (mask_value) {
+ return operation(vs2, vs1);
+ }
+ return 0;
+ });
+ }
+
+ // Helper function for testing vector-scalar/immediate instructions that use
+ // the value of the mask bit.
+ template <typename Vd, typename Vs2, typename Rs1>
+ void BinaryOpWithMaskTestHelperVX(
+ absl::string_view name, int sew, Instruction *inst,
+ std::function<Vd(Vs2, Rs1, bool)> operation) {
+ int byte_sew = sew / 8;
+ if (byte_sew != sizeof(Vd) && byte_sew != sizeof(Vs2) &&
+ byte_sew != sizeof(Rs1)) {
+ FAIL() << name << ": selected element width != any operand types"
+ << "sew: " << sew << " Vd: " << sizeof(Vd)
+ << " Vs2: " << sizeof(Vs2) << " Rs1: " << sizeof(Rs1);
+ return;
+ }
+ // Number of elements per vector register.
+ constexpr int vs2_size = kVectorLengthInBytes / sizeof(Vs2);
+ // Input values for 8 registers.
+ Vs2 vs2_value[vs2_size * 8];
+ auto vs2_span = Span<Vs2>(vs2_value);
+ AppendVectorRegisterOperands({kVs2}, {});
+ AppendRegisterOperands({kRs1Name}, {});
+ AppendVectorRegisterOperands({kVmask}, {kVd});
+ // Initialize input values.
+ FillArrayWithRandomValues<Vs2>(vs2_span);
+ SetVectorRegisterValues<uint8_t>(
+ {{kVmaskName, Span<const uint8_t>(kA5Mask)}});
+ for (int i = 0; i < 8; i++) {
+ auto vs2_name = absl::StrCat("v", kVs2 + i);
+ SetVectorRegisterValues<Vs2>(
+ {{vs2_name, vs2_span.subspan(vs2_size * i, vs2_size)}});
+ }
+ // Iterate across the different lmul values.
+ for (int lmul_index = 0; lmul_index < 7; lmul_index++) {
+ // Try different vstart values.
+ for (int vstart_count = 0; vstart_count < 4; vstart_count++) {
+ for (int vlen_count = 0; vlen_count < 4; vlen_count++) {
+ int lmul8 = kLmul8Values[lmul_index];
+ int lmul8_vd = lmul8 * sizeof(Vd) / byte_sew;
+ int lmul8_vs2 = lmul8 * sizeof(Vs2) / byte_sew;
+ int num_values = lmul8 * kVectorLengthInBytes / (8 * byte_sew);
+ // Set vstart, but leave vstart at 0 at least once.
+ int vstart = 0;
+ if (vstart_count > 0) {
+ vstart = absl::Uniform(absl::IntervalOpen, bitgen_, 0, num_values);
+ }
+ // Set vlen, but leave vlen high at least once.
+ int vlen = 1024;
+ if (vlen_count > 0) {
+ vlen = absl::Uniform(absl::IntervalOpenClosed, bitgen_, vstart,
+ num_values);
+ }
+ num_values = std::min(num_values, vlen);
+ ASSERT_TRUE(vlen > vstart);
+ // Configure vector unit for the different lmul settings.
+ uint32_t vtype = (kSewSettingsByByteSize[byte_sew] << 3) |
+ kLmulSettings[lmul_index];
+ ConfigureVectorUnit(vtype, vlen);
+ rv_vector_->set_vstart(vstart);
+ ClearVectorRegisterGroup(kVd, 8);
+
+ // Generate a new rs1 value.
+ CheriotRegister::ValueType rs1_reg_value =
+ RandomValue<CheriotRegister::ValueType>();
+ SetRegisterValues<CheriotRegister::ValueType>(
+ {{kRs1Name, rs1_reg_value}});
+ // Cast the value to the appropriate width, sign-extending if need
+ // be.
+ Rs1 rs1_value = static_cast<Rs1>(
+ static_cast<typename SameSignedType<CheriotRegister::ValueType,
+ Rs1>::type>(rs1_reg_value));
+
+ inst->Execute();
+ if ((std::min(lmul8_vs2, lmul8_vd) < 1) ||
+ (std::max(lmul8_vs2, lmul8_vd) > 64)) {
+ EXPECT_TRUE(rv_vector_->vector_exception());
+ rv_vector_->clear_vector_exception();
+ continue;
+ }
+
+ EXPECT_FALSE(rv_vector_->vector_exception());
+ EXPECT_EQ(rv_vector_->vstart(), 0);
+ int count = 0;
+ for (int reg = kVd; reg < kVd + 8; reg++) {
+ for (int i = 0; i < kVectorLengthInBytes / sizeof(Vd); i++) {
+ int mask_index = count >> 3;
+ int mask_offset = count & 0b111;
+ bool mask_value =
+ ((kA5Mask[mask_index] >> mask_offset) & 0b1) != 0;
+ // Compare elements that are between vstart and vlen for which
+ // the mask is true.
+ if ((count >= vstart) && (count < num_values)) {
+ Vd expected_value = operation(
+ vs2_value[count], static_cast<Rs1>(rs1_value), mask_value);
+ Vd inst_value = vreg_[reg]->data_buffer()->Get<Vd>(i);
+ EXPECT_EQ(expected_value, inst_value) << absl::StrCat(
+ name, " [", count, "] != reg[", reg, "][", i, "] lmul8(",
+ lmul8, ") op(", absl::Hex(vs2_value[count]), ", ",
+ absl::Hex(static_cast<Rs1>(rs1_value)),
+ ") vreg: ", absl::Hex(inst_value));
+ } else {
+ // The others should be zero.
+ EXPECT_EQ(0, vreg_[reg]->data_buffer()->Get<Vd>(i))
+ << absl::StrCat(name, " 0 != reg[", reg, "][", i,
+ "] lmul8(", lmul8, ")");
+ }
+ count++;
+ }
+ }
+ if (HasFailure()) return;
+ }
+ }
+ }
+ }
+
+ // Templated helper function that tests vector-scalar instructions that do
+ // not use the value of the mask bit.
+ template <typename Vd, typename Vs2, typename Vs1>
+ void BinaryOpTestHelperVX(absl::string_view name, int sew, Instruction *inst,
+ std::function<Vd(Vs2, Vs1)> operation) {
+ BinaryOpWithMaskTestHelperVX<Vd, Vs2, Vs1>(
+ name, sew, inst, [operation](Vs2 vs2, Vs1 vs1, bool mask_value) -> Vd {
+ if (mask_value) {
+ return operation(vs2, vs1);
+ }
+ return 0;
+ });
+ }
+
+ // Helper function for testing vector-vector instructions that use the value
+ // of the mask bit.
+ template <typename Vd, typename Vs2, typename Vs1>
+ void TernaryOpWithMaskTestHelperVV(
+ absl::string_view name, int sew, Instruction *inst,
+ std::function<Vd(Vs2, Vs1, Vd, bool)> operation) {
+ int byte_sew = sew / 8;
+ if (byte_sew != sizeof(Vd) && byte_sew != sizeof(Vs2) &&
+ byte_sew != sizeof(Vs1)) {
+ FAIL() << name << ": selected element width != any operand types"
+ << "sew: " << sew << " Vd: " << sizeof(Vd)
+ << " Vs2: " << sizeof(Vs2) << " Vs1: " << sizeof(Vs1);
+ return;
+ }
+ // Number of elements per vector register.
+ constexpr int vd_size = kVectorLengthInBytes / sizeof(Vd);
+ constexpr int vs2_size = kVectorLengthInBytes / sizeof(Vs2);
+ constexpr int vs1_size = kVectorLengthInBytes / sizeof(Vs1);
+ // Input values for 8 registers.
+ Vd vd_value[vd_size * 8];
+ auto vd_span = Span<Vd>(vd_value);
+ Vs2 vs2_value[vs2_size * 8];
+ auto vs2_span = Span<Vs2>(vs2_value);
+ Vs1 vs1_value[vs1_size * 8];
+ auto vs1_span = Span<Vs1>(vs1_value);
+ AppendVectorRegisterOperands({kVs2, kVs1, kVd, kVmask}, {kVd});
+ // Initialize input values.
+ FillArrayWithRandomValues<Vd>(vd_span);
+ FillArrayWithRandomValues<Vs2>(vs2_span);
+ FillArrayWithRandomValues<Vs1>(vs1_span);
+ SetVectorRegisterValues<uint8_t>(
+ {{kVmaskName, Span<const uint8_t>(kA5Mask)}});
+ for (int i = 0; i < 8; i++) {
+ auto vs1_name = absl::StrCat("v", kVs1 + i);
+ auto vs2_name = absl::StrCat("v", kVs2 + i);
+ SetVectorRegisterValues<Vs2>(
+ {{vs2_name, vs2_span.subspan(vs2_size * i, vs2_size)}});
+ SetVectorRegisterValues<Vs1>(
+ {{vs1_name, vs1_span.subspan(vs1_size * i, vs1_size)}});
+ }
+ // Iterate across the different lmul values.
+ for (int lmul_index = 0; lmul_index < 7; lmul_index++) {
+ // Try different vstart values.
+ for (int vstart_count = 0; vstart_count < 4; vstart_count++) {
+ for (int vlen_count = 0; vlen_count < 4; vlen_count++) {
+ int lmul8 = kLmul8Values[lmul_index];
+ int lmul8_vd = lmul8 * sizeof(Vd) / byte_sew;
+ int lmul8_vs2 = lmul8 * sizeof(Vs2) / byte_sew;
+ int lmul8_vs1 = lmul8 * sizeof(Vs1) / byte_sew;
+ int num_values = lmul8 * kVectorLengthInBytes / (8 * byte_sew);
+ int vstart = 0;
+ if (vstart_count > 0) {
+ vstart = absl::Uniform(absl::IntervalOpen, bitgen_, 0, num_values);
+ }
+ // Set vlen, but leave vlen high at least once.
+ int vlen = 1024;
+ if (vlen_count > 0) {
+ vlen = absl::Uniform(absl::IntervalOpenClosed, bitgen_, vstart,
+ num_values);
+ }
+ num_values = std::min(num_values, vlen);
+ // Configure vector unit for different lmul settings.
+ uint32_t vtype = (kSewSettingsByByteSize[byte_sew] << 3) |
+ kLmulSettings[lmul_index];
+ ConfigureVectorUnit(vtype, vlen);
+ rv_vector_->set_vstart(vstart);
+
+ // Reset Vd values, since the previous instruction execution
+ // overwrites them.
+ for (int i = 0; i < 8; i++) {
+ auto vd_name = absl::StrCat("v", kVd + i);
+ SetVectorRegisterValues<Vd>(
+ {{vd_name, vd_span.subspan(vd_size * i, vd_size)}});
+ }
+
+ inst->Execute();
+
+ if ((std::min(std::min(lmul8_vs2, lmul8_vs1), lmul8_vd) < 1) ||
+ (std::max(std::max(lmul8_vs2, lmul8_vs1), lmul8_vd) > 64)) {
+ EXPECT_TRUE(rv_vector_->vector_exception());
+ rv_vector_->clear_vector_exception();
+ continue;
+ }
+
+ EXPECT_FALSE(rv_vector_->vector_exception());
+ int count = 0;
+ int reg_offset = count * byte_sew / kVectorLengthInBytes;
+ for (int reg = kVd + reg_offset; reg < kVd + 8; reg++) {
+ for (int i = 0; i < kVectorLengthInBytes / sizeof(Vd); i++) {
+ int mask_index = count >> 3;
+ int mask_offset = count & 0b111;
+ bool mask_value =
+ ((kA5Mask[mask_index] >> mask_offset) & 0b1) != 0;
+ if ((count >= vstart) && (count < num_values)) {
+ EXPECT_EQ(operation(vs2_value[count], vs1_value[count],
+ vd_value[count], mask_value),
+ vreg_[reg]->data_buffer()->Get<Vd>(i))
+ << "mask: " << mask_value << " (" << std::hex
+ << (int64_t)vs2_value[count] << ", "
+ << (int64_t)vs1_value[count] << ") (" << std::dec
+ << (int64_t)vs2_value[count] << ", "
+ << (int64_t)vs1_value[count] << ", "
+ << (int64_t)vd_value[count] << ") "
+ << absl::StrCat(name, "[", count, "] != reg[", reg, "][", i,
+ "] lmul8(", lmul8, ") vstart(", vstart,
+ ")");
+ } else {
+ EXPECT_EQ(vd_value[count],
+ vreg_[reg]->data_buffer()->Get<Vd>(i))
+ << absl::StrCat(name, " 0 != reg[", reg, "][", i,
+ "] lmul8(", lmul8, ") vstart(", vstart,
+ ")");
+ }
+ count++;
+ }
+ }
+ if (HasFailure()) return;
+ }
+ }
+ }
+ }
+
+ // Helper function for testing vector-vector instructions that do not
+ // use the value of the mask bit.
+ template <typename Vd, typename Vs2, typename Vs1>
+ void TernaryOpTestHelperVV(absl::string_view name, int sew, Instruction *inst,
+ std::function<Vd(Vs2, Vs1, Vd)> operation) {
+ TernaryOpWithMaskTestHelperVV<Vd, Vs2, Vs1>(
+ name, sew, inst,
+ [operation](Vs2 vs2, Vs1 vs1, Vd vd, bool mask_value) -> Vd {
+ if (mask_value) {
+ return operation(vs2, vs1, vd);
+ }
+ return vd;
+ });
+ }
+
+ // Helper function for testing vector-scalar/immediate instructions that use
+ // the value of the mask bit.
+ template <typename Vd, typename Vs2, typename Rs1>
+ void TernaryOpWithMaskTestHelperVX(
+ absl::string_view name, int sew, Instruction *inst,
+ std::function<Vd(Vs2, Rs1, Vd, bool)> operation) {
+ int byte_sew = sew / 8;
+ if (byte_sew != sizeof(Vd) && byte_sew != sizeof(Vs2) &&
+ byte_sew != sizeof(Rs1)) {
+ FAIL() << name << ": selected element width != any operand types"
+ << "sew: " << sew << " Vd: " << sizeof(Vd)
+ << " Vs2: " << sizeof(Vs2) << " Rs1: " << sizeof(Rs1);
+ return;
+ }
+ // Number of elements per vector register.
+ constexpr int vd_size = kVectorLengthInBytes / sizeof(Vd);
+ constexpr int vs2_size = kVectorLengthInBytes / sizeof(Vs2);
+ // Input values for 8 registers.
+ Vd vd_value[vd_size * 8];
+ auto vd_span = Span<Vd>(vd_value);
+ Vs2 vs2_value[vs2_size * 8];
+ auto vs2_span = Span<Vs2>(vs2_value);
+ AppendVectorRegisterOperands({kVs2}, {});
+ AppendRegisterOperands({kRs1Name}, {});
+ AppendVectorRegisterOperands({kVd, kVmask}, {kVd});
+ // Initialize input values.
+ FillArrayWithRandomValues<Vd>(vd_span);
+ FillArrayWithRandomValues<Vs2>(vs2_span);
+ SetVectorRegisterValues<uint8_t>(
+ {{kVmaskName, Span<const uint8_t>(kA5Mask)}});
+ for (int i = 0; i < 8; i++) {
+ auto vs2_name = absl::StrCat("v", kVs2 + i);
+ SetVectorRegisterValues<Vs2>(
+ {{vs2_name, vs2_span.subspan(vs2_size * i, vs2_size)}});
+ }
+ // Iterate across the different lmul values.
+ for (int lmul_index = 0; lmul_index < 7; lmul_index++) {
+ // Try different vstart values.
+ for (int vstart_count = 0; vstart_count < 4; vstart_count++) {
+ for (int vlen_count = 0; vlen_count < 4; vlen_count++) {
+ int lmul8 = kLmul8Values[lmul_index];
+ int lmul8_vd = lmul8 * sizeof(Vd) / byte_sew;
+ int lmul8_vs2 = lmul8 * sizeof(Vs2) / byte_sew;
+ int num_values = lmul8 * kVectorLengthInBytes / (8 * byte_sew);
+ // Set vstart, but leave vstart at 0 at least once.
+ int vstart = 0;
+ if (vstart_count > 0) {
+ vstart = absl::Uniform(absl::IntervalOpen, bitgen_, 0, num_values);
+ }
+ // Set vlen, but leave vlen high at least once.
+ int vlen = 1024;
+ if (vlen_count > 0) {
+ vlen = absl::Uniform(absl::IntervalOpenClosed, bitgen_, vstart,
+ num_values);
+ }
+ num_values = std::min(num_values, vlen);
+ ASSERT_TRUE(vlen > vstart);
+ // Configure vector unit for the different lmul settings.
+ uint32_t vtype = (kSewSettingsByByteSize[byte_sew] << 3) |
+ kLmulSettings[lmul_index];
+ ConfigureVectorUnit(vtype, vlen);
+ rv_vector_->set_vstart(vstart);
+
+ // Reset Vd values, since the previous instruction execution
+ // overwrites them.
+ for (int i = 0; i < 8; i++) {
+ auto vd_name = absl::StrCat("v", kVd + i);
+ SetVectorRegisterValues<Vd>(
+ {{vd_name, vd_span.subspan(vd_size * i, vd_size)}});
+ }
+
+ // Generate a new rs1 value.
+ CheriotRegister::ValueType rs1_reg_value =
+ RandomValue<CheriotRegister::ValueType>();
+ SetRegisterValues<CheriotRegister::ValueType>(
+ {{kRs1Name, rs1_reg_value}});
+ // Cast the value to the appropriate width, sign-extending if need
+ // be.
+ Rs1 rs1_value = static_cast<Rs1>(
+ static_cast<typename SameSignedType<CheriotRegister::ValueType,
+ Rs1>::type>(rs1_reg_value));
+
+ inst->Execute();
+ if ((std::min(lmul8_vs2, lmul8_vd) < 1) ||
+ (std::max(lmul8_vs2, lmul8_vd) > 64)) {
+ EXPECT_TRUE(rv_vector_->vector_exception());
+ rv_vector_->clear_vector_exception();
+ continue;
+ }
+
+ EXPECT_FALSE(rv_vector_->vector_exception());
+ EXPECT_EQ(rv_vector_->vstart(), 0);
+ int count = 0;
+ for (int reg = kVd; reg < kVd + 8; reg++) {
+ for (int i = 0; i < kVectorLengthInBytes / sizeof(Vd); i++) {
+ int mask_index = count >> 3;
+ int mask_offset = count & 0b111;
+ bool mask_value =
+ ((kA5Mask[mask_index] >> mask_offset) & 0b1) != 0;
+ // Compare elements that are between vstart and vlen for which
+ // the mask is true.
+ if ((count >= vstart) && (count < num_values)) {
+ Vd expected_value =
+ operation(vs2_value[count], static_cast<Rs1>(rs1_value),
+ vd_value[count], mask_value);
+ Vd inst_value = vreg_[reg]->data_buffer()->Get<Vd>(i);
+ EXPECT_EQ(expected_value, inst_value)
+ << "mask: " << mask_value << " (" << std::hex
+ << (int64_t)vs2_value[count] << ", " << (int64_t)rs1_value
+ << ") (" << std::dec << (int64_t)vs2_value[count] << ", "
+ << (int64_t)rs1_value << ", " << (int64_t)vd_value[count]
+ << ") "
+ << absl::StrCat(name, "[", count, "] != reg[", reg, "][", i,
+ "] lmul8(", lmul8, ") vstart(", vstart,
+ ")");
+ } else {
+ // The others should be zero.
+ EXPECT_EQ(vd_span[count], vreg_[reg]->data_buffer()->Get<Vd>(i))
+ << absl::StrCat(name, " 0 != reg[", reg, "][", i,
+ "] lmul8(", lmul8, ")");
+ }
+ count++;
+ }
+ }
+ if (HasFailure()) return;
+ }
+ }
+ }
+ }
+
+ // Templated helper function that tests vector-scalar instructions that do
+ // not use the value of the mask bit.
+ template <typename Vd, typename Vs2, typename Rs1>
+ void TernaryOpTestHelperVX(absl::string_view name, int sew, Instruction *inst,
+ std::function<Vd(Vs2, Rs1, Vd)> operation) {
+ TernaryOpWithMaskTestHelperVX<Vd, Vs2, Rs1>(
+ name, sew, inst,
+ [operation](Vs2 vs2, Rs1 rs1, Vd vd, bool mask_value) -> Vd {
+ if (mask_value) {
+ return operation(vs2, rs1, vd);
+ }
+ return vd;
+ });
+ }
+
+ // Helper function for testing binary mask vector-vector instructions that
+ // use the mask bit.
+ template <typename Vs2, typename Vs1>
+ void BinaryMaskOpWithMaskTestHelperVV(
+ absl::string_view name, int sew, Instruction *inst,
+ std::function<uint8_t(Vs2, Vs1, bool)> operation) {
+ int byte_sew = sew / 8;
+ if (byte_sew != sizeof(Vs2) && byte_sew != sizeof(Vs1)) {
+ FAIL() << name << ": selected element width != any operand types"
+ << "sew: " << sew << " Vs2: " << sizeof(Vs2)
+ << " Vs1: " << sizeof(Vs1);
+ return;
+ }
+ // Number of elements per vector register.
+ constexpr int vs2_size = kVectorLengthInBytes / sizeof(Vs2);
+ constexpr int vs1_size = kVectorLengthInBytes / sizeof(Vs1);
+ // Input values for 8 registers.
+ Vs2 vs2_value[vs2_size * 8];
+ auto vs2_span = Span<Vs2>(vs2_value);
+ Vs1 vs1_value[vs1_size * 8];
+ auto vs1_span = Span<Vs1>(vs1_value);
+ AppendVectorRegisterOperands({kVs2, kVs1, kVmask}, {kVd});
+ // Initialize input values.
+ FillArrayWithRandomValues<Vs2>(vs2_span);
+ FillArrayWithRandomValues<Vs1>(vs1_span);
+ // Make every third value the same (at least if the types are same sized).
+ for (int i = 0; i < std::min(vs1_size, vs2_size); i += 3) {
+ vs1_span[i] = static_cast<Vs1>(vs2_span[i]);
+ }
+
+ SetVectorRegisterValues<uint8_t>(
+ {{kVmaskName, Span<const uint8_t>(kA5Mask)}});
+ for (int i = 0; i < 8; i++) {
+ auto vs2_name = absl::StrCat("v", kVs2 + i);
+ SetVectorRegisterValues<Vs2>(
+ {{vs2_name, vs2_span.subspan(vs2_size * i, vs2_size)}});
+ auto vs1_name = absl::StrCat("v", kVs1 + i);
+ SetVectorRegisterValues<Vs1>(
+ {{vs1_name, vs1_span.subspan(vs1_size * i, vs1_size)}});
+ }
+ for (int lmul_index = 0; lmul_index < 7; lmul_index++) {
+ for (int vstart_count = 0; vstart_count < 4; vstart_count++) {
+ for (int vlen_count = 0; vlen_count < 4; vlen_count++) {
+ ClearVectorRegisterGroup(kVd, 8);
+ int lmul8 = kLmul8Values[lmul_index];
+ int lmul8_vs2 = lmul8 * sizeof(Vs2) / byte_sew;
+ int num_values = lmul8 * kVectorLengthInBytes / (8 * byte_sew);
+ int vstart = 0;
+ if (vstart_count > 0) {
+ vstart = absl::Uniform(absl::IntervalOpen, bitgen_, 0, num_values);
+ }
+ // Set vlen, but leave vlen high at least once.
+ int vlen = 1024;
+ if (vlen_count > 0) {
+ vlen = absl::Uniform(absl::IntervalOpenClosed, bitgen_, vstart,
+ num_values);
+ }
+ num_values = std::min(num_values, vlen);
+ ASSERT_TRUE(vlen > vstart);
+ // Configure vector unit for different lmul settings.
+ uint32_t vtype = (kSewSettingsByByteSize[byte_sew] << 3) |
+ kLmulSettings[lmul_index];
+ ConfigureVectorUnit(vtype, vlen);
+ rv_vector_->set_vstart(vstart);
+
+ inst->Execute();
+ if ((lmul8_vs2 < 1) || (lmul8_vs2 > 64)) {
+ EXPECT_TRUE(rv_vector_->vector_exception());
+ rv_vector_->clear_vector_exception();
+ continue;
+ }
+
+ EXPECT_FALSE(rv_vector_->vector_exception());
+ EXPECT_EQ(rv_vector_->vstart(), 0);
+ auto dest_span = vreg_[kVd]->data_buffer()->Get<uint8_t>();
+ for (int i = 0; i < kVectorLengthInBytes * 8; i++) {
+ int mask_index = i >> 3;
+ int mask_offset = i & 0b111;
+ bool mask_value = ((kA5Mask[mask_index] >> mask_offset) & 0b1) != 0;
+ uint8_t inst_value = dest_span[i >> 3];
+ inst_value = (inst_value >> mask_offset) & 0b1;
+ if ((i >= vstart) && (i < num_values)) {
+ uint8_t expected_value =
+ operation(vs2_value[i], vs1_value[i], mask_value);
+ EXPECT_EQ(expected_value, inst_value) << absl::StrCat(
+ name, "[", i, "] != reg[][", i, "] lmul8(", lmul8,
+ ") vstart(", vstart, ") num_values(", num_values, ")");
+ } else {
+ EXPECT_EQ(0, inst_value) << absl::StrCat(
+ name, "[", i, "] 0 != reg[][", i, "] lmul8(", lmul8,
+ ") vstart(", vstart, ") num_values(", num_values, ")");
+ }
+ }
+ if (HasFailure()) return;
+ }
+ }
+ }
+ }
+
+ // Helper function for testing binary mask vector-vector instructions that do
+ // not use the mask bit.
+ template <typename Vs2, typename Vs1>
+ void BinaryMaskOpTestHelperVV(absl::string_view name, int sew,
+ Instruction *inst,
+ std::function<uint8_t(Vs2, Vs1)> operation) {
+ BinaryMaskOpWithMaskTestHelperVV<Vs2, Vs1>(
+ name, sew, inst,
+ [operation](Vs2 vs2, Vs1 vs1, bool mask_value) -> uint8_t {
+ if (mask_value) {
+ return operation(vs2, vs1);
+ }
+ return 0;
+ });
+ }
+
+ // Helper function for testing mask vector-scalar/immediate instructions that
+ // use the mask bit.
+ template <typename Vs2, typename Rs1>
+ void BinaryMaskOpWithMaskTestHelperVX(
+ absl::string_view name, int sew, Instruction *inst,
+ std::function<uint8_t(Vs2, Rs1, bool)> operation) {
+ int byte_sew = sew / 8;
+ if (byte_sew != sizeof(Vs2) && byte_sew != sizeof(Rs1)) {
+ FAIL() << name << ": selected element width != any operand types"
+ << "sew: " << sew << " Vs2: " << sizeof(Vs2)
+ << " Rs1: " << sizeof(Rs1);
+ return;
+ }
+ // Number of elements per vector register.
+ constexpr int vs2_size = kVectorLengthInBytes / sizeof(Vs2);
+ // Input values for 8 registers.
+ Vs2 vs2_value[vs2_size * 8];
+ auto vs2_span = Span<Vs2>(vs2_value);
+ AppendVectorRegisterOperands({kVs2}, {});
+ AppendRegisterOperands({kRs1Name}, {});
+ AppendVectorRegisterOperands({kVmask}, {kVd});
+ // Initialize input values.
+ FillArrayWithRandomValues<Vs2>(vs2_span);
+ SetVectorRegisterValues<uint8_t>(
+ {{kVmaskName, Span<const uint8_t>(kA5Mask)}});
+ for (int i = 0; i < 8; i++) {
+ auto vs2_name = absl::StrCat("v", kVs2 + i);
+ SetVectorRegisterValues<Vs2>(
+ {{vs2_name, vs2_span.subspan(vs2_size * i, vs2_size)}});
+ }
+ for (int lmul_index = 0; lmul_index < 7; lmul_index++) {
+ for (int vstart_count = 0; vstart_count < 4; vstart_count++) {
+ for (int vlen_count = 0; vlen_count < 4; vlen_count++) {
+ ClearVectorRegisterGroup(kVd, 8);
+ int lmul8 = kLmul8Values[lmul_index];
+ int lmul8_vs2 = lmul8 * sizeof(Vs2) / byte_sew;
+ int num_values = lmul8 * kVectorLengthInBytes / (8 * byte_sew);
+ int vstart = 0;
+ if (vstart_count > 0) {
+ vstart = absl::Uniform(absl::IntervalOpen, bitgen_, 0, num_values);
+ }
+ // Set vlen, but leave vlen high at least once.
+ int vlen = 1024;
+ if (vlen_count > 0) {
+ vlen = absl::Uniform(absl::IntervalOpenClosed, bitgen_, vstart,
+ num_values);
+ }
+ num_values = std::min(num_values, vlen);
+ ASSERT_TRUE(vlen > vstart);
+ // Configure vector unit for different lmul settings.
+ uint32_t vtype = (kSewSettingsByByteSize[byte_sew] << 3) |
+ kLmulSettings[lmul_index];
+ ConfigureVectorUnit(vtype, vlen);
+ rv_vector_->set_vstart(vstart);
+
+ // Generate a new rs1 value.
+ CheriotRegister::ValueType rs1_reg_value =
+ RandomValue<CheriotRegister::ValueType>();
+ SetRegisterValues<CheriotRegister::ValueType>(
+ {{kRs1Name, rs1_reg_value}});
+ // Cast the value to the appropriate width, sign-extending if need be.
+ Rs1 rs1_value = static_cast<Rs1>(
+ static_cast<typename SameSignedType<CheriotRegister::ValueType,
+ Rs1>::type>(rs1_reg_value));
+ inst->Execute();
+ if ((lmul8_vs2 < 1) || (lmul8_vs2 > 64)) {
+ EXPECT_TRUE(rv_vector_->vector_exception());
+ rv_vector_->clear_vector_exception();
+ continue;
+ }
+
+ EXPECT_FALSE(rv_vector_->vector_exception());
+ EXPECT_EQ(rv_vector_->vstart(), 0);
+ auto dest_span = vreg_[kVd]->data_buffer()->Get<uint8_t>();
+ for (int i = 0; i < kVectorLengthInBytes * 8; i++) {
+ int mask_index = i >> 3;
+ int mask_offset = i & 0b111;
+ bool mask_value = ((kA5Mask[mask_index] >> mask_offset) & 0b1) != 0;
+ uint8_t inst_value = dest_span[i >> 3];
+ inst_value = (inst_value >> mask_offset) & 0b1;
+ if ((i >= vstart) && (i < num_values)) {
+ uint8_t expected_value =
+ operation(vs2_value[i], rs1_value, mask_value);
+ EXPECT_EQ(expected_value, inst_value) << absl::StrCat(
+ name, "[i] != reg[0][", i, "] lmul8(", lmul8, ")");
+ } else {
+ EXPECT_EQ(0, inst_value) << absl::StrCat(
+ name, " 0 != reg[0][", i, "] lmul8(", lmul8, ")");
+ }
+ }
+ if (HasFailure()) return;
+ }
+ }
+ }
+ }
+
+ // Helper function for testing mask vector-vector instructions that do not
+ // use the mask bit.
+ template <typename Vs2, typename Vs1>
+ void BinaryMaskOpTestHelperVX(absl::string_view name, int sew,
+ Instruction *inst,
+ std::function<uint8_t(Vs2, Vs1)> operation) {
+ BinaryMaskOpWithMaskTestHelperVX<Vs2, Vs1>(
+ name, sew, inst,
+ [operation](Vs2 vs2, Vs1 vs1, bool mask_value) -> uint8_t {
+ if (mask_value) {
+ return operation(vs2, vs1);
+ }
+ return 0;
+ });
+ }
+
+ // Helper function to compute the rounding output bit.
+ template <typename T>
+ T RoundBits(int num_bits, T lost_bits) {
+ bool bit_d =
+ (num_bits == 0) ? false : ((lost_bits >> (num_bits - 1)) & 0b1) != 0;
+ bool bit_d_minus_1 =
+ (num_bits < 2) ? false : ((lost_bits >> (num_bits - 2)) & 0b1) != 0;
+ bool bits_d_minus_2_to_0 =
+ (num_bits < 3) ? false
+ : (lost_bits & ~(std::numeric_limits<uint64_t>::max()
+ << (num_bits - 2))) != 0;
+ bool bits_d_minus_1_to_0 =
+ (num_bits < 2) ? false
+ : (lost_bits & ~(std::numeric_limits<uint64_t>::max()
+ << (num_bits - 1))) != 0;
+ switch (rv_vector_->vxrm()) {
+ case 0:
+ return bit_d_minus_1;
+ case 1:
+ return bit_d_minus_1 & (bits_d_minus_2_to_0 | bit_d);
+ case 2:
+ return 0;
+ case 3:
+ return !bit_d & bits_d_minus_1_to_0;
+ default:
+ return 0;
+ }
+ }
+
+ CheriotVectorState *rv_vector() const { return rv_vector_; }
+ absl::Span<RVVectorRegister *> vreg() {
+ return absl::Span<RVVectorRegister *>(vreg_);
+ }
+ absl::Span<CheriotRegister *> creg() {
+ return absl::Span<CheriotRegister *>(creg_);
+ }
+ absl::BitGen &bitgen() { return bitgen_; }
+ Instruction *instruction() { return instruction_; }
+
+ protected:
+ CheriotRegister *creg_[32];
+ RVVectorRegister *vreg_[32];
+ RVFpRegister *freg_[32];
+ CheriotState *state_;
+ Instruction *instruction_;
+ Instruction *child_instruction_;
+ TaggedFlatDemandMemory *memory_;
+ CheriotVectorState *rv_vector_;
+ absl::BitGen bitgen_;
+};
+
+#endif // MPACT_RISCV_RISCV_TEST_RISCV_VECTOR_INSTRUCTIONS_TEST_BASE_H_
diff --git a/cheriot/test/riscv_cheriot_vector_memory_instructions_test.cc b/cheriot/test/riscv_cheriot_vector_memory_instructions_test.cc
new file mode 100644
index 0000000..17770bb
--- /dev/null
+++ b/cheriot/test/riscv_cheriot_vector_memory_instructions_test.cc
@@ -0,0 +1,2015 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cheriot/riscv_cheriot_vector_memory_instructions.h"
+
+#include <algorithm>
+#include <cstdint>
+#include <cstring>
+#include <functional>
+#include <ios>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#include "absl/functional/bind_front.h"
+#include "absl/log/log.h"
+#include "absl/numeric/bits.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
+#include "absl/types/span.h"
+#include "cheriot/cheriot_register.h"
+#include "cheriot/cheriot_state.h"
+#include "cheriot/cheriot_vector_state.h"
+#include "googlemock/include/gmock/gmock.h"
+#include "mpact/sim/generic/data_buffer.h"
+#include "mpact/sim/generic/immediate_operand.h"
+#include "mpact/sim/generic/instruction.h"
+#include "mpact/sim/generic/register.h"
+#include "mpact/sim/util/memory/tagged_flat_demand_memory.h"
+#include "riscv//riscv_register.h"
+
+// This file contains the test fixture and tests for testing RiscV vector
+// memory instructions.
+
+namespace {
+
+using ::absl::Span;
+using ::mpact::sim::cheriot::CheriotRegister;
+using ::mpact::sim::cheriot::CheriotState;
+using ::mpact::sim::cheriot::CheriotVectorState;
+using ::mpact::sim::generic::ImmediateOperand;
+using ::mpact::sim::generic::Instruction;
+using ::mpact::sim::generic::RegisterBase;
+using ::mpact::sim::riscv::RV32VectorDestinationOperand;
+using ::mpact::sim::riscv::RV32VectorSourceOperand;
+using ::mpact::sim::riscv::RVVectorRegister;
+using ::mpact::sim::util::TaggedFlatDemandMemory;
+using ::std::tuple;
+
+// Semantic functions.
+using ::mpact::sim::cheriot::VlChild;
+using ::mpact::sim::cheriot::VlIndexed;
+using ::mpact::sim::cheriot::Vlm;
+using ::mpact::sim::cheriot::VlRegister;
+using ::mpact::sim::cheriot::VlSegment;
+using ::mpact::sim::cheriot::VlSegmentChild;
+using ::mpact::sim::cheriot::VlSegmentIndexed;
+using ::mpact::sim::cheriot::VlSegmentStrided;
+using ::mpact::sim::cheriot::VlStrided;
+using ::mpact::sim::cheriot::VlUnitStrided;
+using ::mpact::sim::cheriot::Vsetvl;
+using ::mpact::sim::cheriot::VsIndexed;
+using ::mpact::sim::cheriot::Vsm;
+using ::mpact::sim::cheriot::VsRegister;
+using ::mpact::sim::cheriot::VsSegment;
+using ::mpact::sim::cheriot::VsSegmentIndexed;
+using ::mpact::sim::cheriot::VsSegmentStrided;
+using ::mpact::sim::cheriot::VsStrided;
+
+// Constants used in the tests.
+constexpr int kVectorLengthInBits = 512;
+constexpr int kVectorLengthInBytes = kVectorLengthInBits / 8;
+constexpr uint32_t kInstAddress = 0x1000;
+constexpr uint32_t kDataLoadAddress = 0x1'0000;
+constexpr uint32_t kDataStoreAddress = 0x8'0000;
+constexpr char kRs1Name[] = "c1";
+constexpr int kRs1 = 1;
+constexpr char kRs2Name[] = "c2";
+constexpr char kRs3Name[] = "c3";
+constexpr char kRdName[] = "c8";
+constexpr int kRd = 8;
+constexpr int kVmask = 1;
+constexpr char kVmaskName[] = "v1";
+constexpr int kVd = 8;
+constexpr int kVs1 = 16;
+constexpr int kVs2 = 24;
+
+// Setting bits and corresponding values for lmul and sew.
+constexpr int kLmulSettings[7] = {0b101, 0b110, 0b111, 0b000,
+ 0b001, 0b010, 0b011};
+constexpr int kLmul8Values[7] = {1, 2, 4, 8, 16, 32, 64};
+constexpr int kLmulSettingByLogSize[] = {0, 0b101, 0b110, 0b111,
+ 0b000, 0b001, 0b010, 0b011};
+constexpr int kSewSettings[4] = {0b000, 0b001, 0b010, 0b011};
+constexpr int kSewValues[4] = {1, 2, 4, 8};
+constexpr int kSewSettingsByByteSize[] = {0, 0b000, 0b001, 0, 0b010,
+ 0, 0, 0, 0b011};
+
+// Don't need to set every byte, as only the low bits are used for mask values.
+constexpr uint8_t kA5Mask[kVectorLengthInBytes] = {
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+};
+
+// Test fixture class. This class allows for more convenient manipulations
+// of instructions to test the semantic functions.
+class RiscVCheriotVInstructionsTest : public testing::Test {
+ public:
+ RiscVCheriotVInstructionsTest() {
+ memory_ = new TaggedFlatDemandMemory(8);
+ state_ = new CheriotState("test", memory_);
+ rv_vector_ = new CheriotVectorState(state_, kVectorLengthInBytes);
+ instruction_ = new Instruction(kInstAddress, state_);
+ instruction_->set_size(4);
+ child_instruction_ = new Instruction(kInstAddress, state_);
+ child_instruction_->set_size(4);
+ // Initialize a portion of memory with a known pattern.
+ auto *db = state_->db_factory()->Allocate(8192);
+ auto span = db->Get<uint8_t>();
+ for (int i = 0; i < 8192; i++) {
+ span[i] = i & 0xff;
+ }
+ memory_->Store(kDataLoadAddress - 4096, db);
+ db->DecRef();
+ for (int i = 1; i < 32; i++) {
+ creg_[i] =
+ state_->GetRegister<CheriotRegister>(absl::StrCat("c", i)).first;
+ creg_[i]->ResetMemoryRoot();
+ }
+ for (int i = 1; i < 32; i++) {
+ vreg_[i] =
+ state_->GetRegister<RVVectorRegister>(absl::StrCat("v", i)).first;
+ }
+ }
+
+ ~RiscVCheriotVInstructionsTest() override {
+ delete state_;
+ delete rv_vector_;
+ instruction_->DecRef();
+ child_instruction_->DecRef();
+ delete memory_;
+ }
+
+ // Creates immediate operands with the values from the vector and appends them
+ // to the given instruction.
+ template <typename T>
+ void AppendImmediateOperands(Instruction *inst,
+ const std::vector<T> &values) {
+ for (auto value : values) {
+ auto *src = new ImmediateOperand<T>(value);
+ inst->AppendSource(src);
+ }
+ }
+
+ // Creates immediate operands with the values from the vector and appends them
+ // to the default instruction.
+ template <typename T>
+ void AppendImmediateOperands(const std::vector<T> &values) {
+ AppendImmediateOperands<T>(instruction_, values);
+ }
+
+ // Creates source and destination scalar register operands for the registers
+ // named in the two vectors and append them to the given instruction.
+ void AppendRegisterOperands(Instruction *inst,
+ const std::vector<std::string> &sources,
+ const std::vector<std::string> &destinations) {
+ for (auto ®_name : sources) {
+ auto *reg = state_->GetRegister<CheriotRegister>(reg_name).first;
+ inst->AppendSource(reg->CreateSourceOperand());
+ }
+ for (auto ®_name : destinations) {
+ auto *reg = state_->GetRegister<CheriotRegister>(reg_name).first;
+ inst->AppendDestination(reg->CreateDestinationOperand(0));
+ }
+ }
+
+ // Creates source and destination scalar register operands for the registers
+ // named in the two vectors and append them to the default instruction.
+ void AppendRegisterOperands(const std::vector<std::string> &sources,
+ const std::vector<std::string> &destinations) {
+ AppendRegisterOperands(instruction_, sources, destinations);
+ }
+
+ // Returns the value of the named vector register.
+ template <typename T>
+ T GetRegisterValue(absl::string_view vreg_name) {
+ auto *reg = state_->GetRegister<CheriotRegister>(vreg_name).first;
+ return reg->data_buffer()->Get<T>();
+ }
+
+ // named register and sets it to the corresponding value.
+ template <typename T>
+ void SetRegisterValues(
+ const std::vector<tuple<std::string, const T>> &values) {
+ for (auto &[reg_name, value] : values) {
+ auto *reg = state_->GetRegister<CheriotRegister>(reg_name).first;
+ auto *db = state_->db_factory()->Allocate<CheriotRegister::ValueType>(1);
+ db->Set<T>(0, value);
+ reg->SetDataBuffer(db);
+ db->DecRef();
+ }
+ }
+
+ // Creates source and destination scalar register operands for the registers
+ // named in the two vectors and append them to the given instruction.
+ void AppendVectorRegisterOperands(Instruction *inst,
+ const std::vector<int> &sources,
+ const std::vector<int> &destinations) {
+ for (auto ®_no : sources) {
+ std::vector<RegisterBase *> reg_vec;
+ for (int i = 0; (i < 8) && (i + reg_no < 32); i++) {
+ std::string reg_name = absl::StrCat("v", i + reg_no);
+ reg_vec.push_back(
+ state_->GetRegister<RVVectorRegister>(reg_name).first);
+ }
+ auto *op = new RV32VectorSourceOperand(
+ absl::Span<RegisterBase *>(reg_vec), absl::StrCat("v", reg_no));
+ inst->AppendSource(op);
+ }
+ for (auto ®_no : destinations) {
+ std::vector<RegisterBase *> reg_vec;
+ for (int i = 0; (i < 8) && (i + reg_no < 32); i++) {
+ std::string reg_name = absl::StrCat("v", i + reg_no);
+ reg_vec.push_back(
+ state_->GetRegister<RVVectorRegister>(reg_name).first);
+ }
+ auto *op = new RV32VectorDestinationOperand(
+ absl::Span<RegisterBase *>(reg_vec), 0, absl::StrCat("v", reg_no));
+ inst->AppendDestination(op);
+ }
+ }
+ // Creates source and destination scalar register operands for the registers
+ // named in the two vectors and append them to the default instruction.
+ void AppendVectorRegisterOperands(const std::vector<int> &sources,
+ const std::vector<int> &destinations) {
+ AppendVectorRegisterOperands(instruction_, sources, destinations);
+ }
+
+ // Returns the value of the named vector register.
+ template <typename T>
+ T GetVectorRegisterValue(absl::string_view reg_name) {
+ auto *reg = state_->GetRegister<RVVectorRegister>(reg_name).first;
+ return reg->data_buffer()->Get<T>(0);
+ }
+
+ // Set a vector register value. Takes a vector of tuples of register names and
+ // spans of values, fetches each register and sets it to the corresponding
+ // value.
+ template <typename T>
+ void SetVectorRegisterValues(
+ const std::vector<tuple<std::string, Span<const T>>> &values) {
+ for (auto &[vreg_name, span] : values) {
+ auto *vreg = state_->GetRegister<RVVectorRegister>(vreg_name).first;
+ auto *db = state_->db_factory()->MakeCopyOf(vreg->data_buffer());
+ db->template Set<T>(span);
+ vreg->SetDataBuffer(db);
+ db->DecRef();
+ }
+ }
+
+ // Initializes the semantic function of the instruction object.
+ void SetSemanticFunction(Instruction *inst,
+ Instruction::SemanticFunction fcn) {
+ inst->set_semantic_function(fcn);
+ }
+
+ // Initializes the semantic function for the default instruction.
+ void SetSemanticFunction(Instruction::SemanticFunction fcn) {
+ instruction_->set_semantic_function(fcn);
+ }
+
+ // Sets the default child instruction as the child of the default instruction.
+ void SetChildInstruction() { instruction_->AppendChild(child_instruction_); }
+
+ // Initializes the semantic function for the default child instruction.
+ void SetChildSemanticFunction(Instruction::SemanticFunction fcn) {
+ child_instruction_->set_semantic_function(fcn);
+ }
+
+ // Configure the vector unit according to the vtype and vlen values.
+ void ConfigureVectorUnit(uint32_t vtype, uint32_t vlen) {
+ Instruction *inst = new Instruction(state_);
+ AppendImmediateOperands<uint32_t>(inst, {vlen, vtype});
+ SetSemanticFunction(inst, absl::bind_front(&Vsetvl, true, false));
+ inst->Execute(nullptr);
+ inst->DecRef();
+ }
+
+ template <typename T>
+ T ComputeValue(int address) {
+ T value = 0;
+ uint8_t *ptr = reinterpret_cast<uint8_t *>(&value);
+ for (int j = 0; j < sizeof(T); j++) {
+ ptr[j] = (address + j) & 0xff;
+ }
+ return value;
+ }
+
+ template <typename T>
+ void VectorLoadUnitStridedHelper() {
+ // Set up instructions.
+ AppendRegisterOperands({kRs1Name}, {});
+ AppendVectorRegisterOperands({kVmask}, {});
+ SetSemanticFunction(absl::bind_front(&VlUnitStrided,
+ /*element_width*/ sizeof(T)));
+ // Add the child instruction that performs the register write-back.
+ SetChildInstruction();
+ SetChildSemanticFunction(&VlChild);
+ AppendVectorRegisterOperands(child_instruction_, {}, {kVd});
+ // Set up register values.
+ SetRegisterValues<uint32_t>({{kRs1Name, kDataLoadAddress}});
+ SetVectorRegisterValues<uint8_t>(
+ {{kVmaskName, Span<const uint8_t>(kA5Mask)}});
+ // Iterate over different lmul values.
+ for (int lmul_index = 0; lmul_index < 7; lmul_index++) {
+ uint32_t vtype =
+ (kSewSettingsByByteSize[sizeof(T)] << 3) | kLmulSettings[lmul_index];
+ int lmul8 = kLmul8Values[lmul_index];
+ int num_values = kVectorLengthInBytes * lmul8 / (sizeof(T) * 8);
+ ConfigureVectorUnit(vtype, /*vlen*/ 1024);
+ // Execute instruction.
+ instruction_->Execute(nullptr);
+
+ // Check register values.
+ int count = 0;
+ for (int reg = kVd; reg < kVd + 8; reg++) {
+ auto span = vreg_[reg]->data_buffer()->Get<T>();
+ for (int i = 0; i < kVectorLengthInBytes / sizeof(T); i++) {
+ int mask_index = count / 8;
+ int mask_offset = count % 8;
+ bool mask = (kA5Mask[mask_index] >> mask_offset) & 0x1;
+ if (mask && (count < num_values)) {
+ // First compute the expected value, then compare it.
+ T value = ComputeValue<T>(4096 + count * sizeof(T));
+ EXPECT_EQ(value, span[i])
+ << "element size " << sizeof(T) << " LMUL8 " << lmul8
+ << " Count " << count << " Reg " << reg << " value " << i;
+ } else {
+ // The remainder of the values should be zero.
+ EXPECT_EQ(0, span[i])
+ << "element size " << sizeof(T) << " LMUL8 " << lmul8
+ << " Count " << count << " Reg " << reg << " value " << i;
+ }
+ count++;
+ }
+ }
+ }
+ }
+
+ template <typename T>
+ void VectorLoadStridedHelper() {
+ const int strides[5] = {1, 4, 0, -1, -3};
+ // Set up instructions.
+ AppendRegisterOperands({kRs1Name, kRs2Name}, {});
+ AppendVectorRegisterOperands({kVmask}, {});
+ SetSemanticFunction(absl::bind_front(&VlStrided,
+ /*element_width*/ sizeof(T)));
+ // Add the child instruction that performs the register write-back.
+ SetChildInstruction();
+ SetChildSemanticFunction(&VlChild);
+ AppendVectorRegisterOperands(child_instruction_, {}, {kVd});
+ // Set up register values.
+ SetRegisterValues<uint32_t>({{kRs1Name, kDataLoadAddress}});
+ SetVectorRegisterValues<uint8_t>(
+ {{kVmaskName, Span<const uint8_t>(kA5Mask)}});
+ // Iterate over different lmul values.
+ for (int lmul_index = 0; lmul_index < 7; lmul_index++) {
+ // Try different strides.
+ for (int s = 0; s < 5; s++) {
+ int32_t stride = strides[s] * sizeof(T);
+ SetRegisterValues<int32_t>({{kRs2Name, stride}});
+ // Configure vector unit.
+ uint32_t vtype = (kSewSettingsByByteSize[sizeof(T)] << 3) |
+ kLmulSettings[lmul_index];
+ int lmul8 = kLmul8Values[lmul_index];
+ int num_values = kVectorLengthInBytes * lmul8 / (sizeof(T) * 8);
+ ConfigureVectorUnit(vtype, /*vlen*/ 1024);
+ // Execute instruction.
+ instruction_->Execute(nullptr);
+
+ // Check register values.
+ int count = 0;
+ for (int reg = kVd; reg < kVd + 8; reg++) {
+ auto span = vreg_[reg]->data_buffer()->Get<T>();
+ for (int i = 0; i < kVectorLengthInBytes / sizeof(T); i++) {
+ int mask_index = count / 8;
+ int mask_offset = count % 8;
+ bool mask = (kA5Mask[mask_index] >> mask_offset) & 0x1;
+ if (mask && (count < num_values)) {
+ // First compute the expected value, then compare it.
+ T value = ComputeValue<T>(4096 + count * stride);
+ EXPECT_EQ(value, span[i])
+ << "element size " << sizeof(T) << " stride: " << stride
+ << " LMUL8 " << lmul8 << " Count " << count << " Reg " << reg
+ << " value " << i;
+ } else {
+ // The remainder of the values should be zero.
+ EXPECT_EQ(0, span[i])
+ << "element size " << sizeof(T) << " stride: " << stride
+ << " LMUL8 " << lmul8 << " Count " << count << " Reg " << reg
+ << " value " << i;
+ }
+ count++;
+ }
+ }
+ }
+ }
+ }
+
+ template <typename T>
+ T IndexValue(int i) {
+ T offset = ~i & 0b1111;
+ T val = (i & ~0b1111) | offset;
+ return val * sizeof(T);
+ }
+
+ // Helper function for testing vector load indexed instructions.
+ template <typename IndexType, typename ValueType>
+ void VectorLoadIndexedHelper() {
+ // Set up instructions.
+ AppendRegisterOperands({kRs1Name}, {});
+ AppendVectorRegisterOperands({kVs2, kVmask}, {});
+ SetSemanticFunction(absl::bind_front(&VlIndexed,
+ /*index_width*/ sizeof(IndexType)));
+ // Add the child instruction that performs the register write-back.
+ SetChildInstruction();
+ SetChildSemanticFunction(&VlChild);
+ AppendVectorRegisterOperands(child_instruction_, {}, {kVd});
+ // Set up register values.
+ SetRegisterValues<uint32_t>({{kRs1Name, kDataLoadAddress}});
+ SetVectorRegisterValues<uint8_t>(
+ {{kVmaskName, Span<const uint8_t>(kA5Mask)}});
+
+ // Iterate over different lmul values.
+ for (int lmul_index = 0; lmul_index < 7; lmul_index++) {
+ // Configure vector unit.
+ uint32_t vtype = (kSewSettingsByByteSize[sizeof(ValueType)] << 3) |
+ kLmulSettings[lmul_index];
+ ConfigureVectorUnit(vtype, /*vlen*/ 1024);
+ int lmul8 = kLmul8Values[lmul_index];
+ int index_emul8 = lmul8 * sizeof(IndexType) / sizeof(ValueType);
+
+ if ((index_emul8 == 0) || (index_emul8 > 64)) {
+ // The index vector length is illegal.
+ instruction_->Execute(nullptr);
+ EXPECT_TRUE(rv_vector_->vector_exception());
+ rv_vector_->clear_vector_exception();
+ continue;
+ }
+ EXPECT_FALSE(rv_vector_->vector_exception());
+
+ int num_values = kVectorLengthInBytes * lmul8 / (sizeof(ValueType) * 8);
+ // Set up index vector values.
+ int values_per_reg = kVectorLengthInBytes / sizeof(IndexType);
+ for (int i = 0; i < num_values; i++) {
+ int reg_index = kVs2 + i / values_per_reg;
+ int reg_offset = i % values_per_reg;
+ auto index_span = vreg_[reg_index]->data_buffer()->Get<IndexType>();
+ index_span[reg_offset] = IndexValue<ValueType>(i);
+ }
+ // Execute instruction.
+ instruction_->Execute(nullptr);
+
+ // Check register values.
+ int count = 0;
+ for (int reg = kVd; reg < kVd + 8; reg++) {
+ auto span = vreg_[reg]->data_buffer()->Get<ValueType>();
+ for (int i = 0; i < kVectorLengthInBytes / sizeof(ValueType); i++) {
+ int mask_index = count / 8;
+ int mask_offset = count % 8;
+ bool mask = (kA5Mask[mask_index] >> mask_offset) & 0x1;
+ if (mask && (count < num_values)) {
+ // Compare expected value.
+ auto value =
+ ComputeValue<ValueType>(4096 + IndexValue<ValueType>(count));
+ EXPECT_EQ(value, span[i])
+ << "element size " << sizeof(ValueType) << " index: " << index
+ << " LMUL8 " << lmul8 << " Count " << count << " reg " << reg;
+ } else {
+ // The remainder of the values should be zero.
+ EXPECT_EQ(0, span[i])
+ << "element size " << sizeof(ValueType) << " index: " << index
+ << " LMUL8 " << lmul8 << " count " << count << " reg " << reg;
+ }
+ count++;
+ }
+ }
+ }
+ }
+
+ // Helper function to test vector load segment strided instructions.
+ template <typename T>
+ void VectorLoadSegmentHelper() {
+ // Set up instructions.
+ AppendRegisterOperands({kRs1Name}, {});
+ AppendVectorRegisterOperands({kVmask}, {});
+ AppendRegisterOperands({kRs3Name}, {});
+ SetVectorRegisterValues<uint8_t>(
+ {{kVmaskName, Span<const uint8_t>(kA5Mask)}});
+ SetSemanticFunction(absl::bind_front(&VlSegment,
+ /*element_width*/ sizeof(T)));
+ // Add the child instruction that performs the register write-back.
+ SetChildInstruction();
+ SetChildSemanticFunction(
+ absl::bind_front(&VlSegmentChild, /*element_width*/ sizeof(T)));
+ AppendRegisterOperands(child_instruction_, {kRs3Name}, {});
+ AppendVectorRegisterOperands(child_instruction_, {}, {kVd});
+ // Set up register values.
+ SetRegisterValues<uint32_t>({{kRs1Name, kDataLoadAddress}});
+ // Iterate over legal values in the nf field.
+ for (int nf = 1; nf < 8; nf++) {
+ int num_fields = nf + 1;
+ // Iterate over different lmul values.
+ SetRegisterValues<int32_t>({{kRs3Name, nf}});
+ for (int lmul_index = 0; lmul_index < 7; lmul_index++) {
+ // Configure vector unit, set the sew to the element width.
+ uint32_t vtype = (kSewSettingsByByteSize[sizeof(T)] << 3) |
+ kLmulSettings[lmul_index];
+ int lmul8 = kLmul8Values[lmul_index];
+ int num_values = kVectorLengthInBytes * lmul8 / (sizeof(T) * 8);
+ ConfigureVectorUnit(vtype, /*vlen*/ 1024);
+ // Execute instruction.
+ instruction_->Execute(nullptr);
+
+ if (lmul8 * num_fields > 64) {
+ EXPECT_TRUE(rv_vector_->vector_exception());
+ rv_vector_->clear_vector_exception();
+ continue;
+ }
+ EXPECT_FALSE(rv_vector_->vector_exception());
+
+ // Check register values.
+ int count = 0;
+ // Fields are in consecutive (groups of) registers. First compute the
+ // number of registers for each field.
+ int regs_per_field = ::std::max(1, lmul8 / 8);
+ for (int field = 0; field < num_fields; field++) {
+ int start_reg = kVd + field * regs_per_field;
+ int max_reg = start_reg + regs_per_field;
+ count = 0;
+ for (int reg = start_reg; reg < max_reg; reg++) {
+ auto span = vreg_[reg]->data_buffer()->Get<T>();
+ int num_reg_elements =
+ std::min(kVectorLengthInBytes / sizeof(T),
+ kVectorLengthInBytes * lmul8 / (sizeof(T) * 8));
+ for (int i = 0; i < num_reg_elements; i++) {
+ int mask_index = count / 8;
+ int mask_offset = count % 8;
+ bool mask = (kA5Mask[mask_index] >> mask_offset) & 0x1;
+ if (mask && (count < num_values)) {
+ int address =
+ 4096 + count * sizeof(T) * num_fields + field * sizeof(T);
+ T value = ComputeValue<T>(address);
+ EXPECT_EQ(value, span[i])
+ << "element size " << sizeof(T) << " LMUL8 " << lmul8
+ << " Count " << count << " Reg " << reg << " value " << i;
+ } else {
+ // The remainder of the values should be zero.
+ EXPECT_EQ(0, span[i])
+ << "element size " << sizeof(T) << " LMUL8 " << lmul8
+ << " Count " << count << " Reg " << reg << " value " << i;
+ }
+ count++;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Helper function to test vector load segment strided instructions.
+ template <typename T>
+ void VectorLoadStridedSegmentHelper() {
+ const int strides[5] = {1, 4, 0, -1, -3};
+ // Set up instructions.
+ // Base address and stride.
+ AppendRegisterOperands({kRs1Name, kRs2Name}, {});
+ // Vector mask register.
+ AppendVectorRegisterOperands({kVmask}, {});
+ // Operand to hold the number of fields.
+ AppendRegisterOperands({kRs3Name}, {});
+ // Initialize the mask.
+ SetVectorRegisterValues<uint8_t>(
+ {{kVmaskName, Span<const uint8_t>(kA5Mask)}});
+ // Bind semantic function.
+ SetSemanticFunction(absl::bind_front(&VlSegmentStrided,
+ /*element_width*/ sizeof(T)));
+ // Add the child instruction that performs the register write-back.
+ SetChildInstruction();
+ SetChildSemanticFunction(
+ absl::bind_front(&VlSegmentChild, /*element_width*/ sizeof(T)));
+ // Number of fields.
+ AppendRegisterOperands(child_instruction_, {kRs3Name}, {});
+ // Destination vector register operand.
+ AppendVectorRegisterOperands(child_instruction_, {}, {kVd});
+
+ // Set up register values.
+ // Base address.
+ SetRegisterValues<uint32_t>({{kRs1Name, kDataLoadAddress}});
+ // Iterate over legal values in the nf field.
+ for (int nf = 1; nf < 8; nf++) {
+ int num_fields = nf + 1;
+ // Set the number of fields.
+ SetRegisterValues<int32_t>({{kRs3Name, nf}});
+ // Iterate over different lmul values.
+ for (int lmul_index = 0; lmul_index < 7; lmul_index++) {
+ // Configure vector unit, set the sew to the element width.
+ uint32_t vtype = (kSewSettingsByByteSize[sizeof(T)] << 3) |
+ kLmulSettings[lmul_index];
+ int lmul8 = kLmul8Values[lmul_index];
+ int num_values = kVectorLengthInBytes * lmul8 / (sizeof(T) * 8);
+ ConfigureVectorUnit(vtype, /*vlen*/ 1024);
+
+ // Try different strides.
+ for (int s = 0; s < 5; s++) {
+ int32_t stride = strides[s] * num_fields * sizeof(T);
+ // Set the stride.
+ SetRegisterValues<int32_t>({{kRs2Name, stride}});
+ // Execute instruction.
+ instruction_->Execute(nullptr);
+
+ if (lmul8 * num_fields > 64) {
+ EXPECT_TRUE(rv_vector_->vector_exception());
+ rv_vector_->clear_vector_exception();
+ continue;
+ }
+ EXPECT_FALSE(rv_vector_->vector_exception());
+
+ // Check register values.
+ // Fields are in consecutive (groups of) registers. First compute the
+ // number of registers for each field.
+ int regs_per_field = ::std::max(1, lmul8 / 8);
+ for (int field = 0; field < num_fields; field++) {
+ int start_reg = kVd + field * regs_per_field;
+ int max_reg = start_reg + regs_per_field;
+ int count = 0;
+ for (int reg = start_reg; reg < max_reg; reg++) {
+ auto span = vreg_[reg]->data_buffer()->Get<T>();
+ int num_reg_elements =
+ std::min(kVectorLengthInBytes / sizeof(T),
+ kVectorLengthInBytes * lmul8 / (sizeof(T) * 8));
+ for (int i = 0; i < num_reg_elements; i++) {
+ int mask_index = count / 8;
+ int mask_offset = count % 8;
+ bool mask = (kA5Mask[mask_index] >> mask_offset) & 0x1;
+ if (mask && (count < num_values)) {
+ // First compute the expected value, then compare it.
+ int address = 4096 + stride * count + field * sizeof(T);
+ T value = ComputeValue<T>(address);
+
+ EXPECT_EQ(value, span[i])
+ << "element size " << sizeof(T) << " stride: " << stride
+ << " LMUL8 " << lmul8 << " Count " << count << " Reg "
+ << reg << " value " << i;
+ } else {
+ // The remainder of the values should be zero.
+ EXPECT_EQ(0, span[i])
+ << "element size " << sizeof(T) << " stride: " << stride
+ << " LMUL8 " << lmul8 << " Count " << count << " Reg "
+ << reg << " value " << i;
+ }
+ count++;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Helper function to test vector load segment indexed instructions.
+ template <typename IndexType, typename ValueType>
+ void VectorLoadIndexedSegmentHelper() {
+ // Set up instructions.
+ // Base address and stride.
+ AppendRegisterOperands({kRs1Name}, {});
+ // Vector mask register.
+ AppendVectorRegisterOperands({kVs2, kVmask}, {});
+ // Operand to hold the number of fields.
+ AppendRegisterOperands({kRs3Name}, {});
+ // Initialize the mask.
+ SetVectorRegisterValues<uint8_t>(
+ {{kVmaskName, Span<const uint8_t>(kA5Mask)}});
+ // Bind semantic function.
+ SetSemanticFunction(absl::bind_front(&VlSegmentIndexed,
+ /*element_width*/ sizeof(IndexType)));
+ // Add the child instruction that performs the register write-back.
+ SetChildInstruction();
+ SetChildSemanticFunction(
+ absl::bind_front(&VlSegmentChild,
+ /*element_width*/ sizeof(ValueType)));
+ // Number of fields.
+ AppendRegisterOperands(child_instruction_, {kRs3Name}, {});
+ // Destination vector register operand.
+ AppendVectorRegisterOperands(child_instruction_, {}, {kVd});
+
+ // Set up register values.
+ // Base address.
+ SetRegisterValues<uint32_t>({{kRs1Name, kDataLoadAddress}});
+ // Iterate over legal values in the nf field.
+ for (int nf = 1; nf < 8; nf++) {
+ int num_fields = nf + 1;
+ // Set the number of fields.
+ SetRegisterValues<int32_t>({{kRs3Name, nf}});
+ // Iterate over different lmul values.
+ for (int lmul_index = 0; lmul_index < 7; lmul_index++) {
+ rv_vector_->clear_vector_exception();
+ // Configure vector unit, set the sew to the element width.
+ uint32_t vtype = (kSewSettingsByByteSize[sizeof(ValueType)] << 3) |
+ kLmulSettings[lmul_index];
+ int lmul8 = kLmul8Values[lmul_index];
+ ConfigureVectorUnit(vtype, /*vlen*/ 1024);
+ int index_emul8 = lmul8 * sizeof(IndexType) / sizeof(ValueType);
+ int num_values = kVectorLengthInBytes * lmul8 / (sizeof(ValueType) * 8);
+
+ if ((index_emul8 == 0) || (index_emul8 > 64)) {
+ // The index vector length is illegal.
+ instruction_->Execute(nullptr);
+ EXPECT_TRUE(rv_vector_->vector_exception());
+ continue;
+ }
+ if (lmul8 * num_fields > 64) {
+ instruction_->Execute(nullptr);
+ EXPECT_TRUE(rv_vector_->vector_exception());
+ continue;
+ }
+
+ // Set up index vector values.
+ int values_per_reg = kVectorLengthInBytes / sizeof(IndexType);
+ for (int i = 0; i < num_values; i++) {
+ int reg_index = kVs2 + i / values_per_reg;
+ int reg_offset = i % values_per_reg;
+ auto index_span = vreg_[reg_index]->data_buffer()->Get<IndexType>();
+ index_span[reg_offset] = IndexValue<ValueType>(i);
+ }
+
+ // Execute instruction.
+ instruction_->Execute(nullptr);
+ EXPECT_FALSE(rv_vector_->vector_exception());
+
+ // Check register values.
+ // Fields are in consecutive (groups of) registers. First compute the
+ // number of registers for each field.
+ int regs_per_field = ::std::max(1, lmul8 / 8);
+ for (int field = 0; field < num_fields; field++) {
+ int start_reg = kVd + field * regs_per_field;
+ int max_reg = start_reg + regs_per_field;
+ int count = 0;
+ for (int reg = start_reg; reg < max_reg; reg++) {
+ auto span = vreg_[reg]->data_buffer()->Get<ValueType>();
+ int num_reg_elements = std::min(
+ kVectorLengthInBytes / sizeof(ValueType),
+ kVectorLengthInBytes * lmul8 / (sizeof(ValueType) * 8));
+ for (int i = 0; i < num_reg_elements; i++) {
+ int mask_index = count / 8;
+ int mask_offset = count % 8;
+ bool mask = (kA5Mask[mask_index] >> mask_offset) & 0x1;
+ if (mask && (count < num_values)) {
+ // First compute the expected value, then compare it.
+ int address = 4096 + IndexValue<IndexType>(count) +
+ field * sizeof(ValueType);
+ ValueType value = ComputeValue<ValueType>(address);
+
+ EXPECT_EQ(value, span[i])
+ << "element size " << sizeof(ValueType) << " LMUL8 "
+ << lmul8 << " Count " << count << " Reg " << reg
+ << " value " << i;
+ } else {
+ // The remainder of the values should be zero.
+ EXPECT_EQ(0, span[i])
+ << "element size " << sizeof(ValueType) << " LMUL8 "
+ << lmul8 << " Count " << count << " Reg " << reg
+ << " value " << i;
+ }
+ count++;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ template <typename T>
+ void VectorStoreStridedHelper() {
+ const int strides[5] = {1, 4, 8, -1, -3};
+ // Set up instructions.
+ AppendVectorRegisterOperands({kVs1}, {});
+ AppendRegisterOperands({kRs1Name, kRs2Name}, {});
+ AppendVectorRegisterOperands({kVmask}, {});
+ SetSemanticFunction(absl::bind_front(&VsStrided,
+ /*element_width*/ sizeof(T)));
+ // Set up register values.
+ SetRegisterValues<uint32_t>({{kRs1Name, kDataStoreAddress}});
+ SetVectorRegisterValues<uint8_t>(
+ {{kVmaskName, Span<const uint8_t>(kA5Mask)}});
+ // Set the store data register elements to be consecutive integers.
+ for (int reg = 0; reg < 8; reg++) {
+ auto reg_span = vreg_[reg + kVs1]->data_buffer()->Get<T>();
+ for (int i = 0; i < reg_span.size(); i++) {
+ reg_span[i] = static_cast<T>(reg * reg_span.size() + i + 1);
+ }
+ }
+ auto *clear_mem_db = state_->db_factory()->Allocate<T>(0x8000);
+ memset(clear_mem_db->raw_ptr(), 0, 0x8000);
+ // Iterate over different lmul values.
+ for (int lmul_index = 0; lmul_index < 7; lmul_index++) {
+ // Try different strides.
+ for (int s = 0; s < 5; s++) {
+ int32_t stride = strides[s] * sizeof(T);
+ SetRegisterValues<int32_t>({{kRs2Name, stride}});
+ // Configure vector unit.
+ uint32_t vtype = (kSewSettingsByByteSize[sizeof(T)] << 3) |
+ kLmulSettings[lmul_index];
+ int lmul8 = kLmul8Values[lmul_index];
+ int num_values = kVectorLengthInBytes * lmul8 / (sizeof(T) * 8);
+ ConfigureVectorUnit(vtype, /*vlen*/ 1024);
+ // Execute instruction.
+ instruction_->Execute(nullptr);
+
+ // Check memory values.
+ auto *data_db = state_->db_factory()->Allocate<T>(1);
+ uint64_t base = kDataStoreAddress;
+ T value = 1;
+ for (int i = 0; i < 8 * kVectorLengthInBytes / sizeof(T); i++) {
+ data_db->template Set<T>(0, 0);
+ state_->DbgLoadMemory(base, data_db);
+ int mask_index = i / 8;
+ int mask_offset = i % 8;
+ bool mask = (kA5Mask[mask_index] >> mask_offset) & 0x1;
+ if (mask && (i < num_values)) {
+ EXPECT_EQ(data_db->template Get<T>(0), static_cast<T>(value))
+ << "index: " << i << " element_size: " << sizeof(T)
+ << " lmul8: " << lmul8 << " stride: " << stride;
+ } else {
+ EXPECT_EQ(data_db->template Get<T>(0), 0) << "index: " << i;
+ }
+ base += stride;
+ value++;
+ }
+ data_db->DecRef();
+ // Clear memory.
+ state_->DbgStoreMemory(kDataStoreAddress - 0x4000, clear_mem_db);
+ }
+ }
+ clear_mem_db->DecRef();
+ }
+
+ template <typename IndexType, typename ValueType>
+ void VectorStoreIndexedHelper() {
+ // Set up instructions.
+ AppendVectorRegisterOperands({kVs1}, {});
+ AppendRegisterOperands({kRs1Name}, {});
+ AppendVectorRegisterOperands({kVs2, kVmask}, {});
+ SetSemanticFunction(absl::bind_front(&VsIndexed,
+ /*index_width*/ sizeof(IndexType)));
+
+ // Set up register values.
+ SetRegisterValues<uint32_t>({{kRs1Name, kDataStoreAddress}});
+ SetVectorRegisterValues<uint8_t>(
+ {{kVmaskName, Span<const uint8_t>(kA5Mask)}});
+
+ int values_per_reg = kVectorLengthInBytes / sizeof(ValueType);
+ int index_values_per_reg = kVectorLengthInBytes / sizeof(IndexType);
+ for (int reg = 0; reg < 8; reg++) {
+ for (int i = 0; i < values_per_reg; i++) {
+ vreg_[kVs1 + reg]->data_buffer()->Set<ValueType>(
+ i, reg * values_per_reg + i);
+ }
+ }
+
+ auto *data_db = state_->db_factory()->Allocate<ValueType>(1);
+ // Iterate over different lmul values.
+ for (int lmul_index = 0; lmul_index < 7; lmul_index++) {
+ // Configure vector unit.
+ uint32_t vtype = (kSewSettingsByByteSize[sizeof(ValueType)] << 3) |
+ kLmulSettings[lmul_index];
+ ConfigureVectorUnit(vtype, /*vlen*/ 1024);
+ int lmul8 = kLmul8Values[lmul_index];
+ int index_emul8 = lmul8 * sizeof(IndexType) / sizeof(ValueType);
+ int num_values = kVectorLengthInBytes * lmul8 / (sizeof(ValueType) * 8);
+
+ // Skip if the number of values is greater than the offset representation.
+ // This only happens for uint8_t.
+ if (num_values > 256) continue;
+
+ // Check the index vector length.
+ if ((index_emul8 == 0) || (index_emul8 > 64)) {
+ // The index vector length is illegal.
+ instruction_->Execute(nullptr);
+ EXPECT_TRUE(rv_vector_->vector_exception());
+ rv_vector_->clear_vector_exception();
+ continue;
+ }
+
+ for (int i = 0; i < num_values; i++) {
+ int reg = i / index_values_per_reg;
+ int element = i % index_values_per_reg;
+ vreg_[kVs2 + reg]->data_buffer()->Set<IndexType>(
+ element, IndexValue<IndexType>(i));
+ }
+
+ // Execute instruction.
+ instruction_->Execute(nullptr);
+
+ // Check results.
+ EXPECT_FALSE(rv_vector_->vector_exception());
+
+ // Check register values.
+ for (int i = 0; i < 8 * kVectorLengthInBytes / sizeof(ValueType); i++) {
+ uint64_t address = kDataStoreAddress + IndexValue<IndexType>(i);
+ state_->DbgLoadMemory(address, data_db);
+ int mask_index = i / 8;
+ int mask_offset = i % 8;
+ bool mask = (kA5Mask[mask_index] >> mask_offset) & 0x1;
+ if (mask && (i < num_values)) {
+ EXPECT_EQ(data_db->template Get<ValueType>(0), i)
+ << "reg[" << i / values_per_reg << "][" << i % values_per_reg
+ << "]";
+ } else {
+ EXPECT_EQ(data_db->template Get<ValueType>(0), 0)
+ << "reg[" << i / values_per_reg << "][" << i % values_per_reg
+ << "]";
+ }
+ // Clear the memory location.
+ data_db->template Set<ValueType>(0, 0);
+ state_->DbgStoreMemory(address, data_db);
+ }
+ }
+ data_db->DecRef();
+ }
+
+ // Helper function to test vector load segment strided instructions.
+ template <typename T>
+ void VectorStoreSegmentHelper() {
+ // Set up instructions.
+ // Store data register.
+ AppendVectorRegisterOperands({kVs1}, {});
+ // Base address and stride.
+ AppendRegisterOperands({kRs1Name}, {});
+ // Vector mask register.
+ AppendVectorRegisterOperands({kVmask}, {});
+ // Operand to hold the number of fields.
+ AppendRegisterOperands({kRs3Name}, {});
+ // Bind semantic function.
+ SetSemanticFunction(absl::bind_front(&VsSegment,
+ /*element_width*/ sizeof(T)));
+
+ // Set up register values.
+ // Set the store data register elements to be consecutive integers.
+ for (int reg = 0; reg < 8; reg++) {
+ auto reg_span = vreg_[reg + kVs1]->data_buffer()->Get<T>();
+ for (int i = 0; i < reg_span.size(); i++) {
+ reg_span[i] = static_cast<T>(reg * reg_span.size() + i + 1);
+ }
+ }
+ // Base address.
+ SetRegisterValues<uint32_t>({{kRs1Name, kDataStoreAddress}});
+ // Initialize the mask.
+ SetVectorRegisterValues<uint8_t>(
+ {{kVmaskName, Span<const uint8_t>(kA5Mask)}});
+
+ int num_values_per_register = kVectorLengthInBytes / sizeof(T);
+ // Can load all the data in one load, so set the data_db size accordingly.
+ auto *data_db =
+ state_->db_factory()->Allocate<uint8_t>(kVectorLengthInBytes * 8);
+ // Iterate over legal values in the nf field.
+ for (int nf = 1; nf < 8; nf++) {
+ int num_fields = nf + 1;
+ // Set the number of fields in the source operand.
+ SetRegisterValues<int32_t>({{kRs3Name, nf}});
+ // Iterate over different lmul values.
+ for (int lmul_index = 0; lmul_index < 7; lmul_index++) {
+ // Configure vector unit, set the sew to the element width.
+ uint32_t vtype = (kSewSettingsByByteSize[sizeof(T)] << 3) |
+ kLmulSettings[lmul_index];
+ int lmul8 = kLmul8Values[lmul_index];
+ ConfigureVectorUnit(vtype, /*vlen*/ 1024);
+ // Clear the memory.
+ uint64_t base = kDataStoreAddress;
+ std::memset(data_db->raw_ptr(), 0, data_db->template size<uint8_t>());
+ state_->DbgStoreMemory(base, data_db);
+
+ // Execute instruction.
+ instruction_->Execute(nullptr);
+
+ if (lmul8 * num_fields > 64) {
+ EXPECT_TRUE(rv_vector_->vector_exception());
+ rv_vector_->clear_vector_exception();
+ continue;
+ }
+ int emul = sizeof(T) * lmul8 / rv_vector_->selected_element_width();
+ if (emul == 0 || emul > 64) {
+ EXPECT_TRUE(rv_vector_->vector_exception());
+ rv_vector_->clear_vector_exception();
+ continue;
+ }
+ EXPECT_FALSE(rv_vector_->vector_exception());
+ // Check memory values.
+ T value = 1;
+ int vlen = rv_vector_->vector_length();
+ int num_regs = std::max(1, lmul8 / 8);
+ // Load the store data.
+ state_->DbgLoadMemory(base, data_db);
+ // Iterate over fields.
+ for (int field = 0; field < num_fields; field++) {
+ // Iterate over the registers used for each field.
+ int segment_no = 0;
+ for (int reg = 0; reg < num_regs; reg++) {
+ // Iterate over segments within each register.
+ for (int i = 0; i < num_values_per_register; i++) {
+ // Get the mask value.
+ int mask_index = segment_no >> 3;
+ int mask_offset = segment_no & 0b111;
+ bool mask = (kA5Mask[mask_index] >> mask_offset) & 0x1;
+ T mem_value =
+ data_db->template Get<T>(segment_no * num_fields + field);
+ if (mask && (segment_no < vlen)) {
+ EXPECT_EQ(mem_value, static_cast<T>(value))
+ << " segment_no: " << segment_no << " field: " << field
+ << " index: " << i << " element_size: " << sizeof(T)
+ << " lmul8: " << lmul8 << " mask: " << mask;
+ // Zero the memory location.
+ data_db->template Set<T>(0, 0);
+ } else {
+ EXPECT_EQ(mem_value, 0)
+ << " segment_no: " << segment_no << " field: " << field
+ << " index: " << i << " element_size: " << sizeof(T)
+ << " lmul8: " << lmul8 << " mask: " << mask;
+ }
+ value++;
+ segment_no++;
+ }
+ }
+ }
+ if (HasFailure()) {
+ data_db->DecRef();
+ return;
+ }
+ }
+ }
+ data_db->DecRef();
+ }
+
+ // Helper function to test vector load segment strided instructions.
+ template <typename T>
+ void VectorStoreStridedSegmentHelper() {
+ const int strides[5] = {1, 4, 8, -1, -3};
+ // Set up instructions.
+ // Store data register.
+ AppendVectorRegisterOperands({kVs1}, {});
+ // Base address and stride.
+ AppendRegisterOperands({kRs1Name, kRs2Name}, {});
+ // Vector mask register.
+ AppendVectorRegisterOperands({kVmask}, {});
+ // Operand to hold the number of fields.
+ AppendRegisterOperands({kRs3Name}, {});
+ // Bind semantic function.
+ SetSemanticFunction(absl::bind_front(&VsSegmentStrided,
+ /*element_width*/ sizeof(T)));
+
+ // Set up register values.
+ // Set the store data register elements to be consecutive integers.
+ for (int reg = 0; reg < 8; reg++) {
+ auto reg_span = vreg_[reg + kVs1]->data_buffer()->Get<T>();
+ for (int i = 0; i < reg_span.size(); i++) {
+ reg_span[i] = static_cast<T>(reg * reg_span.size() + i + 1);
+ }
+ }
+ // Base address.
+ SetRegisterValues<uint32_t>({{kRs1Name, kDataStoreAddress}});
+ // Initialize the mask.
+ SetVectorRegisterValues<uint8_t>(
+ {{kVmaskName, Span<const uint8_t>(kA5Mask)}});
+
+ int num_values_per_register = kVectorLengthInBytes / sizeof(T);
+
+ auto *data_db = state_->db_factory()->Allocate<T>(1);
+ // Iterate over legal values in the nf field.
+ for (int nf = 1; nf < 8; nf++) {
+ int num_fields = nf + 1;
+ // Set the number of fields in the source operand.
+ SetRegisterValues<int32_t>({{kRs3Name, nf}});
+ // Iterate over different lmul values.
+ for (int lmul_index = 0; lmul_index < 7; lmul_index++) {
+ // Configure vector unit, set the sew to the element width.
+ uint32_t vtype = (kSewSettingsByByteSize[sizeof(T)] << 3) |
+ kLmulSettings[lmul_index];
+ int lmul8 = kLmul8Values[lmul_index];
+ ConfigureVectorUnit(vtype, /*vlen*/ 1024);
+
+ // Try different strides.
+ for (int s : strides) {
+ int32_t stride = s * num_fields * sizeof(T);
+ // Set the stride.
+ SetRegisterValues<int32_t>({{kRs2Name, stride}});
+ // Execute instruction.
+ instruction_->Execute(nullptr);
+
+ if (lmul8 * num_fields > 64) {
+ EXPECT_TRUE(rv_vector_->vector_exception());
+ rv_vector_->clear_vector_exception();
+ continue;
+ }
+ int emul = sizeof(T) * lmul8 / rv_vector_->selected_element_width();
+ if (emul == 0 || emul > 64) {
+ EXPECT_TRUE(rv_vector_->vector_exception());
+ rv_vector_->clear_vector_exception();
+ continue;
+ }
+ EXPECT_FALSE(rv_vector_->vector_exception());
+ // Check memory values.
+ uint64_t base = kDataStoreAddress;
+ T value = 1;
+ int vlen = rv_vector_->vector_length();
+ int num_regs = std::max(1, lmul8 / 8);
+ // Iterate over fields.
+ for (int field = 0; field < num_fields; field++) {
+ uint64_t address = base + field * sizeof(T);
+ // Iterate over the registers used for each field.
+ int segment_no = 0;
+ for (int reg = 0; reg < num_regs; reg++) {
+ // Iterate over segments within each register.
+ for (int i = 0; i < num_values_per_register; i++) {
+ // Load the data.
+ data_db->template Set<T>(0, 0);
+ state_->DbgLoadMemory(address, data_db);
+ // Get the mask value.
+ int mask_index = segment_no >> 3;
+ int mask_offset = segment_no & 0b111;
+ bool mask = (kA5Mask[mask_index] >> mask_offset) & 0x1;
+ if (mask && (segment_no < vlen)) {
+ EXPECT_EQ(data_db->template Get<T>(0), static_cast<T>(value))
+ << std::hex << "address: 0x" << address << std::dec
+ << " segment_no: " << segment_no << " field: " << field
+ << " index: " << i << " element_size: " << sizeof(T)
+ << " lmul8: " << lmul8 << " stride: " << stride;
+ // Zero the memory location.
+ data_db->template Set<T>(0, 0);
+ state_->StoreMemory(instruction_, address, data_db);
+ } else {
+ EXPECT_EQ(data_db->template Get<T>(0), 0)
+ << std::hex << "address: 0x" << address << std::dec
+ << " segment_no: " << segment_no << " field: " << field
+ << " index: " << i << " element_size: " << sizeof(T)
+ << " lmul8: " << lmul8 << " stride: " << stride
+ << " mask: " << mask;
+ }
+ value++;
+ address += stride;
+ segment_no++;
+ }
+ }
+ }
+ if (HasFailure()) {
+ data_db->DecRef();
+ return;
+ }
+ }
+ }
+ }
+ data_db->DecRef();
+ }
+
+ // Helper function to test vector load segment strided instructions.
+ template <typename T, typename I>
+ void VectorStoreIndexedSegmentHelper() {
+ // Ensure that the IndexType is signed.
+ using IndexType = typename std::make_signed<I>::type;
+ // Set up instructions.
+ // Store data register.
+ AppendVectorRegisterOperands({kVs1}, {});
+ // Base address and stride.
+ AppendRegisterOperands({kRs1Name}, {});
+ // Vector index register and vector mask register.
+ AppendVectorRegisterOperands({kVs2, kVmask}, {});
+ // Operand to hold the number of fields.
+ AppendRegisterOperands({kRs3Name}, {});
+ // Bind semantic function.
+ SetSemanticFunction(
+ absl::bind_front(&VsSegmentIndexed, /*index_width*/ sizeof(IndexType)));
+
+ // Set up register values.
+ // Set the store data register elements to be consecutive integers.
+ for (int reg = 0; reg < 8; reg++) {
+ auto reg_span = vreg_[reg + kVs1]->data_buffer()->Get<T>();
+ for (int i = 0; i < reg_span.size(); i++) {
+ reg_span[i] = static_cast<T>(reg * reg_span.size() + i + 1);
+ }
+ }
+ // Base address.
+ SetRegisterValues<uint32_t>({{kRs1Name, kDataStoreAddress}});
+ // Initialize the mask.
+ SetVectorRegisterValues<uint8_t>(
+ {{kVmaskName, Span<const uint8_t>(kA5Mask)}});
+ // Index values.
+ int index_values_per_reg = kVectorLengthInBytes / sizeof(IndexType);
+ int num_values_per_register = kVectorLengthInBytes / sizeof(T);
+
+ auto *data_db = state_->db_factory()->Allocate<T>(1);
+ // Iterate over legal values in the nf field.
+ for (int num_fields = 1; num_fields < 8; num_fields++) {
+ // Set the number of fields in the source operand.
+ SetRegisterValues<int32_t>({{kRs3Name, num_fields - 1}});
+ // Iterate over different lmul values.
+ for (int lmul_index = 0; lmul_index < 7; lmul_index++) {
+ // Configure vector unit, set the sew to the element width.
+ uint32_t vtype = (kSewSettingsByByteSize[sizeof(T)] << 3) |
+ kLmulSettings[lmul_index];
+ int lmul8 = kLmul8Values[lmul_index];
+
+ // Set up Index vector.
+ // Max number of segments (for testing) is limited by the range of the
+ // index type. For byte indices, the range is only +/- 128 for each
+ // index. Since the index isn't scaled, this means that the max number
+ // of segments for byte indices is 256 / (sizeof(T) * nf)
+ int segment_size = sizeof(T) * num_fields;
+ int max_segments =
+ kVectorLengthInBytes * lmul8 / (8 * num_fields * sizeof(T));
+ if (sizeof(IndexType) == 1) {
+ max_segments = std::min(256 / num_fields, max_segments);
+ }
+ ConfigureVectorUnit(vtype, max_segments);
+
+ int emul8 =
+ sizeof(IndexType) * lmul8 / rv_vector_->selected_element_width();
+ // Make sure not to write too many indices. At this point the emul
+ // value may still be "illegal", so avoid a "crash" due to writing
+ // the data_buffer out of range.
+ int max_indices = kVectorLengthInBytes *
+ std::min(8, std::max(1, emul8 / 8)) /
+ sizeof(IndexType);
+ if (max_indices > max_segments) {
+ max_indices = max_segments;
+ }
+ // Verify that the index space is large enough. That means that the
+ // index data type can contain enough index values to spread out all the
+ // stores to unique locations.
+ int num_values = kVectorLengthInBytes * lmul8 / (sizeof(T) * 8);
+ if (((num_values * sizeof(T)) >> 8) > (1 << (sizeof(IndexType) - 1))) {
+ LOG(WARNING) << "Index space is too small for the number of bytes: "
+ << num_values * sizeof(T);
+ continue;
+ }
+ for (int i = 0; i < max_indices; i++) {
+ int reg = i / index_values_per_reg;
+ int element = i % index_values_per_reg;
+ // Scale index by the segment size to avoid writing to the same
+ // location twice.
+ vreg_[kVs2 + reg]->data_buffer()->Set<IndexType>(
+ element, IndexValue<IndexType>(i) * segment_size);
+ }
+ // Execute instruction.
+ instruction_->Execute(nullptr);
+
+ // Check for exceptions when they should be set, and verify no
+ // exception otherwise.
+ if (lmul8 * num_fields > 64) {
+ EXPECT_TRUE(rv_vector_->vector_exception())
+ << "emul8: " << emul8 << " lmul8: " << lmul8;
+ rv_vector_->clear_vector_exception();
+ continue;
+ }
+ if (emul8 == 0 || emul8 > 64) {
+ EXPECT_TRUE(rv_vector_->vector_exception())
+ << "emul8: " << emul8 << " lmul8: " << lmul8;
+ rv_vector_->clear_vector_exception();
+ continue;
+ }
+ EXPECT_FALSE(rv_vector_->vector_exception())
+ << "emul8: " << emul8 << " lmul8: " << lmul8;
+
+ // Check memory values.
+ uint64_t base = kDataStoreAddress;
+ T value = 1;
+ int vlen = rv_vector_->vector_length();
+ // Iterate over fields.
+ for (int field = 0; field < num_fields; field++) {
+ // Expected value starts at 1 for the first register element and
+ // increments from there. The following computes the expected value
+ // of segment 0 for each field.
+ value = field * kVectorLengthInBytes * std::max(1, lmul8 / 8) /
+ sizeof(T) +
+ 1;
+ for (int segment = 0; segment < max_segments; segment++) {
+ int index_reg = segment / index_values_per_reg;
+ int index_no = segment % index_values_per_reg;
+ // Load the data.
+ int64_t index =
+ vreg_[kVs2 + index_reg]->data_buffer()->Get<IndexType>(
+ index_no);
+ uint64_t address = base + field * sizeof(T) + index;
+ state_->DbgLoadMemory(address, data_db);
+ int element = segment % num_values_per_register;
+ // Get the mask value.
+ int mask_index = segment >> 3;
+ int mask_offset = segment & 0b111;
+ bool mask = (kA5Mask[mask_index] >> mask_offset) & 0x1;
+ if (mask && (segment < vlen)) {
+ EXPECT_EQ(data_db->template Get<T>(0), static_cast<T>(value))
+ << std::hex << "address: 0x" << address << std::dec
+ << " index: " << index << " segment_no: " << segment
+ << " field: " << field << " i: " << element
+ << " element_size: " << sizeof(T) << " lmul8: " << lmul8
+ << " num_fields: " << num_fields;
+ // Zero the memory location.
+ data_db->template Set<T>(0, 0);
+ state_->StoreMemory(instruction_, address, data_db);
+ } else {
+ EXPECT_EQ(data_db->template Get<T>(0), 0)
+ << std::hex << "address: 0x" << address << std::dec
+ << " index: " << index << " segment_no: " << segment
+ << " field: " << field << " i: " << element
+ << " element_size: " << sizeof(T) << " lmul8: " << lmul8
+ << " mask: " << mask;
+ }
+ value++;
+ }
+ }
+ if (HasFailure()) {
+ data_db->DecRef();
+ return;
+ }
+ }
+ }
+ data_db->DecRef();
+ }
+
+ protected:
+ CheriotRegister *creg_[32];
+ RVVectorRegister *vreg_[32];
+ CheriotState *state_;
+ Instruction *instruction_;
+ Instruction *child_instruction_;
+ TaggedFlatDemandMemory *memory_;
+ CheriotVectorState *rv_vector_;
+};
+
+// Test the vector configuration set instructions. There are three separate
+// versions depending on whether Rs1 is X0 or not, of if Rd is X0.
+// The first handles the case when Rs1 is not X0.
+TEST_F(RiscVCheriotVInstructionsTest, VsetvlNN) {
+ AppendRegisterOperands({kRs1Name, kRs2Name}, {kRdName});
+ SetSemanticFunction(absl::bind_front(&Vsetvl,
+ /*rd_zero*/ false,
+ /*rs1_zero*/ false));
+ for (int lmul = 0; lmul < 7; lmul++) {
+ for (int sew = 0; sew < 4; sew++) {
+ for (int vlen_select = 0; vlen_select < 2; vlen_select++) {
+ // Try vlen below max and above.
+ uint32_t vlen = (vlen_select == 0) ? 16 : 1024;
+ uint32_t vma = (lmul & 1) ? 0b1'0'000'000 : 0;
+ uint32_t vta = (sew & 1) ? 0b0'1'000'000 : 0;
+ uint32_t vtype =
+ vma | vta | (kSewSettings[sew] << 3) | kLmulSettings[lmul];
+
+ SetRegisterValues<uint32_t>(
+ {{kRs1Name, vlen}, {kRs2Name, vtype}, {kRdName, 0}});
+
+ // Execute instruction.
+ instruction_->Execute(nullptr);
+
+ // Check results.
+ uint32_t expected_vlen =
+ std::min<uint32_t>(vlen, kVectorLengthInBytes * kLmul8Values[lmul] /
+ (8 * kSewValues[sew]));
+ EXPECT_EQ(creg_[kRd]->data_buffer()->Get<uint32_t>(0), expected_vlen)
+ << "LMUL: " << kLmul8Values[lmul] << " SEW: " << kSewValues[sew]
+ << " AVL: " << vlen;
+ EXPECT_EQ(rv_vector_->vector_length(), expected_vlen);
+ EXPECT_EQ(rv_vector_->vector_mask_agnostic(), vma != 0);
+ EXPECT_EQ(rv_vector_->vector_tail_agnostic(), vta != 0);
+ EXPECT_EQ(rv_vector_->vector_length_multiplier(), kLmul8Values[lmul]);
+ EXPECT_EQ(rv_vector_->selected_element_width(), kSewValues[sew]);
+ }
+ }
+ }
+}
+
+// The case when Rd is X0, but not Rs1.
+TEST_F(RiscVCheriotVInstructionsTest, VsetvlZN) {
+ AppendRegisterOperands({kRs1Name, kRs2Name}, {kRdName});
+ SetSemanticFunction(absl::bind_front(&Vsetvl,
+ /*rd_zero*/ true, /*rs1_zero*/ false));
+ for (int lmul = 0; lmul < 7; lmul++) {
+ for (int sew = 0; sew < 4; sew++) {
+ for (int vlen_select = 0; vlen_select < 2; vlen_select++) {
+ // Try vlen below max and above.
+ uint32_t vlen = (vlen_select == 0) ? 16 : 1024;
+ uint32_t vma = (lmul & 1) ? 0b1'0'000'000 : 0;
+ uint32_t vta = (sew & 1) ? 0b0'1'000'000 : 0;
+ uint32_t vtype =
+ vma | vta | (kSewSettings[sew] << 3) | kLmulSettings[lmul];
+
+ SetRegisterValues<uint32_t>(
+ {{kRs1Name, vlen}, {kRs2Name, vtype}, {kRdName, 0}});
+
+ // Execute instruction.
+ instruction_->Execute(nullptr);
+
+ // Check results.
+ uint32_t expected_vlen =
+ std::min<uint32_t>(vlen, kVectorLengthInBytes * kLmul8Values[lmul] /
+ (8 * kSewValues[sew]));
+ EXPECT_EQ(creg_[kRd]->data_buffer()->Get<uint32_t>(0), 0);
+ EXPECT_EQ(rv_vector_->vector_length(), expected_vlen);
+ EXPECT_EQ(rv_vector_->vector_mask_agnostic(), vma != 0);
+ EXPECT_EQ(rv_vector_->vector_tail_agnostic(), vta != 0);
+ EXPECT_EQ(rv_vector_->vector_length_multiplier(), kLmul8Values[lmul]);
+ EXPECT_EQ(rv_vector_->selected_element_width(), kSewValues[sew]);
+ }
+ }
+ }
+}
+
+// The case when Rd is not X0, but Rs1 is X0.
+TEST_F(RiscVCheriotVInstructionsTest, VsetvlNZ) {
+ AppendRegisterOperands({kRs1Name, kRs2Name}, {kRdName});
+ SetSemanticFunction(absl::bind_front(&Vsetvl,
+ /*rd_zero*/ false, /*rs1_zero*/ true));
+ for (int lmul = 0; lmul < 7; lmul++) {
+ for (int sew = 0; sew < 4; sew++) {
+ for (int vlen_select = 0; vlen_select < 2; vlen_select++) {
+ // Try vlen below max and above.
+ uint32_t vlen = (vlen_select == 0) ? 16 : 1024;
+ uint32_t vma = (lmul & 1) ? 0b1'0'000'000 : 0;
+ uint32_t vta = (sew & 1) ? 0b0'1'000'000 : 0;
+ uint32_t vtype =
+ vma | vta | (kSewSettings[sew] << 3) | kLmulSettings[lmul];
+
+ SetRegisterValues<uint32_t>(
+ {{kRs1Name, vlen}, {kRs2Name, vtype}, {kRdName, 0}});
+
+ // Execute instruction.
+ instruction_->Execute(nullptr);
+
+ // Check results.
+ // In this case, vlen is vlen max.
+ uint32_t expected_vlen =
+ kVectorLengthInBytes * kLmul8Values[lmul] / (8 * kSewValues[sew]);
+ EXPECT_EQ(creg_[kRd]->data_buffer()->Get<uint32_t>(0), expected_vlen);
+ EXPECT_EQ(rv_vector_->vector_length(), expected_vlen);
+ EXPECT_EQ(rv_vector_->vector_mask_agnostic(), vma != 0);
+ EXPECT_EQ(rv_vector_->vector_tail_agnostic(), vta != 0);
+ EXPECT_EQ(rv_vector_->vector_length_multiplier(), kLmul8Values[lmul]);
+ EXPECT_EQ(rv_vector_->selected_element_width(), kSewValues[sew]);
+ }
+ }
+ }
+}
+
+// The case when Rd and Rs1 are X0. In this case we are testing if an invalid
+// vector operation happens, which occurs if the max vector length changes due
+// to the new value of vtype.
+TEST_F(RiscVCheriotVInstructionsTest, VsetvlZZ) {
+ AppendRegisterOperands({kRs1Name, kRs2Name}, {kRdName});
+ SetSemanticFunction(absl::bind_front(&Vsetvl,
+ /*rd_zero*/ true, /*rs1_zero*/ true));
+ // Iterate over vector lengths.
+ for (int vlen = 512; vlen > 8; vlen /= 2) {
+ // First set the appropriate vector type for sew = 1 byte.
+ uint32_t lmul8 = vlen * 8 / kVectorLengthInBytes;
+ ASSERT_LE(lmul8, 64);
+ ASSERT_GE(lmul8, 1);
+ int lmul8_log2 = absl::bit_width<uint32_t>(lmul8);
+ int lmul_setting = kLmulSettingByLogSize[lmul8_log2];
+ // Set vtype for this vector length.
+ rv_vector_->SetVectorType(lmul_setting);
+ int max_vector_length = rv_vector_->max_vector_length();
+ for (int lmul = 0; lmul < 7; lmul++) {
+ for (int sew = 0; sew < 4; sew++) {
+ // Clear any exception.
+ rv_vector_->clear_vector_exception();
+ // Set up the vtype to try to set using the instruction.
+ uint32_t vma = (lmul & 1) ? 0b1'0'000'000 : 0;
+ uint32_t vta = (sew & 1) ? 0b0'1'000'000 : 0;
+ uint32_t vtype =
+ vma | vta | (kSewSettings[sew] << 3) | kLmulSettings[lmul];
+
+ SetRegisterValues<uint32_t>(
+ {{kRs1Name, 0xdeadbeef}, {kRs2Name, vtype}, {kRdName, 0xdeadbeef}});
+
+ // Execute instruction.
+ instruction_->Execute(nullptr);
+
+ // Check results.
+ uint32_t new_vlen =
+ kVectorLengthInBytes * kLmul8Values[lmul] / (8 * kSewValues[sew]);
+ // If vlen changes, then we expect an error and no change.
+ if (new_vlen != max_vector_length) {
+ EXPECT_TRUE(rv_vector_->vector_exception())
+ << "vlen: " << max_vector_length
+ << " lmul: " << kLmul8Values[lmul] << " sew: " << kSewValues[sew];
+ } else {
+ // Otherwise, check that the values are as expected.
+ EXPECT_FALSE(rv_vector_->vector_exception());
+ EXPECT_EQ(rv_vector_->vector_length_multiplier(), kLmul8Values[lmul]);
+ EXPECT_EQ(rv_vector_->selected_element_width(), kSewValues[sew]);
+ EXPECT_EQ(rv_vector_->vector_mask_agnostic(), vma != 0);
+ EXPECT_EQ(rv_vector_->vector_tail_agnostic(), vta != 0);
+ }
+ // No change in registers or vector length.
+ EXPECT_EQ(creg_[kRs1]->data_buffer()->Get<uint32_t>(0), 0xdeadbeef);
+ EXPECT_EQ(creg_[kRd]->data_buffer()->Get<uint32_t>(0), 0xdeadbeef);
+ EXPECT_EQ(rv_vector_->max_vector_length(), max_vector_length);
+ }
+ }
+ }
+}
+
+// This tests the semantic function for the VleN and VlseN instructions. VleN
+// is just a unit stride Vlse.
+TEST_F(RiscVCheriotVInstructionsTest, Vle8) {
+ VectorLoadUnitStridedHelper<uint8_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, Vle16) {
+ VectorLoadUnitStridedHelper<uint16_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, Vse32) {
+ VectorLoadUnitStridedHelper<uint32_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, Vle64) {
+ VectorLoadUnitStridedHelper<uint64_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, Vlse8) {
+ VectorLoadStridedHelper<uint8_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, Vlse16) {
+ VectorLoadStridedHelper<uint16_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, Vlse32) {
+ VectorLoadStridedHelper<uint32_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, Vlse64) {
+ VectorLoadStridedHelper<uint64_t>();
+}
+
+// Test of vector load mask.
+TEST_F(RiscVCheriotVInstructionsTest, Vlm) {
+ // Set up operands and register values.
+ AppendRegisterOperands({kRs1Name}, {});
+ SetSemanticFunction(&Vlm);
+ SetChildInstruction();
+ AppendVectorRegisterOperands(child_instruction_, {}, {kVd});
+ SetChildSemanticFunction(&VlChild);
+ SetRegisterValues<uint32_t>({{kRs1Name, kDataLoadAddress}});
+ // Execute instruction.
+ instruction_->Execute(nullptr);
+ EXPECT_FALSE(rv_vector_->vector_exception());
+ auto span = vreg_[kVd]->data_buffer()->Get<uint8_t>();
+ for (int i = 0; i < kVectorLengthInBytes; i++) {
+ EXPECT_EQ(i & 0xff, span[i]) << "element: " << i;
+ }
+}
+
+// Test of vector load register. Loads 1, 2, 4 or 8 registers.
+TEST_F(RiscVCheriotVInstructionsTest, VlRegister) {
+ // Set up operands and register values.
+ AppendRegisterOperands({kRs1Name}, {});
+ SetChildInstruction();
+ AppendVectorRegisterOperands(child_instruction_, {}, {kVd});
+ SetChildSemanticFunction(&VlChild);
+ SetRegisterValues<uint32_t>({{kRs1Name, kDataLoadAddress}});
+ // Test 1, 2, 4 and 8 register versions.
+ for (int num_reg = 1; num_reg <= 8; num_reg *= 2) {
+ SetSemanticFunction(
+ absl::bind_front(&VlRegister, num_reg, /*element_width*/ 1));
+ // Execute instruction.
+ instruction_->Execute();
+ // Check values.
+
+ for (int reg = kVd; reg < num_reg; reg++) {
+ auto span = vreg_[reg]->data_buffer()->Get<uint8_t>();
+ for (int i = 0; i < kVectorLengthInBytes; i++) {
+ EXPECT_EQ(span[i], i & 0xff)
+ << absl::StrCat("Reg: ", reg, " element ", i);
+ }
+ }
+ }
+}
+
+// Indexed loads directly encode the element width of the index value. The
+// width of the load value is determined by sew (selected element width).
+TEST_F(RiscVCheriotVInstructionsTest, VlIndexed8_8) {
+ VectorLoadIndexedHelper<uint8_t, uint8_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, VlIndexed8_16) {
+ VectorLoadIndexedHelper<uint8_t, uint16_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, VlIndexed8_32) {
+ VectorLoadIndexedHelper<uint8_t, uint32_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, VlIndexed8_64) {
+ VectorLoadIndexedHelper<uint8_t, uint64_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, VlIndexed16_8) {
+ VectorLoadIndexedHelper<uint16_t, uint8_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, VlIndexed16_16) {
+ VectorLoadIndexedHelper<uint16_t, uint16_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, VlIndexed16_32) {
+ VectorLoadIndexedHelper<uint16_t, uint32_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, VlIndexed16_64) {
+ VectorLoadIndexedHelper<uint16_t, uint64_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, VlIndexed32_8) {
+ VectorLoadIndexedHelper<uint32_t, uint8_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, VlIndexed32_16) {
+ VectorLoadIndexedHelper<uint32_t, uint16_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, VlIndexed32_32) {
+ VectorLoadIndexedHelper<uint32_t, uint32_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, VlIndexed32_64) {
+ VectorLoadIndexedHelper<uint32_t, uint64_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, VlIndexed64_8) {
+ VectorLoadIndexedHelper<uint64_t, uint8_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, VlIndexed64_16) {
+ VectorLoadIndexedHelper<uint64_t, uint16_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, VlIndexed64_32) {
+ VectorLoadIndexedHelper<uint64_t, uint32_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, VlIndexed64_64) {
+ VectorLoadIndexedHelper<uint64_t, uint64_t>();
+}
+
+// Test vector load segment unit stride.
+TEST_F(RiscVCheriotVInstructionsTest, Vlsege8) {
+ VectorLoadSegmentHelper<uint8_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, Vlsege16) {
+ VectorLoadSegmentHelper<uint16_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, Vlsege32) {
+ VectorLoadSegmentHelper<uint32_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, Vlsege64) {
+ VectorLoadSegmentHelper<uint64_t>();
+}
+
+// Test vector load segment, strided.
+TEST_F(RiscVCheriotVInstructionsTest, Vlssege8) {
+ VectorLoadStridedSegmentHelper<uint8_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, Vlssege16) {
+ VectorLoadStridedSegmentHelper<uint16_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, Vlssege32) {
+ VectorLoadStridedSegmentHelper<uint32_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, Vlssege64) {
+ VectorLoadStridedSegmentHelper<uint64_t>();
+}
+
+// Test vector load segment, indexed.
+TEST_F(RiscVCheriotVInstructionsTest, Vluxsegei8_8) {
+ VectorLoadIndexedSegmentHelper<uint8_t, uint8_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, Vluxsegei8_16) {
+ VectorLoadIndexedSegmentHelper<uint8_t, uint8_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, Vluxsegei8_32) {
+ VectorLoadIndexedSegmentHelper<uint8_t, uint8_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, Vluxsegei8_64) {
+ VectorLoadIndexedSegmentHelper<uint8_t, uint8_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, Vluxsegei16_8) {
+ VectorLoadIndexedSegmentHelper<uint8_t, uint8_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, Vluxsegei16_16) {
+ VectorLoadIndexedSegmentHelper<uint8_t, uint8_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, Vluxsegei16_32) {
+ VectorLoadIndexedSegmentHelper<uint8_t, uint8_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, Vluxsegei16_64) {
+ VectorLoadIndexedSegmentHelper<uint8_t, uint8_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, Vluxsegei32_8) {
+ VectorLoadIndexedSegmentHelper<uint8_t, uint8_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, Vluxsegei32_16) {
+ VectorLoadIndexedSegmentHelper<uint8_t, uint8_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, Vluxsegei32_32) {
+ VectorLoadIndexedSegmentHelper<uint8_t, uint8_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, Vluxsegei32_64) {
+ VectorLoadIndexedSegmentHelper<uint8_t, uint8_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, Vluxsegei64_8) {
+ VectorLoadIndexedSegmentHelper<uint8_t, uint8_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, Vluxsegei64_16) {
+ VectorLoadIndexedSegmentHelper<uint8_t, uint8_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, Vluxsegei64_32) {
+ VectorLoadIndexedSegmentHelper<uint8_t, uint8_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, Vluxsegei64_64) {
+ VectorLoadIndexedSegmentHelper<uint8_t, uint8_t>();
+}
+
+// Test Vector store strided.
+
+TEST_F(RiscVCheriotVInstructionsTest, Vsse8) {
+ VectorStoreStridedHelper<uint8_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, Vsse16) {
+ VectorStoreStridedHelper<uint16_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, Vsse32) {
+ VectorStoreStridedHelper<uint32_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, Vsse64) {
+ VectorStoreStridedHelper<uint64_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, Vsm) {
+ ConfigureVectorUnit(0b0'0'000'000, /*vlen*/ 1024);
+ // Set up operands and register values.
+ AppendVectorRegisterOperands({kVs1}, {});
+ AppendRegisterOperands({kRs1Name}, {});
+ SetSemanticFunction(&Vsm);
+ SetRegisterValues<uint32_t>({{kRs1Name, kDataStoreAddress}});
+ for (int i = 0; i < kVectorLengthInBytes; i++) {
+ vreg_[kVs1]->data_buffer()->Set<uint8_t>(i, i);
+ }
+ // Execute instruction.
+ instruction_->Execute(nullptr);
+
+ // Verify result.
+ EXPECT_FALSE(rv_vector_->vector_exception());
+ auto *data_db = state_->db_factory()->Allocate<uint8_t>(kVectorLengthInBytes);
+ state_->DbgLoadMemory(kDataStoreAddress, data_db);
+ auto span = data_db->Get<uint8_t>();
+ for (int i = 0; i < kVectorLengthInBytes; i++) {
+ EXPECT_EQ(static_cast<int>(span[i]), i);
+ }
+ data_db->DecRef();
+}
+
+// Tests of indexed stores, cross product of index types with value types.
+TEST_F(RiscVCheriotVInstructionsTest, VsIndexed8_8) {
+ VectorStoreIndexedHelper<uint8_t, uint8_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, VsIndexed8_16) {
+ VectorStoreIndexedHelper<uint8_t, uint8_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, VsIndexed8_32) {
+ VectorStoreIndexedHelper<uint8_t, uint8_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, VsIndexed8_64) {
+ VectorStoreIndexedHelper<uint8_t, uint8_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, VsIndexed16_8) {
+ VectorStoreIndexedHelper<uint8_t, uint8_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, VsIndexed16_16) {
+ VectorStoreIndexedHelper<uint8_t, uint8_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, VsIndexed16_32) {
+ VectorStoreIndexedHelper<uint8_t, uint8_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, VsIndexed16_64) {
+ VectorStoreIndexedHelper<uint8_t, uint8_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, VsIndexed32_8) {
+ VectorStoreIndexedHelper<uint8_t, uint8_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, VsIndexed32_16) {
+ VectorStoreIndexedHelper<uint8_t, uint8_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, VsIndexed32_32) {
+ VectorStoreIndexedHelper<uint8_t, uint8_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, VsIndexed32_64) {
+ VectorStoreIndexedHelper<uint8_t, uint8_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, VsIndexed64_8) {
+ VectorStoreIndexedHelper<uint8_t, uint8_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, VsIndexed64_16) {
+ VectorStoreIndexedHelper<uint8_t, uint8_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, VsIndexed64_32) {
+ VectorStoreIndexedHelper<uint8_t, uint8_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, VsIndexed64_64) {
+ VectorStoreIndexedHelper<uint8_t, uint8_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, VsRegister) {
+ ConfigureVectorUnit(0b0'0'000'000, /*vlen*/ 1024);
+ int num_elem = kVectorLengthInBytes / sizeof(uint64_t);
+ // Set up operands and register values.
+ AppendVectorRegisterOperands({kVs1}, {});
+ for (int reg = 0; reg < 8; reg++) {
+ for (int i = 0; i < num_elem; i++) {
+ vreg_[kVs1 + reg]->data_buffer()->Set<uint64_t>(i, reg * num_elem + i);
+ }
+ }
+ AppendRegisterOperands({kRs1Name}, {});
+ SetRegisterValues<uint32_t>({{kRs1Name, kDataStoreAddress}});
+
+ auto data_db = state_->db_factory()->Allocate(8 * kVectorLengthInBytes);
+ for (int num_regs = 1; num_regs <= 8; num_regs++) {
+ // Clear Memory.
+ memset(data_db->raw_ptr(), 0, data_db->size<uint8_t>());
+ state_->DbgStoreMemory(kDataStoreAddress, data_db);
+
+ SetSemanticFunction(absl::bind_front(&VsRegister, num_regs));
+
+ // Execute instruction.
+ instruction_->Execute();
+
+ // Verify results.
+ EXPECT_FALSE(rv_vector_->vector_exception());
+ uint64_t base = kDataStoreAddress;
+ for (int reg = 0; reg < 8; reg++) {
+ state_->DbgLoadMemory(base, data_db);
+ auto span = data_db->Get<uint64_t>();
+ for (int i = 0; i < num_elem; i++) {
+ if (reg < num_regs) {
+ EXPECT_EQ(span[i], reg * num_elem + i)
+ << "reg[" << reg << "][" << i << "]";
+ } else {
+ EXPECT_EQ(span[i], 0) << "reg[" << reg << "][" << i << "]";
+ }
+ }
+ base += kVectorLengthInBytes;
+ }
+ }
+ data_db->DecRef();
+}
+
+// Test vector store segment unit stride.
+TEST_F(RiscVCheriotVInstructionsTest, VsSegment8) {
+ VectorStoreSegmentHelper<uint8_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, VsSegment16) {
+ VectorStoreSegmentHelper<uint16_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, VsSegment32) {
+ VectorStoreSegmentHelper<uint32_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, VsSegment64) {
+ VectorStoreSegmentHelper<uint64_t>();
+}
+
+// Test vector store segment strided.
+TEST_F(RiscVCheriotVInstructionsTest, VsSegmentStrided8) {
+ VectorStoreStridedSegmentHelper<uint8_t>();
+}
+TEST_F(RiscVCheriotVInstructionsTest, VsSegmentStrided16) {
+ VectorStoreStridedSegmentHelper<uint16_t>();
+}
+TEST_F(RiscVCheriotVInstructionsTest, VsSegmentStrided32) {
+ VectorStoreStridedSegmentHelper<uint32_t>();
+}
+TEST_F(RiscVCheriotVInstructionsTest, VsSegmentStrided64) {
+ VectorStoreStridedSegmentHelper<uint64_t>();
+}
+
+// Test vector store segment indexed. Test each
+// combination of element size and index size.
+TEST_F(RiscVCheriotVInstructionsTest, VsSegmentIndexed8_8) {
+ VectorStoreIndexedSegmentHelper<uint8_t, int8_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, VsSegmentIndexed8_16) {
+ VectorStoreIndexedSegmentHelper<uint8_t, int16_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, VsSegmentIndexed8_32) {
+ VectorStoreIndexedSegmentHelper<uint8_t, int32_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, VsSegmentIndexed8_64) {
+ VectorStoreIndexedSegmentHelper<uint8_t, int64_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, VsSegmentIndexed16_8) {
+ VectorStoreIndexedSegmentHelper<uint16_t, int8_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, VsSegmentIndexed16_16) {
+ VectorStoreIndexedSegmentHelper<uint16_t, int16_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, VsSegmentIndexed16_32) {
+ VectorStoreIndexedSegmentHelper<uint16_t, int32_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, VsSegmentIndexed16_64) {
+ VectorStoreIndexedSegmentHelper<uint16_t, int64_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, VsSegmentIndexed32_8) {
+ VectorStoreIndexedSegmentHelper<uint32_t, int8_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, VsSegmentIndexed32_16) {
+ VectorStoreIndexedSegmentHelper<uint32_t, int16_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, VsSegmentIndexed32_32) {
+ VectorStoreIndexedSegmentHelper<uint32_t, int32_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, VsSegmentIndexed32_64) {
+ VectorStoreIndexedSegmentHelper<uint32_t, int64_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, VsSegmentIndexed64_8) {
+ VectorStoreIndexedSegmentHelper<uint64_t, int8_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, VsSegmentIndexed64_16) {
+ VectorStoreIndexedSegmentHelper<uint64_t, int16_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, VsSegmentIndexed64_32) {
+ VectorStoreIndexedSegmentHelper<uint64_t, int32_t>();
+}
+
+TEST_F(RiscVCheriotVInstructionsTest, VsSegmentIndexed64_64) {
+ VectorStoreIndexedSegmentHelper<uint64_t, int64_t>();
+}
+} // namespace
diff --git a/cheriot/test/riscv_cheriot_vector_opi_instructions_test.cc b/cheriot/test/riscv_cheriot_vector_opi_instructions_test.cc
new file mode 100644
index 0000000..268099f
--- /dev/null
+++ b/cheriot/test/riscv_cheriot_vector_opi_instructions_test.cc
@@ -0,0 +1,2381 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cheriot/riscv_cheriot_vector_opi_instructions.h"
+
+#include <cstdint>
+#include <limits>
+#include <vector>
+
+#include "absl/functional/bind_front.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
+#include "cheriot/cheriot_vector_state.h"
+#include "cheriot/test/riscv_cheriot_vector_instructions_test_base.h"
+#include "googlemock/include/gmock/gmock.h"
+#include "mpact/sim/generic/instruction.h"
+#include "riscv//riscv_register.h"
+
+// This file contains test cases for most of the RiscV OPIVV, IPIVX and OPIVI
+// instructions. The only instructions not covered by this file are the vector
+// permutation instructions.
+
+namespace {
+
+using ::absl::Span;
+using ::mpact::sim::cheriot::CheriotVectorState;
+using ::mpact::sim::generic::Instruction;
+using ::mpact::sim::generic::MakeUnsigned;
+using ::mpact::sim::generic::WideType;
+using ::mpact::sim::riscv::RV32Register;
+using ::mpact::sim::riscv::RVVectorRegister;
+
+// Semantic functions.
+using ::mpact::sim::cheriot::Vadc;
+using ::mpact::sim::cheriot::Vadd;
+using ::mpact::sim::cheriot::Vand;
+using ::mpact::sim::cheriot::Vmadc;
+using ::mpact::sim::cheriot::Vmax;
+using ::mpact::sim::cheriot::Vmaxu;
+using ::mpact::sim::cheriot::Vmerge;
+using ::mpact::sim::cheriot::Vmin;
+using ::mpact::sim::cheriot::Vminu;
+using ::mpact::sim::cheriot::Vmsbc;
+using ::mpact::sim::cheriot::Vmseq;
+using ::mpact::sim::cheriot::Vmsgt;
+using ::mpact::sim::cheriot::Vmsgtu;
+using ::mpact::sim::cheriot::Vmsle;
+using ::mpact::sim::cheriot::Vmsleu;
+using ::mpact::sim::cheriot::Vmslt;
+using ::mpact::sim::cheriot::Vmsltu;
+using ::mpact::sim::cheriot::Vmsne;
+using ::mpact::sim::cheriot::Vmvr;
+using ::mpact::sim::cheriot::Vnclip;
+using ::mpact::sim::cheriot::Vnclipu;
+using ::mpact::sim::cheriot::Vnsra;
+using ::mpact::sim::cheriot::Vnsrl;
+using ::mpact::sim::cheriot::Vor;
+using ::mpact::sim::cheriot::Vrsub;
+using ::mpact::sim::cheriot::Vsadd;
+using ::mpact::sim::cheriot::Vsaddu;
+using ::mpact::sim::cheriot::Vsbc;
+using ::mpact::sim::cheriot::Vsll;
+using ::mpact::sim::cheriot::Vsmul;
+using ::mpact::sim::cheriot::Vsra;
+using ::mpact::sim::cheriot::Vsrl;
+using ::mpact::sim::cheriot::Vssra;
+using ::mpact::sim::cheriot::Vssrl;
+using ::mpact::sim::cheriot::Vssub;
+using ::mpact::sim::cheriot::Vssubu;
+using ::mpact::sim::cheriot::Vsub;
+using ::mpact::sim::cheriot::Vxor;
+
+class RiscVCheriotVectorInstructionsTest
+ : public RiscVCheriotVectorInstructionsTestBase {};
+
+// Each instruction is tested for each element width, and for vector-vector
+// as well as vector-scalar (as applicable).
+
+// Vector add.
+// Vector-vector.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vadd8VV) {
+ SetSemanticFunction(&Vadd);
+ BinaryOpTestHelperVV<uint8_t, uint8_t, uint8_t>(
+ "Vadd8", /*sew*/ 8, instruction_,
+ [](uint8_t val0, uint8_t val1) -> uint8_t { return val0 + val1; });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vadd16VV) {
+ SetSemanticFunction(&Vadd);
+ BinaryOpTestHelperVV<uint16_t, uint16_t, uint16_t>(
+ "Vadd16", /*sew*/ 16, instruction_,
+ [](uint16_t val0, uint16_t val1) -> uint16_t { return val0 + val1; });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vadd32VV) {
+ SetSemanticFunction(&Vadd);
+ BinaryOpTestHelperVV<uint32_t, uint32_t, uint32_t>(
+ "Vadd32", /*sew*/ 32, instruction_,
+ [](uint32_t val0, uint32_t val1) -> uint32_t { return val0 + val1; });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vadd64VV) {
+ SetSemanticFunction(&Vadd);
+ BinaryOpTestHelperVV<uint64_t, uint64_t, uint64_t>(
+ "Vadd64", /*sew*/ 64, instruction_,
+ [](uint64_t val0, uint64_t val1) -> uint64_t { return val0 + val1; });
+}
+
+// Vector-scalar.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vadd8VX) {
+ SetSemanticFunction(&Vadd);
+
+ BinaryOpTestHelperVX<uint8_t, uint8_t, uint8_t>(
+ "Vadd8", /*sew*/ 8, instruction_,
+ [](uint8_t val0, uint8_t val1) -> uint8_t {
+ return val0 + static_cast<uint8_t>(val1);
+ });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vadd16VX) {
+ SetSemanticFunction(&Vadd);
+ BinaryOpTestHelperVX<uint16_t, uint16_t, uint16_t>(
+ "Vadd16", /*sew*/ 16, instruction_,
+ [](uint16_t val0, uint16_t val1) -> uint16_t { return val0 + val1; });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vadd32VX) {
+ SetSemanticFunction(&Vadd);
+ BinaryOpTestHelperVX<uint32_t, uint32_t, uint32_t>(
+ "Vadd32", /*sew*/ 32, instruction_,
+ [](uint32_t val0, uint32_t val1) -> uint32_t { return val0 + val1; });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vadd64VX) {
+ SetSemanticFunction(&Vadd);
+ BinaryOpTestHelperVX<uint64_t, uint64_t, uint64_t>(
+ "Vadd64", /*sew*/ 64, instruction_,
+ [](uint64_t val0, uint64_t val1) -> uint64_t { return val0 + val1; });
+}
+
+// Vector subtract.
+// Vector-vector.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsub8VV) {
+ SetSemanticFunction(&Vsub);
+ BinaryOpTestHelperVV<uint8_t, uint8_t, uint8_t>(
+ "Vsub8", /*sew*/ 8, instruction_,
+ [](uint8_t val0, uint8_t val1) -> uint8_t { return val0 - val1; });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsub16VV) {
+ SetSemanticFunction(&Vsub);
+ BinaryOpTestHelperVV<uint16_t, uint16_t, uint16_t>(
+ "Vsub16", /*sew*/ 16, instruction_,
+ [](uint16_t val0, uint16_t val1) -> uint16_t { return val0 - val1; });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsub32VV) {
+ SetSemanticFunction(&Vsub);
+ BinaryOpTestHelperVV<uint32_t, uint32_t, uint32_t>(
+ "Vsub32", /*sew*/ 32, instruction_,
+ [](uint32_t val0, uint32_t val1) -> uint32_t { return val0 - val1; });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsub64VV) {
+ SetSemanticFunction(&Vsub);
+ BinaryOpTestHelperVV<uint64_t, uint64_t, uint64_t>(
+ "Vsub64", /*sew*/ 64, instruction_,
+ [](uint64_t val0, uint64_t val1) -> uint64_t { return val0 - val1; });
+}
+
+// Vector-scalar.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsub8VX) {
+ SetSemanticFunction(&Vsub);
+ BinaryOpTestHelperVX<uint8_t, uint8_t, uint8_t>(
+ "Vsub8", /*sew*/ 8, instruction_,
+ [](uint8_t val0, uint8_t val1) -> uint8_t { return val0 - val1; });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsub16VX) {
+ SetSemanticFunction(&Vsub);
+ BinaryOpTestHelperVX<uint16_t, uint16_t, uint16_t>(
+ "Vsub16", /*sew*/ 16, instruction_,
+ [](uint16_t val0, uint16_t val1) -> uint16_t { return val0 - val1; });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsub32VX) {
+ SetSemanticFunction(&Vsub);
+ BinaryOpTestHelperVX<uint32_t, uint32_t, uint32_t>(
+ "Vsub32", /*sew*/ 32, instruction_,
+ [](uint32_t val0, uint32_t val1) -> uint32_t { return val0 - val1; });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsub64VX) {
+ SetSemanticFunction(&Vsub);
+ BinaryOpTestHelperVX<uint64_t, uint64_t, uint64_t>(
+ "Vsub64", /*sew*/ 64, instruction_,
+ [](uint64_t val0, uint64_t val1) -> uint64_t { return val0 - val1; });
+}
+
+// Vector reverse subtract.
+// Vector-Scalar only.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vrsub8VX) {
+ SetSemanticFunction(&Vrsub);
+ BinaryOpTestHelperVX<uint8_t, uint8_t, uint8_t>(
+ "Vrsub8", /*sew*/ 8, instruction_,
+ [](uint8_t val0, uint8_t val1) -> uint8_t { return val1 - val0; });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vrsub16VX) {
+ SetSemanticFunction(&Vrsub);
+ BinaryOpTestHelperVX<uint16_t, uint16_t, uint16_t>(
+ "Vrsub16", /*sew*/ 16, instruction_,
+ [](uint16_t val0, uint16_t val1) -> uint16_t { return val1 - val0; });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vrsub32VX) {
+ SetSemanticFunction(&Vrsub);
+ BinaryOpTestHelperVX<uint32_t, uint32_t, uint32_t>(
+ "Vrsub32", /*sew*/ 32, instruction_,
+ [](uint32_t val0, uint32_t val1) -> uint32_t { return val1 - val0; });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vrsub64VX) {
+ SetSemanticFunction(&Vrsub);
+ BinaryOpTestHelperVX<uint64_t, uint64_t, uint64_t>(
+ "Vrsub64", /*sew*/ 64, instruction_,
+ [](uint64_t val0, uint64_t val1) -> uint64_t { return val1 - val0; });
+}
+
+// Vector and.
+// Vector-Vector.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vand8VV) {
+ SetSemanticFunction(&Vand);
+
+ BinaryOpTestHelperVV<uint8_t, uint8_t, uint8_t>(
+ "Vand8", /*sew*/ 8, instruction_,
+ [](uint8_t val0, uint8_t val1) -> uint8_t { return val0 & val1; });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vand16VV) {
+ SetSemanticFunction(&Vand);
+
+ BinaryOpTestHelperVV<uint16_t, uint16_t, uint16_t>(
+ "Vand16", /*sew*/ 16, instruction_,
+ [](uint16_t val0, uint16_t val1) -> uint16_t { return val0 & val1; });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vand32VV) {
+ SetSemanticFunction(&Vand);
+
+ BinaryOpTestHelperVV<uint32_t, uint32_t, uint32_t>(
+ "Vand32", /*sew*/ 32, instruction_,
+ [](uint32_t val0, uint32_t val1) -> uint32_t { return val0 & val1; });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vand64VV) {
+ SetSemanticFunction(&Vand);
+
+ BinaryOpTestHelperVV<uint64_t, uint64_t, uint64_t>(
+ "Vand64", /*sew*/ 64, instruction_,
+ [](uint64_t val0, uint64_t val1) -> uint64_t { return val0 & val1; });
+}
+
+// Vector-Scalar.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vand8VX) {
+ SetSemanticFunction(&Vand);
+
+ BinaryOpTestHelperVX<uint8_t, uint8_t, uint8_t>(
+ "Vand8", /*sew*/ 8, instruction_,
+ [](uint8_t val0, uint8_t val1) -> uint8_t { return val0 & val1; });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vand16VX) {
+ SetSemanticFunction(&Vand);
+
+ BinaryOpTestHelperVX<uint16_t, uint16_t, uint16_t>(
+ "Vand16", /*sew*/ 16, instruction_,
+ [](uint16_t val0, uint16_t val1) -> uint16_t { return val0 & val1; });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vand32VX) {
+ SetSemanticFunction(&Vand);
+
+ BinaryOpTestHelperVX<uint32_t, uint32_t, uint32_t>(
+ "Vand32", /*sew*/ 32, instruction_,
+ [](uint32_t val0, uint32_t val1) -> uint32_t { return val0 & val1; });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vand64VX) {
+ SetSemanticFunction(&Vand);
+
+ BinaryOpTestHelperVX<uint64_t, uint64_t, uint64_t>(
+ "Vand64", /*sew*/ 64, instruction_,
+ [](uint64_t val0, uint64_t val1) -> uint64_t { return val0 & val1; });
+}
+
+// Vector or.
+// Vector-Vector.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vor8VV) {
+ SetSemanticFunction(&Vor);
+
+ BinaryOpTestHelperVV<uint8_t, uint8_t, uint8_t>(
+ "Vor8", /*sew*/ 8, instruction_,
+ [](uint8_t val0, uint8_t val1) -> uint8_t { return val0 | val1; });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vor16VV) {
+ SetSemanticFunction(&Vor);
+
+ BinaryOpTestHelperVV<uint16_t, uint16_t, uint16_t>(
+ "Vor16", /*sew*/ 16, instruction_,
+ [](uint16_t val0, uint16_t val1) -> uint16_t { return val0 | val1; });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vor32VV) {
+ SetSemanticFunction(&Vor);
+
+ BinaryOpTestHelperVV<uint32_t, uint32_t, uint32_t>(
+ "Vor32", /*sew*/ 32, instruction_,
+ [](uint32_t val0, uint32_t val1) -> uint32_t { return val0 | val1; });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vor64VV) {
+ SetSemanticFunction(&Vor);
+
+ BinaryOpTestHelperVV<uint64_t, uint64_t, uint64_t>(
+ "Vor64", /*sew*/ 64, instruction_,
+ [](uint64_t val0, uint64_t val1) -> uint64_t { return val0 | val1; });
+}
+
+// Vector-Scalar.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vor8VX) {
+ SetSemanticFunction(&Vor);
+
+ BinaryOpTestHelperVX<uint8_t, uint8_t, uint8_t>(
+ "Vor8", /*sew*/ 8, instruction_,
+ [](uint8_t val0, uint8_t val1) -> uint8_t { return val0 | val1; });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vor16VX) {
+ SetSemanticFunction(&Vor);
+
+ BinaryOpTestHelperVX<uint16_t, uint16_t, uint16_t>(
+ "Vor16", /*sew*/ 16, instruction_,
+ [](uint16_t val0, uint16_t val1) -> uint16_t { return val0 | val1; });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vor32VX) {
+ SetSemanticFunction(&Vor);
+
+ BinaryOpTestHelperVX<uint32_t, uint32_t, uint32_t>(
+ "Vor32", /*sew*/ 32, instruction_,
+ [](uint32_t val0, uint32_t val1) -> uint32_t { return val0 | val1; });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vor64VX) {
+ SetSemanticFunction(&Vor);
+
+ BinaryOpTestHelperVX<uint64_t, uint64_t, uint64_t>(
+ "Vor64", /*sew*/ 64, instruction_,
+ [](uint64_t val0, uint64_t val1) -> uint64_t { return val0 | val1; });
+}
+
+// Vector xor.
+// Vector-Vector.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vxor8VV) {
+ SetSemanticFunction(&Vxor);
+
+ BinaryOpTestHelperVV<uint8_t, uint8_t, uint8_t>(
+ "Vxor8", /*sew*/ 8, instruction_,
+ [](uint8_t val0, uint8_t val1) -> uint8_t { return val0 ^ val1; });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vxor16VV) {
+ SetSemanticFunction(&Vxor);
+
+ BinaryOpTestHelperVV<uint16_t, uint16_t, uint16_t>(
+ "Vxor16", /*sew*/ 16, instruction_,
+ [](uint16_t val0, uint16_t val1) -> uint16_t { return val0 ^ val1; });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vxor32VV) {
+ SetSemanticFunction(&Vxor);
+
+ BinaryOpTestHelperVV<uint32_t, uint32_t, uint32_t>(
+ "Vxor32", /*sew*/ 32, instruction_,
+ [](uint32_t val0, uint32_t val1) -> uint32_t { return val0 ^ val1; });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vxor64VV) {
+ SetSemanticFunction(&Vxor);
+
+ BinaryOpTestHelperVV<uint64_t, uint64_t, uint64_t>(
+ "Vxor64", /*sew*/ 64, instruction_,
+ [](uint64_t val0, uint64_t val1) -> uint64_t { return val0 ^ val1; });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vxor8VX) {
+ SetSemanticFunction(&Vxor);
+
+ BinaryOpTestHelperVX<uint8_t, uint8_t, uint8_t>(
+ "Vxor8", /*sew*/ 8, instruction_,
+ [](uint8_t val0, uint8_t val1) -> uint8_t { return val0 ^ val1; });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vxor16VX) {
+ SetSemanticFunction(&Vxor);
+
+ BinaryOpTestHelperVX<uint16_t, uint16_t, uint16_t>(
+ "Vxor16", /*sew*/ 16, instruction_,
+ [](uint16_t val0, uint16_t val1) -> uint16_t { return val0 ^ val1; });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vxor32VX) {
+ SetSemanticFunction(&Vxor);
+
+ BinaryOpTestHelperVX<uint32_t, uint32_t, uint32_t>(
+ "Vxor32", /*sew*/ 32, instruction_,
+ [](uint32_t val0, uint32_t val1) -> uint32_t { return val0 ^ val1; });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vxor64VX) {
+ SetSemanticFunction(&Vxor);
+
+ BinaryOpTestHelperVX<uint64_t, uint64_t, uint64_t>(
+ "Vxor64", /*sew*/ 64, instruction_,
+ [](uint64_t val0, uint64_t val1) -> uint64_t { return val0 ^ val1; });
+}
+
+// Vector sll.
+// Vector-Vector.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsll8VV) {
+ SetSemanticFunction(&Vsll);
+
+ BinaryOpTestHelperVV<uint8_t, uint8_t, uint8_t>(
+ "Vsll8", /*sew*/ 8, instruction_,
+ [](uint8_t val0, uint8_t val1) -> uint8_t {
+ return val0 << (val1 & 0b111);
+ });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsll16VV) {
+ SetSemanticFunction(&Vsll);
+
+ BinaryOpTestHelperVV<uint16_t, uint16_t, uint16_t>(
+ "Vsll16", /*sew*/ 16, instruction_,
+ [](uint16_t val0, uint16_t val1) -> uint16_t {
+ return val0 << (val1 & 0b1111);
+ });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsll32VV) {
+ SetSemanticFunction(&Vsll);
+
+ BinaryOpTestHelperVV<uint32_t, uint32_t, uint32_t>(
+ "Vsll32", /*sew*/ 32, instruction_,
+ [](uint32_t val0, uint32_t val1) -> uint32_t {
+ return val0 << (val1 & 0b1'1111);
+ });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsll64VV) {
+ SetSemanticFunction(&Vsll);
+
+ BinaryOpTestHelperVV<uint64_t, uint64_t, uint64_t>(
+ "Vsll64", /*sew*/ 64, instruction_,
+ [](uint64_t val0, uint64_t val1) -> uint64_t {
+ return val0 << (val1 & 0b11'1111);
+ });
+}
+
+// Vector-Scalar.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsll8VX) {
+ SetSemanticFunction(&Vsll);
+
+ BinaryOpTestHelperVX<uint8_t, uint8_t, uint8_t>(
+ "Vsll8", /*sew*/ 8, instruction_,
+ [](uint8_t val0, uint8_t val1) -> uint8_t {
+ return val0 << (val1 & 0b111);
+ });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsll16VX) {
+ SetSemanticFunction(&Vsll);
+
+ BinaryOpTestHelperVX<uint16_t, uint16_t, uint16_t>(
+ "Vsll16", /*sew*/ 16, instruction_,
+ [](uint16_t val0, uint16_t val1) -> uint16_t {
+ return val0 << (val1 & 0b1111);
+ });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsll32VX) {
+ SetSemanticFunction(&Vsll);
+
+ BinaryOpTestHelperVX<uint32_t, uint32_t, uint32_t>(
+ "Vsll32", /*sew*/ 32, instruction_,
+ [](uint32_t val0, uint32_t val1) -> uint32_t {
+ return val0 << (val1 & 0b1'1111);
+ });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsll64VX) {
+ SetSemanticFunction(&Vsll);
+
+ BinaryOpTestHelperVX<uint64_t, uint64_t, uint64_t>(
+ "Vsll64", /*sew*/ 64, instruction_,
+ [](uint64_t val0, uint64_t val1) -> uint64_t {
+ return val0 << (val1 & 0b11'1111);
+ });
+}
+
+// Vector srl.
+// Vector-Vector.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsrl8VV) {
+ SetSemanticFunction(&Vsrl);
+
+ BinaryOpTestHelperVV<uint8_t, uint8_t, uint8_t>(
+ "Vsrl8", /*sew*/ 8, instruction_,
+ [](uint8_t val0, uint8_t val1) -> uint8_t {
+ return val0 >> (val1 & 0b111);
+ });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsrl16VV) {
+ SetSemanticFunction(&Vsrl);
+
+ BinaryOpTestHelperVV<uint16_t, uint16_t, uint16_t>(
+ "Vsrl16", /*sew*/ 16, instruction_,
+ [](uint16_t val0, uint16_t val1) -> uint16_t {
+ return val0 >> (val1 & 0b1111);
+ });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsrl32VV) {
+ SetSemanticFunction(&Vsrl);
+
+ BinaryOpTestHelperVV<uint32_t, uint32_t, uint32_t>(
+ "Vsrl32", /*sew*/ 32, instruction_,
+ [](uint32_t val0, uint32_t val1) -> uint32_t {
+ return val0 >> (val1 & 0b1'1111);
+ });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsrl64VV) {
+ SetSemanticFunction(&Vsrl);
+
+ BinaryOpTestHelperVV<uint64_t, uint64_t, uint64_t>(
+ "Vsrl64", /*sew*/ 64, instruction_,
+ [](uint64_t val0, uint64_t val1) -> uint64_t {
+ return val0 >> (val1 & 0b11'1111);
+ });
+}
+
+// Vector-Scalar.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsrl8VX) {
+ SetSemanticFunction(&Vsrl);
+
+ BinaryOpTestHelperVX<uint8_t, uint8_t, uint8_t>(
+ "Vsrl8", /*sew*/ 8, instruction_,
+ [](uint8_t val0, uint8_t val1) -> uint8_t {
+ return val0 >> (val1 & 0b111);
+ });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsrl16VX) {
+ SetSemanticFunction(&Vsrl);
+
+ BinaryOpTestHelperVX<uint16_t, uint16_t, uint16_t>(
+ "Vsrl16", /*sew*/ 16, instruction_,
+ [](uint16_t val0, uint16_t val1) -> uint16_t {
+ return val0 >> (val1 & 0b1111);
+ });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsrl32VX) {
+ SetSemanticFunction(&Vsrl);
+
+ BinaryOpTestHelperVX<uint32_t, uint32_t, uint32_t>(
+ "Vsrl32", /*sew*/ 32, instruction_,
+ [](uint32_t val0, uint32_t val1) -> uint32_t {
+ return val0 >> (val1 & 0b1'1111);
+ });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsrl64VX) {
+ SetSemanticFunction(&Vsrl);
+
+ BinaryOpTestHelperVX<uint64_t, uint64_t, uint64_t>(
+ "Vsrl64", /*sew*/ 64, instruction_,
+ [](uint64_t val0, uint64_t val1) -> uint64_t {
+ return val0 >> (val1 & 0b11'1111);
+ });
+}
+
+// Vector sra.
+// Vector-Vector.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsra8VV) {
+ SetSemanticFunction(&Vsra);
+
+ BinaryOpTestHelperVV<uint8_t, int8_t, uint8_t>(
+ "Vsra8", /*sew*/ 8, instruction_,
+ [](int8_t val0, uint8_t val1) -> int8_t {
+ return val0 >> (val1 & 0b111);
+ });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsra16VV) {
+ SetSemanticFunction(&Vsra);
+
+ BinaryOpTestHelperVV<uint16_t, int16_t, uint16_t>(
+ "Vsra16", /*sew*/ 16, instruction_,
+ [](int16_t val0, uint16_t val1) -> int16_t {
+ return val0 >> (val1 & 0b1111);
+ });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsra32VV) {
+ SetSemanticFunction(&Vsra);
+
+ BinaryOpTestHelperVV<uint32_t, int32_t, uint32_t>(
+ "Vsra32", /*sew*/ 32, instruction_,
+ [](int32_t val0, uint32_t val1) -> int32_t {
+ return val0 >> (val1 & 0b1'1111);
+ });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsra64VV) {
+ SetSemanticFunction(&Vsra);
+
+ BinaryOpTestHelperVV<uint64_t, int64_t, uint64_t>(
+ "Vsll64", /*sew*/ 64, instruction_,
+ [](int64_t val0, uint64_t val1) -> int64_t {
+ return val0 >> (val1 & 0b11'1111);
+ });
+}
+
+// Vector-Scalar.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsra8VX) {
+ SetSemanticFunction(&Vsra);
+
+ BinaryOpTestHelperVX<uint8_t, int8_t, uint8_t>(
+ "Vsra8", /*sew*/ 8, instruction_,
+ [](int8_t val0, uint8_t val1) -> int8_t {
+ return val0 >> (val1 & 0b111);
+ });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsra16VX) {
+ SetSemanticFunction(&Vsra);
+
+ BinaryOpTestHelperVX<uint16_t, int16_t, uint16_t>(
+ "Vsra16", /*sew*/ 16, instruction_,
+ [](int16_t val0, uint16_t val1) -> int16_t {
+ return val0 >> (val1 & 0b1111);
+ });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsra32VX) {
+ SetSemanticFunction(&Vsra);
+
+ BinaryOpTestHelperVX<uint32_t, int32_t, uint32_t>(
+ "Vsra32", /*sew*/ 32, instruction_,
+ [](int32_t val0, uint32_t val1) -> int32_t {
+ return val0 >> (val1 & 0b1'1111);
+ });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsra64VX) {
+ SetSemanticFunction(&Vsra);
+
+ BinaryOpTestHelperVX<uint64_t, int64_t, uint64_t>(
+ "Vsll64", /*sew*/ 64, instruction_,
+ [](int64_t val0, uint64_t val1) -> int64_t {
+ return val0 >> (val1 & 0b11'1111);
+ });
+}
+
+// Vector narrowing srl.
+// Vector-Vector.VV
+TEST_F(RiscVCheriotVectorInstructionsTest, Vnsrl8VV) {
+ SetSemanticFunction(&Vnsrl);
+
+ BinaryOpTestHelperVV<uint8_t, uint16_t, uint8_t>(
+ "Vsra8", /*sew*/ 8, instruction_,
+ [](uint16_t val0, uint8_t val1) -> uint8_t {
+ return static_cast<uint8_t>(val0 >> (val1 & 0b1111));
+ });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vnsrl16VV) {
+ SetSemanticFunction(&Vnsrl);
+
+ BinaryOpTestHelperVV<uint16_t, uint32_t, uint16_t>(
+ "Vsll16", /*sew*/ 16, instruction_,
+ [](uint32_t val0, uint16_t val1) -> uint16_t {
+ return static_cast<uint16_t>(val0 >> (val1 & 0b1'1111));
+ });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vnsrl32VV) {
+ SetSemanticFunction(&Vnsrl);
+
+ BinaryOpTestHelperVV<uint32_t, uint64_t, uint32_t>(
+ "Vsll32", /*sew*/ 32, instruction_,
+ [](uint64_t val0, uint32_t val1) -> uint32_t {
+ return static_cast<uint32_t>(val0 >> (val1 & 0b11'1111));
+ });
+}
+
+// Vector-Scalar.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vnsrl8VX) {
+ SetSemanticFunction(&Vnsrl);
+
+ BinaryOpTestHelperVX<uint8_t, uint16_t, uint8_t>(
+ "Vsra8", /*sew*/ 8, instruction_,
+ [](uint16_t val0, uint8_t val1) -> uint8_t {
+ return static_cast<uint8_t>(val0 >> (val1 & 0b1111));
+ });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vnsrl16VX) {
+ SetSemanticFunction(&Vnsrl);
+
+ BinaryOpTestHelperVX<uint16_t, uint32_t, uint16_t>(
+ "Vsll16", /*sew*/ 16, instruction_,
+ [](uint32_t val0, uint16_t val1) -> uint16_t {
+ return static_cast<uint16_t>(val0 >> (val1 & 0b1'1111));
+ });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vnsrl32VX) {
+ SetSemanticFunction(&Vnsrl);
+
+ BinaryOpTestHelperVX<uint32_t, uint64_t, uint32_t>(
+ "Vsll32", /*sew*/ 32, instruction_,
+ [](uint64_t val0, uint32_t val1) -> uint32_t {
+ return static_cast<uint32_t>(val0 >> (val1 & 0b11'1111));
+ });
+}
+
+// Vector narrowing sra.
+// Vector-Vector.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vnsra8VV) {
+ SetSemanticFunction(&Vnsra);
+
+ BinaryOpTestHelperVV<uint8_t, uint16_t, uint8_t>(
+ "Vsra8", /*sew*/ 8, instruction_,
+ [](int16_t val0, uint8_t val1) -> uint8_t {
+ return static_cast<uint8_t>(val0 >> (val1 & 0b1111));
+ });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vnsra16VV) {
+ SetSemanticFunction(&Vnsra);
+
+ BinaryOpTestHelperVV<uint16_t, uint32_t, uint16_t>(
+ "Vsll16", /*sew*/ 16, instruction_,
+ [](int32_t val0, uint16_t val1) -> uint16_t {
+ return static_cast<uint16_t>(val0 >> (val1 & 0b1'1111));
+ });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vnsra32VV) {
+ SetSemanticFunction(&Vnsra);
+
+ BinaryOpTestHelperVV<uint32_t, uint64_t, uint32_t>(
+ "Vsll32", /*sew*/ 32, instruction_,
+ [](int64_t val0, uint32_t val1) -> uint32_t {
+ return static_cast<uint32_t>(val0 >> (val1 & 0b11'1111));
+ });
+}
+
+// Vector-Scalar.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vnsra8VX) {
+ SetSemanticFunction(&Vnsra);
+
+ BinaryOpTestHelperVX<uint8_t, uint16_t, uint8_t>(
+ "Vsra8", /*sew*/ 8, instruction_,
+ [](int16_t val0, uint8_t val1) -> uint8_t {
+ return static_cast<uint8_t>(val0 >> (val1 & 0b1111));
+ });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vnsra16VX) {
+ SetSemanticFunction(&Vnsra);
+
+ BinaryOpTestHelperVX<uint16_t, uint32_t, uint16_t>(
+ "Vsll16", /*sew*/ 16, instruction_,
+ [](int32_t val0, uint16_t val1) -> uint16_t {
+ return static_cast<uint16_t>(val0 >> (val1 & 0b1'1111));
+ });
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vnsra32VX) {
+ SetSemanticFunction(&Vnsra);
+
+ BinaryOpTestHelperVX<uint32_t, uint64_t, uint32_t>(
+ "Vsll32", /*sew*/ 32, instruction_,
+ [](int64_t val0, uint32_t val1) -> uint32_t {
+ return static_cast<uint32_t>(val0 >> (val1 & 0b11'1111));
+ });
+}
+
+// Vector unsigned min.
+// Vector-Vector
+TEST_F(RiscVCheriotVectorInstructionsTest, Vminu8VV) {
+ SetSemanticFunction(&Vminu);
+ BinaryOpTestHelperVV<uint8_t, uint8_t, uint8_t>(
+ "Vminu8", /*sew*/ 8, instruction_,
+ [](uint8_t val0, uint8_t val1) -> uint8_t {
+ return (val0 < val1) ? val0 : val1;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vminu16VV) {
+ SetSemanticFunction(&Vminu);
+ BinaryOpTestHelperVV<uint16_t, uint16_t, uint16_t>(
+ "Vminu16", /*sew*/ 16, instruction_,
+ [](uint16_t val0, uint16_t val1) -> uint16_t {
+ return (val0 < val1) ? val0 : val1;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vminu32VV) {
+ SetSemanticFunction(&Vminu);
+ BinaryOpTestHelperVV<uint32_t, uint32_t, uint32_t>(
+ "Vminu32", /*sew*/ 32, instruction_,
+ [](uint32_t val0, uint32_t val1) -> uint32_t {
+ return (val0 < val1) ? val0 : val1;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vminu64VV) {
+ SetSemanticFunction(&Vminu);
+ BinaryOpTestHelperVV<uint64_t, uint64_t, uint64_t>(
+ "Vminu64", /*sew*/ 64, instruction_,
+ [](uint64_t val0, uint64_t val1) -> uint64_t {
+ return (val0 < val1) ? val0 : val1;
+ });
+}
+// Vector-Scalar
+TEST_F(RiscVCheriotVectorInstructionsTest, Vminu8VX) {
+ SetSemanticFunction(&Vminu);
+ BinaryOpTestHelperVX<uint8_t, uint8_t, uint8_t>(
+ "Vminu8", /*sew*/ 8, instruction_,
+ [](uint8_t val0, uint8_t val1) -> uint8_t {
+ return (val0 < val1) ? val0 : val1;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vminu16VX) {
+ SetSemanticFunction(&Vminu);
+ BinaryOpTestHelperVX<uint16_t, uint16_t, uint16_t>(
+ "Vminu16", /*sew*/ 16, instruction_,
+ [](uint16_t val0, uint16_t val1) -> uint16_t {
+ return (val0 < val1) ? val0 : val1;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vminu32VX) {
+ SetSemanticFunction(&Vminu);
+ BinaryOpTestHelperVX<uint32_t, uint32_t, uint32_t>(
+ "Vminu32", /*sew*/ 32, instruction_,
+ [](uint32_t val0, uint32_t val1) -> uint32_t {
+ return (val0 < val1) ? val0 : val1;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vminu64VX) {
+ SetSemanticFunction(&Vminu);
+ BinaryOpTestHelperVX<uint64_t, uint64_t, uint64_t>(
+ "Vminu64", /*sew*/ 64, instruction_,
+ [](uint64_t val0, uint64_t val1) -> uint64_t {
+ return (val0 < val1) ? val0 : val1;
+ });
+}
+
+// Vector signed min.
+// Vector-Vector.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmin8VV) {
+ SetSemanticFunction(&Vmin);
+ BinaryOpTestHelperVV<int8_t, int8_t, int8_t>(
+ "Vmin8", /*sew*/ 8, instruction_, [](int8_t val0, int8_t val1) -> int8_t {
+ return (val0 < val1) ? val0 : val1;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmin16VV) {
+ SetSemanticFunction(&Vmin);
+ BinaryOpTestHelperVV<int16_t, int16_t, int16_t>(
+ "Vmin16", /*sew*/ 16, instruction_,
+ [](int16_t val0, int16_t val1) -> int16_t {
+ return (val0 < val1) ? val0 : val1;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmin32VV) {
+ SetSemanticFunction(&Vmin);
+ BinaryOpTestHelperVV<int32_t, int32_t, int32_t>(
+ "Vmin32", /*sew*/ 32, instruction_,
+ [](int32_t val0, int32_t val1) -> int32_t {
+ return (val0 < val1) ? val0 : val1;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmin64VV) {
+ SetSemanticFunction(&Vmin);
+ BinaryOpTestHelperVV<int64_t, int64_t, int64_t>(
+ "Vmin64", /*sew*/ 64, instruction_,
+ [](int64_t val0, int64_t val1) -> int64_t {
+ return (val0 < val1) ? val0 : val1;
+ });
+}
+// Vector-Scalar
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmin8VX) {
+ SetSemanticFunction(&Vmin);
+ BinaryOpTestHelperVX<int8_t, int8_t, int8_t>(
+ "Vmin8", /*sew*/ 8, instruction_, [](int8_t val0, int8_t val1) -> int8_t {
+ return (val0 < val1) ? val0 : val1;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmin16VX) {
+ SetSemanticFunction(&Vmin);
+ BinaryOpTestHelperVX<int16_t, int16_t, int16_t>(
+ "Vmin16", /*sew*/ 16, instruction_,
+ [](int16_t val0, int16_t val1) -> int16_t {
+ return (val0 < val1) ? val0 : val1;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmin32VX) {
+ SetSemanticFunction(&Vmin);
+ BinaryOpTestHelperVX<int32_t, int32_t, int32_t>(
+ "Vmin32", /*sew*/ 32, instruction_,
+ [](int32_t val0, int32_t val1) -> int32_t {
+ return (val0 < val1) ? val0 : val1;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmin64VX) {
+ SetSemanticFunction(&Vmin);
+ BinaryOpTestHelperVX<int64_t, int64_t, int64_t>(
+ "Vmin64", /*sew*/ 64, instruction_,
+ [](int64_t val0, int64_t val1) -> int64_t {
+ return (val0 < val1) ? val0 : val1;
+ });
+}
+
+// Vector unsigned max.
+// Vector-Vector
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmaxu8VV) {
+ SetSemanticFunction(&Vmaxu);
+ BinaryOpTestHelperVV<uint8_t, uint8_t, uint8_t>(
+ "Vmaxu8", /*sew*/ 8, instruction_,
+ [](uint8_t val0, uint8_t val1) -> uint8_t {
+ return (val0 > val1) ? val0 : val1;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmaxu16VV) {
+ SetSemanticFunction(&Vmaxu);
+ BinaryOpTestHelperVV<uint16_t, uint16_t, uint16_t>(
+ "Vmaxu16", /*sew*/ 16, instruction_,
+ [](uint16_t val0, uint16_t val1) -> uint16_t {
+ return (val0 > val1) ? val0 : val1;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmaxu32VV) {
+ SetSemanticFunction(&Vmaxu);
+ BinaryOpTestHelperVV<uint32_t, uint32_t, uint32_t>(
+ "Vmaxu32", /*sew*/ 32, instruction_,
+ [](uint32_t val0, uint32_t val1) -> uint32_t {
+ return (val0 > val1) ? val0 : val1;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmaxu64VV) {
+ SetSemanticFunction(&Vmaxu);
+ BinaryOpTestHelperVV<uint64_t, uint64_t, uint64_t>(
+ "Vmaxu64", /*sew*/ 64, instruction_,
+ [](uint64_t val0, uint64_t val1) -> uint64_t {
+ return (val0 > val1) ? val0 : val1;
+ });
+}
+// Vector-Scalar.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmaxu8VX) {
+ SetSemanticFunction(&Vmaxu);
+ BinaryOpTestHelperVX<uint8_t, uint8_t, uint8_t>(
+ "Vmaxu8", /*sew*/ 8, instruction_,
+ [](uint8_t val0, uint8_t val1) -> uint8_t {
+ return (val0 > val1) ? val0 : val1;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmaxu16VX) {
+ SetSemanticFunction(&Vmaxu);
+ BinaryOpTestHelperVX<uint16_t, uint16_t, uint16_t>(
+ "Vmaxu16", /*sew*/ 16, instruction_,
+ [](uint16_t val0, uint16_t val1) -> uint16_t {
+ return (val0 > val1) ? val0 : val1;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmaxu32VX) {
+ SetSemanticFunction(&Vmaxu);
+ BinaryOpTestHelperVX<uint32_t, uint32_t, uint32_t>(
+ "Vmaxu32", /*sew*/ 32, instruction_,
+ [](uint32_t val0, uint32_t val1) -> uint32_t {
+ return (val0 > val1) ? val0 : val1;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmaxu64VX) {
+ SetSemanticFunction(&Vmaxu);
+ BinaryOpTestHelperVX<uint64_t, uint64_t, uint64_t>(
+ "Vmaxu64", /*sew*/ 64, instruction_,
+ [](uint64_t val0, uint64_t val1) -> uint64_t {
+ return (val0 > val1) ? val0 : val1;
+ });
+}
+// Vector signed max.
+// Vector-Vector.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmax8VV) {
+ SetSemanticFunction(&Vmax);
+ BinaryOpTestHelperVV<int8_t, int8_t, int8_t>(
+ "Vmin8", /*sew*/ 8, instruction_, [](int8_t val0, int8_t val1) -> int8_t {
+ return (val0 > val1) ? val0 : val1;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmax16VV) {
+ SetSemanticFunction(&Vmax);
+ BinaryOpTestHelperVV<int16_t, int16_t, int16_t>(
+ "Vmin16", /*sew*/ 16, instruction_,
+ [](int16_t val0, int16_t val1) -> int16_t {
+ return (val0 > val1) ? val0 : val1;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmax32VV) {
+ SetSemanticFunction(&Vmax);
+ BinaryOpTestHelperVV<int32_t, int32_t, int32_t>(
+ "Vmin32", /*sew*/ 32, instruction_,
+ [](int32_t val0, int32_t val1) -> int32_t {
+ return (val0 > val1) ? val0 : val1;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmax64VV) {
+ SetSemanticFunction(&Vmax);
+ BinaryOpTestHelperVV<int64_t, int64_t, int64_t>(
+ "Vmin64", /*sew*/ 64, instruction_,
+ [](int64_t val0, int64_t val1) -> int64_t {
+ return (val0 > val1) ? val0 : val1;
+ });
+}
+// Vector-Scalar
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmax8VX) {
+ SetSemanticFunction(&Vmax);
+ BinaryOpTestHelperVX<int8_t, int8_t, int8_t>(
+ "Vmin8", /*sew*/ 8, instruction_, [](int8_t val0, int8_t val1) -> int8_t {
+ return (val0 > val1) ? val0 : val1;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmax16VX) {
+ SetSemanticFunction(&Vmax);
+ BinaryOpTestHelperVX<int16_t, int16_t, int16_t>(
+ "Vmin16", /*sew*/ 16, instruction_,
+ [](int16_t val0, int16_t val1) -> int16_t {
+ return (val0 > val1) ? val0 : val1;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmax32VX) {
+ SetSemanticFunction(&Vmax);
+ BinaryOpTestHelperVX<int32_t, int32_t, int32_t>(
+ "Vmin32", /*sew*/ 32, instruction_,
+ [](int32_t val0, int32_t val1) -> int32_t {
+ return (val0 > val1) ? val0 : val1;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmax64VX) {
+ SetSemanticFunction(&Vmax);
+ BinaryOpTestHelperVX<int64_t, int64_t, int64_t>(
+ "Vmin64", /*sew*/ 64, instruction_,
+ [](int64_t val0, int64_t val1) -> int64_t {
+ return (val0 > val1) ? val0 : val1;
+ });
+}
+
+// Integer compare instructions.
+
+// Vector mask set equal.
+// Vector-Vector.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmseq8VV) {
+ SetSemanticFunction(&Vmseq);
+ BinaryMaskOpTestHelperVV<uint8_t, uint8_t>(
+ "Vmseq8", /*sew*/ 8, instruction_,
+ [](uint8_t val0, uint8_t val1) -> uint8_t {
+ return (val0 == val1) ? 1 : 0;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmseq16VV) {
+ SetSemanticFunction(&Vmseq);
+ BinaryMaskOpTestHelperVV<uint16_t, uint16_t>(
+ "Vmseq16", /*sew*/ 16, instruction_,
+ [](uint16_t val0, uint16_t val1) -> uint16_t {
+ return (val0 == val1) ? 1 : 0;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmseq32VV) {
+ SetSemanticFunction(&Vmseq);
+ BinaryMaskOpTestHelperVV<uint32_t, uint32_t>(
+ "Vmseq32", /*sew*/ 32, instruction_,
+ [](uint32_t val0, uint32_t val1) -> uint32_t {
+ return (val0 == val1) ? 1 : 0;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmseq64VV) {
+ SetSemanticFunction(&Vmseq);
+ BinaryMaskOpTestHelperVV<uint64_t, uint64_t>(
+ "Vmseq64", /*sew*/ 64, instruction_,
+ [](uint64_t val0, uint64_t val1) -> uint64_t {
+ return (val0 == val1) ? 1 : 0;
+ });
+}
+// Vector-Scalar.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmseq8VX) {
+ SetSemanticFunction(&Vmseq);
+ BinaryMaskOpTestHelperVX<uint8_t, uint8_t>(
+ "Vmseq8", /*sew*/ 8, instruction_,
+ [](uint8_t val0, uint8_t val1) -> uint8_t {
+ return (val0 == val1) ? 1 : 0;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmseq16VX) {
+ SetSemanticFunction(&Vmseq);
+ BinaryMaskOpTestHelperVX<uint16_t, uint16_t>(
+ "Vmseq16", /*sew*/ 16, instruction_,
+ [](uint16_t val0, uint16_t val1) -> uint8_t {
+ return (val0 == val1) ? 1 : 0;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmseq32VX) {
+ SetSemanticFunction(&Vmseq);
+ BinaryMaskOpTestHelperVX<uint32_t, uint32_t>(
+ "Vmseq32", /*sew*/ 32, instruction_,
+ [](uint32_t val0, uint32_t val1) -> uint8_t {
+ return (val0 == val1) ? 1 : 0;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmseq64VX) {
+ SetSemanticFunction(&Vmseq);
+ BinaryMaskOpTestHelperVX<uint64_t, uint64_t>(
+ "Vmseq64", /*sew*/ 64, instruction_,
+ [](uint64_t val0, uint64_t val1) -> uint8_t {
+ return (val0 == val1) ? 1 : 0;
+ });
+}
+
+// Vector mask set not equal.
+// Vector-Vector.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmsne8VV) {
+ SetSemanticFunction(&Vmsne);
+ BinaryMaskOpTestHelperVV<uint8_t, uint8_t>(
+ "Vmsne8", /*sew*/ 8, instruction_,
+ [](uint8_t val0, uint8_t val1) -> uint8_t {
+ return (val0 != val1) ? 1 : 0;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmsne16VV) {
+ SetSemanticFunction(&Vmsne);
+ BinaryMaskOpTestHelperVV<uint16_t, uint16_t>(
+ "Vmsne16", /*sew*/ 16, instruction_,
+ [](uint16_t val0, uint16_t val1) -> uint16_t {
+ return (val0 != val1) ? 1 : 0;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmsne32VV) {
+ SetSemanticFunction(&Vmsne);
+ BinaryMaskOpTestHelperVV<uint32_t, uint32_t>(
+ "Vmsne32", /*sew*/ 32, instruction_,
+ [](uint32_t val0, uint32_t val1) -> uint32_t {
+ return (val0 != val1) ? 1 : 0;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmsne64VV) {
+ SetSemanticFunction(&Vmsne);
+ BinaryMaskOpTestHelperVV<uint64_t, uint64_t>(
+ "Vmsne64", /*sew*/ 64, instruction_,
+ [](uint64_t val0, uint64_t val1) -> uint64_t {
+ return (val0 != val1) ? 1 : 0;
+ });
+}
+// Vector-Scalar.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmsne8VX) {
+ SetSemanticFunction(&Vmsne);
+ BinaryMaskOpTestHelperVX<uint8_t, uint8_t>(
+ "Vmsne8", /*sew*/ 8, instruction_,
+ [](uint8_t val0, uint8_t val1) -> uint8_t {
+ return (val0 != val1) ? 1 : 0;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmsne16VX) {
+ SetSemanticFunction(&Vmsne);
+ BinaryMaskOpTestHelperVX<uint16_t, uint16_t>(
+ "Vmsne16", /*sew*/ 16, instruction_,
+ [](uint16_t val0, uint16_t val1) -> uint8_t {
+ return (val0 != val1) ? 1 : 0;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmsne32VX) {
+ SetSemanticFunction(&Vmsne);
+ BinaryMaskOpTestHelperVX<uint32_t, uint32_t>(
+ "Vmsne32", /*sew*/ 32, instruction_,
+ [](uint32_t val0, uint32_t val1) -> uint8_t {
+ return (val0 != val1) ? 1 : 0;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmsne64VX) {
+ SetSemanticFunction(&Vmsne);
+ BinaryMaskOpTestHelperVX<uint64_t, uint64_t>(
+ "Vmsne64", /*sew*/ 64, instruction_,
+ [](uint64_t val0, uint64_t val1) -> uint8_t {
+ return (val0 != val1) ? 1 : 0;
+ });
+}
+
+// Vector mask unsigned set less than.
+// Vector-Vector.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmsltu8VV) {
+ SetSemanticFunction(&Vmsltu);
+ BinaryMaskOpTestHelperVV<uint8_t, uint8_t>(
+ "Vmsltu8", /*sew*/ 8, instruction_,
+ [](uint8_t val0, uint8_t val1) -> uint8_t {
+ return (val0 < val1) ? 1 : 0;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmsltu16VV) {
+ SetSemanticFunction(&Vmsltu);
+ BinaryMaskOpTestHelperVV<uint16_t, uint16_t>(
+ "Vmsltu16", /*sew*/ 16, instruction_,
+ [](uint16_t val0, uint16_t val1) -> uint16_t {
+ return (val0 < val1) ? 1 : 0;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmsltu32VV) {
+ SetSemanticFunction(&Vmsltu);
+ BinaryMaskOpTestHelperVV<uint32_t, uint32_t>(
+ "Vmsltu32", /*sew*/ 32, instruction_,
+ [](uint32_t val0, uint32_t val1) -> uint32_t {
+ return (val0 < val1) ? 1 : 0;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmsltu64VV) {
+ SetSemanticFunction(&Vmsltu);
+ BinaryMaskOpTestHelperVV<uint64_t, uint64_t>(
+ "Vmsltu64", /*sew*/ 64, instruction_,
+ [](uint64_t val0, uint64_t val1) -> uint64_t {
+ return (val0 < val1) ? 1 : 0;
+ });
+}
+// Vector-Scalar.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmsltu8VX) {
+ SetSemanticFunction(&Vmsltu);
+ BinaryMaskOpTestHelperVX<uint8_t, uint8_t>(
+ "Vmsltu8", /*sew*/ 8, instruction_,
+ [](uint8_t val0, uint8_t val1) -> uint8_t {
+ return (val0 < val1) ? 1 : 0;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmsltu16VX) {
+ SetSemanticFunction(&Vmsltu);
+ BinaryMaskOpTestHelperVX<uint16_t, uint16_t>(
+ "Vmsltu16", /*sew*/ 16, instruction_,
+ [](uint16_t val0, uint16_t val1) -> uint8_t {
+ return (val0 < val1) ? 1 : 0;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmsltu32VX) {
+ SetSemanticFunction(&Vmsltu);
+ BinaryMaskOpTestHelperVX<uint32_t, uint32_t>(
+ "Vmsltu32", /*sew*/ 32, instruction_,
+ [](uint32_t val0, uint32_t val1) -> uint8_t {
+ return (val0 < val1) ? 1 : 0;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmsltu64VX) {
+ SetSemanticFunction(&Vmsltu);
+ BinaryMaskOpTestHelperVX<uint64_t, uint64_t>(
+ "Vmsltu64", /*sew*/ 64, instruction_,
+ [](uint64_t val0, uint64_t val1) -> uint8_t {
+ return (val0 < val1) ? 1 : 0;
+ });
+}
+
+// Vector mask signed set less than.
+// Vector-Vector.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmslt8VV) {
+ SetSemanticFunction(&Vmslt);
+ BinaryMaskOpTestHelperVV<int8_t, int8_t>(
+ "Vmslt8", /*sew*/ 8, instruction_,
+ [](int8_t val0, int8_t val1) -> uint8_t {
+ return (val0 < val1) ? 1 : 0;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmslt16VV) {
+ SetSemanticFunction(&Vmslt);
+ BinaryMaskOpTestHelperVV<int16_t, int16_t>(
+ "Vmslt16", /*sew*/ 16, instruction_,
+ [](int16_t val0, int16_t val1) -> uint16_t {
+ return (val0 < val1) ? 1 : 0;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmslt32VV) {
+ SetSemanticFunction(&Vmslt);
+ BinaryMaskOpTestHelperVV<int32_t, int32_t>(
+ "Vmslt32", /*sew*/ 32, instruction_,
+ [](int32_t val0, int32_t val1) -> uint32_t {
+ return (val0 < val1) ? 1 : 0;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmslt64VV) {
+ SetSemanticFunction(&Vmslt);
+ BinaryMaskOpTestHelperVV<int64_t, int64_t>(
+ "Vmslt64", /*sew*/ 64, instruction_,
+ [](int64_t val0, int64_t val1) -> uint64_t {
+ return (val0 < val1) ? 1 : 0;
+ });
+}
+// Vector-Scalar.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmslt8VX) {
+ SetSemanticFunction(&Vmslt);
+ BinaryMaskOpTestHelperVX<int8_t, int8_t>(
+ "Vmslt8", /*sew*/ 8, instruction_,
+ [](int8_t val0, int8_t val1) -> uint8_t {
+ return (val0 < val1) ? 1 : 0;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmslt16VX) {
+ SetSemanticFunction(&Vmslt);
+ BinaryMaskOpTestHelperVX<int16_t, int16_t>(
+ "Vmslt16", /*sew*/ 16, instruction_,
+ [](int16_t val0, int16_t val1) -> uint8_t {
+ return (val0 < val1) ? 1 : 0;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmslt32VX) {
+ SetSemanticFunction(&Vmslt);
+ BinaryMaskOpTestHelperVX<int32_t, int32_t>(
+ "Vmslt32", /*sew*/ 32, instruction_,
+ [](int32_t val0, int32_t val1) -> uint8_t {
+ return (val0 < val1) ? 1 : 0;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmslt64VX) {
+ SetSemanticFunction(&Vmslt);
+ BinaryMaskOpTestHelperVX<int64_t, int64_t>(
+ "Vmslt64", /*sew*/ 64, instruction_,
+ [](int64_t val0, int64_t val1) -> uint8_t {
+ return (val0 < val1) ? 1 : 0;
+ });
+}
+
+// Vector mask unsigned set less than or equal.
+// Vector-Vector.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmsleu8VV) {
+ SetSemanticFunction(&Vmsleu);
+ BinaryMaskOpTestHelperVV<uint8_t, uint8_t>(
+ "Vmsleu8", /*sew*/ 8, instruction_,
+ [](uint8_t val0, uint8_t val1) -> uint8_t {
+ return (val0 <= val1) ? 1 : 0;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmsleu16VV) {
+ SetSemanticFunction(&Vmsleu);
+ BinaryMaskOpTestHelperVV<uint16_t, uint16_t>(
+ "Vmsleu16", /*sew*/ 16, instruction_,
+ [](uint16_t val0, uint16_t val1) -> uint16_t {
+ return (val0 <= val1) ? 1 : 0;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmsleu32VV) {
+ SetSemanticFunction(&Vmsleu);
+ BinaryMaskOpTestHelperVV<uint32_t, uint32_t>(
+ "Vmsleu32", /*sew*/ 32, instruction_,
+ [](uint32_t val0, uint32_t val1) -> uint32_t {
+ return (val0 <= val1) ? 1 : 0;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmsleu64VV) {
+ SetSemanticFunction(&Vmsleu);
+ BinaryMaskOpTestHelperVV<uint64_t, uint64_t>(
+ "Vmsleu64", /*sew*/ 64, instruction_,
+ [](uint64_t val0, uint64_t val1) -> uint64_t {
+ return (val0 <= val1) ? 1 : 0;
+ });
+}
+// Vector-Scalar.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmsleu8VX) {
+ SetSemanticFunction(&Vmsleu);
+ BinaryMaskOpTestHelperVX<uint8_t, uint8_t>(
+ "Vmsleu8", /*sew*/ 8, instruction_,
+ [](uint8_t val0, uint8_t val1) -> uint8_t {
+ return (val0 <= val1) ? 1 : 0;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmsleu16VX) {
+ SetSemanticFunction(&Vmsleu);
+ BinaryMaskOpTestHelperVX<uint16_t, uint16_t>(
+ "Vmsleu16", /*sew*/ 16, instruction_,
+ [](uint16_t val0, uint16_t val1) -> uint8_t {
+ return (val0 <= val1) ? 1 : 0;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmsleu32VX) {
+ SetSemanticFunction(&Vmsleu);
+ BinaryMaskOpTestHelperVX<uint32_t, uint32_t>(
+ "Vmsleu32", /*sew*/ 32, instruction_,
+ [](uint32_t val0, uint32_t val1) -> uint8_t {
+ return (val0 <= val1) ? 1 : 0;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmsleu64VX) {
+ SetSemanticFunction(&Vmsleu);
+ BinaryMaskOpTestHelperVX<uint64_t, uint64_t>(
+ "Vmsleu64", /*sew*/ 64, instruction_,
+ [](uint64_t val0, uint64_t val1) -> uint8_t {
+ return (val0 <= val1) ? 1 : 0;
+ });
+}
+
+// Vector mask signed set less than or equal.
+// Vector-Vector.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmsle8VV) {
+ SetSemanticFunction(&Vmsle);
+ BinaryMaskOpTestHelperVV<int8_t, int8_t>(
+ "Vmsle8", /*sew*/ 8, instruction_,
+ [](int8_t val0, int8_t val1) -> uint8_t {
+ return (val0 <= val1) ? 1 : 0;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmsle16VV) {
+ SetSemanticFunction(&Vmsle);
+ BinaryMaskOpTestHelperVV<int16_t, int16_t>(
+ "Vmsle16", /*sew*/ 16, instruction_,
+ [](int16_t val0, int16_t val1) -> uint16_t {
+ return (val0 <= val1) ? 1 : 0;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmsle32VV) {
+ SetSemanticFunction(&Vmsle);
+ BinaryMaskOpTestHelperVV<int32_t, int32_t>(
+ "Vmsle32", /*sew*/ 32, instruction_,
+ [](int32_t val0, int32_t val1) -> uint32_t {
+ return (val0 <= val1) ? 1 : 0;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmsle64VV) {
+ SetSemanticFunction(&Vmsle);
+ BinaryMaskOpTestHelperVV<int64_t, int64_t>(
+ "Vmsle64", /*sew*/ 64, instruction_,
+ [](int64_t val0, int64_t val1) -> uint64_t {
+ return (val0 <= val1) ? 1 : 0;
+ });
+}
+// Vector-Scalar.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmsle8VX) {
+ SetSemanticFunction(&Vmsle);
+ BinaryMaskOpTestHelperVX<int8_t, int8_t>(
+ "Vmsle8", /*sew*/ 8, instruction_,
+ [](int8_t val0, int8_t val1) -> uint8_t {
+ return (val0 <= val1) ? 1 : 0;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmsle16VX) {
+ SetSemanticFunction(&Vmsle);
+ BinaryMaskOpTestHelperVX<int16_t, int16_t>(
+ "Vmsle16", /*sew*/ 16, instruction_,
+ [](int16_t val0, int16_t val1) -> uint8_t {
+ return (val0 <= val1) ? 1 : 0;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmsle32VX) {
+ SetSemanticFunction(&Vmsle);
+ BinaryMaskOpTestHelperVX<int32_t, int32_t>(
+ "Vmsle32", /*sew*/ 32, instruction_,
+ [](int32_t val0, int32_t val1) -> uint8_t {
+ return (val0 <= val1) ? 1 : 0;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmsle64VX) {
+ SetSemanticFunction(&Vmsle);
+ BinaryMaskOpTestHelperVX<int64_t, int64_t>(
+ "Vmsle64", /*sew*/ 64, instruction_,
+ [](int64_t val0, int64_t val1) -> uint8_t {
+ return (val0 <= val1) ? 1 : 0;
+ });
+}
+
+// Vector mask unsigned set greater than.
+// Vector-Vector.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmsgtu8VX) {
+ SetSemanticFunction(&Vmsgtu);
+ BinaryMaskOpTestHelperVX<uint8_t, uint8_t>(
+ "Vmsgtu8", /*sew*/ 8, instruction_,
+ [](uint8_t val0, uint8_t val1) -> uint8_t {
+ return (val0 > val1) ? 1 : 0;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmsgtu16VX) {
+ SetSemanticFunction(&Vmsgtu);
+ BinaryMaskOpTestHelperVX<uint16_t, uint16_t>(
+ "Vmsgtu16", /*sew*/ 16, instruction_,
+ [](uint16_t val0, uint16_t val1) -> uint8_t {
+ return (val0 > val1) ? 1 : 0;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmsgtu32VX) {
+ SetSemanticFunction(&Vmsgtu);
+ BinaryMaskOpTestHelperVX<uint32_t, uint32_t>(
+ "Vmsgtu32", /*sew*/ 32, instruction_,
+ [](uint32_t val0, uint32_t val1) -> uint8_t {
+ return (val0 > val1) ? 1 : 0;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmsgtu64VX) {
+ SetSemanticFunction(&Vmsgtu);
+ BinaryMaskOpTestHelperVX<uint64_t, uint64_t>(
+ "Vmsgtuk64", /*sew*/ 64, instruction_,
+ [](uint64_t val0, uint64_t val1) -> uint8_t {
+ return (val0 > val1) ? 1 : 0;
+ });
+}
+
+// Vector mask signed set greater than.
+// Vector-Scalar.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmsgt8VX) {
+ SetSemanticFunction(&Vmsgt);
+ BinaryMaskOpTestHelperVX<int8_t, int8_t>(
+ "Vmsgt8", /*sew*/ 8, instruction_,
+ [](int8_t val0, int8_t val1) -> uint8_t {
+ return (val0 > val1) ? 1 : 0;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmsgt16VX) {
+ SetSemanticFunction(&Vmsgt);
+ BinaryMaskOpTestHelperVX<int16_t, int16_t>(
+ "Vmsgt16", /*sew*/ 16, instruction_,
+ [](int16_t val0, int16_t val1) -> uint8_t {
+ return (val0 > val1) ? 1 : 0;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmsgt32VX) {
+ SetSemanticFunction(&Vmsgt);
+ BinaryMaskOpTestHelperVX<int32_t, int32_t>(
+ "Vmsgt32", /*sew*/ 32, instruction_,
+ [](int32_t val0, int32_t val1) -> uint8_t {
+ return (val0 > val1) ? 1 : 0;
+ });
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmsgt64VX) {
+ SetSemanticFunction(&Vmsgt);
+ BinaryMaskOpTestHelperVX<int64_t, int64_t>(
+ "Vmsgt64", /*sew*/ 64, instruction_,
+ [](int64_t val0, int64_t val1) -> uint8_t {
+ return (val0 > val1) ? 1 : 0;
+ });
+}
+
+// Vector unsigned saturated add.
+template <typename T>
+T VsadduHelper(T val0, T val1) {
+ T sum = val0 + val1;
+ if (sum < val1) {
+ sum = std::numeric_limits<T>::max();
+ }
+ return sum;
+}
+// Vector-Vector.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsaddu8VV) {
+ SetSemanticFunction(&Vsaddu);
+ BinaryOpTestHelperVV<uint8_t, uint8_t, uint8_t>(
+ "Vsaddu8", /*sew*/ 8, instruction_, VsadduHelper<uint8_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsaddu16VV) {
+ SetSemanticFunction(&Vsaddu);
+ BinaryOpTestHelperVV<uint16_t, uint16_t, uint16_t>(
+ "Vsaddu16", /*sew*/ 16, instruction_, VsadduHelper<uint16_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsaddu32VV) {
+ SetSemanticFunction(&Vsaddu);
+ BinaryOpTestHelperVV<uint32_t, uint32_t, uint32_t>(
+ "Vsaddu32", /*sew*/ 32, instruction_, VsadduHelper<uint32_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsaddu64VV) {
+ SetSemanticFunction(&Vsaddu);
+ BinaryOpTestHelperVV<uint64_t, uint64_t, uint64_t>(
+ "Vsaddu64", /*sew*/ 64, instruction_, VsadduHelper<uint64_t>);
+}
+
+// Vector-Scalar
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsaddu8VX) {
+ SetSemanticFunction(&Vsaddu);
+ BinaryOpTestHelperVX<uint8_t, uint8_t, uint8_t>(
+ "Vsaddu8", /*sew*/ 8, instruction_, VsadduHelper<uint8_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsaddu16VX) {
+ SetSemanticFunction(&Vsaddu);
+ BinaryOpTestHelperVX<uint16_t, uint16_t, uint16_t>(
+ "Vsaddu16", /*sew*/ 16, instruction_, VsadduHelper<uint16_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsaddu32VX) {
+ SetSemanticFunction(&Vsaddu);
+ BinaryOpTestHelperVX<uint32_t, uint32_t, uint32_t>(
+ "Vsaddu32", /*sew*/ 32, instruction_, VsadduHelper<uint32_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsaddu64VX) {
+ SetSemanticFunction(&Vsaddu);
+ BinaryOpTestHelperVX<uint64_t, uint64_t, uint64_t>(
+ "Vsaddu64", /*sew*/ 64, instruction_, VsadduHelper<uint64_t>);
+}
+
+// Vector signed saturated add.
+template <typename T>
+T VsaddHelper(T val0, T val1) {
+ using WT = typename WideType<T>::type;
+ WT wval0 = static_cast<WT>(val0);
+ WT wval1 = static_cast<WT>(val1);
+ WT wsum = wval0 + wval1;
+ if (wsum > std::numeric_limits<T>::max()) {
+ return std::numeric_limits<T>::max();
+ }
+ if (wsum < std::numeric_limits<T>::min()) {
+ return std::numeric_limits<T>::min();
+ }
+ T sum = static_cast<T>(wsum);
+ return sum;
+}
+
+// Vector-Vector.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsadd8VV) {
+ SetSemanticFunction(&Vsadd);
+ BinaryOpTestHelperVV<int8_t, int8_t, int8_t>(
+ "Vsadd8", /*sew*/ 8, instruction_, VsaddHelper<int8_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsadd16VV) {
+ SetSemanticFunction(&Vsadd);
+ BinaryOpTestHelperVV<int16_t, int16_t, int16_t>(
+ "Vsadd16", /*sew*/ 16, instruction_, VsaddHelper<int16_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsadd32VV) {
+ SetSemanticFunction(&Vsadd);
+ BinaryOpTestHelperVV<int32_t, int32_t, int32_t>(
+ "Vsadd32", /*sew*/ 32, instruction_, VsaddHelper<int32_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsadd64VV) {
+ SetSemanticFunction(&Vsadd);
+ BinaryOpTestHelperVV<int64_t, int64_t, int64_t>(
+ "Vsadd64", /*sew*/ 64, instruction_, VsaddHelper<int64_t>);
+}
+
+// Vector-Scalar
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsadd8VX) {
+ SetSemanticFunction(&Vsadd);
+ BinaryOpTestHelperVX<int8_t, int8_t, int8_t>(
+ "Vsadd8", /*sew*/ 8, instruction_, VsaddHelper<int8_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsadd16VX) {
+ SetSemanticFunction(&Vsadd);
+ BinaryOpTestHelperVX<int16_t, int16_t, int16_t>(
+ "Vsadd16", /*sew*/ 16, instruction_, VsaddHelper<int16_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsadd32VX) {
+ SetSemanticFunction(&Vsadd);
+ BinaryOpTestHelperVX<int32_t, int32_t, int32_t>(
+ "Vsadd32", /*sew*/ 32, instruction_, VsaddHelper<int32_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsadd64VX) {
+ SetSemanticFunction(&Vsadd);
+ BinaryOpTestHelperVX<int64_t, int64_t, int64_t>(
+ "Vsadd64", /*sew*/ 64, instruction_, VsaddHelper<int64_t>);
+}
+
+// Vector unsigned saturated subtract.
+// Vector-Vector.
+template <typename T>
+T SsubuHelper(T val0, T val1) {
+ T diff = val0 - val1;
+ if (val0 < val1) {
+ diff = 0;
+ }
+ return diff;
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vssubu8VV) {
+ SetSemanticFunction(&Vssubu);
+ BinaryOpTestHelperVV<uint8_t, uint8_t, uint8_t>(
+ "Vssubu8", /*sew*/ 8, instruction_, SsubuHelper<uint8_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vssubu16VV) {
+ SetSemanticFunction(&Vssubu);
+ BinaryOpTestHelperVV<uint16_t, uint16_t, uint16_t>(
+ "Vssubu16", /*sew*/ 16, instruction_, SsubuHelper<uint16_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vssubu32VV) {
+ SetSemanticFunction(&Vssubu);
+ BinaryOpTestHelperVV<uint32_t, uint32_t, uint32_t>(
+ "Vssubu32", /*sew*/ 32, instruction_, SsubuHelper<uint32_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vssubu64VV) {
+ SetSemanticFunction(&Vssubu);
+ BinaryOpTestHelperVV<uint64_t, uint64_t, uint64_t>(
+ "Vssubu64", /*sew*/ 64, instruction_, SsubuHelper<uint64_t>);
+}
+
+// Vector-Scalar
+TEST_F(RiscVCheriotVectorInstructionsTest, Vssubu8VX) {
+ SetSemanticFunction(&Vssubu);
+ BinaryOpTestHelperVX<uint8_t, uint8_t, uint8_t>(
+ "Vssubu8", /*sew*/ 8, instruction_, SsubuHelper<uint8_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vssubu16VX) {
+ SetSemanticFunction(&Vssubu);
+ BinaryOpTestHelperVX<uint16_t, uint16_t, uint16_t>(
+ "Vssubu16", /*sew*/ 16, instruction_, SsubuHelper<uint16_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vssubu32VX) {
+ SetSemanticFunction(&Vssubu);
+ BinaryOpTestHelperVX<uint32_t, uint32_t, uint32_t>(
+ "Vssubu32", /*sew*/ 32, instruction_, SsubuHelper<uint32_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vssubu64VX) {
+ SetSemanticFunction(&Vssubu);
+ BinaryOpTestHelperVX<uint64_t, uint64_t, uint64_t>(
+ "Vssubu64", /*sew*/ 64, instruction_, SsubuHelper<uint64_t>);
+}
+
+// Vector signed saturated subtract.
+template <typename T>
+T VssubHelper(T val0, T val1) {
+ using UT = typename MakeUnsigned<T>::type;
+ UT uval0 = static_cast<UT>(val0);
+ UT uval1 = static_cast<UT>(val1);
+ UT udiff = uval0 - uval1;
+ T diff = static_cast<T>(udiff);
+ if (val0 < 0 && val1 >= 0 && diff >= 0) return std::numeric_limits<T>::min();
+ if (val0 >= 0 && val1 < 0 && diff < 0) return std::numeric_limits<T>::max();
+ return diff;
+}
+// Vector-Vector.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vssub8VV) {
+ SetSemanticFunction(&Vssub);
+ BinaryOpTestHelperVV<int8_t, int8_t, int8_t>(
+ "Vssub8", /*sew*/ 8, instruction_, VssubHelper<int8_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vssub16VV) {
+ SetSemanticFunction(&Vssub);
+ BinaryOpTestHelperVV<int16_t, int16_t, int16_t>(
+ "Vssub16", /*sew*/ 16, instruction_, VssubHelper<int16_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vssub32VV) {
+ SetSemanticFunction(&Vssub);
+ BinaryOpTestHelperVV<int32_t, int32_t, int32_t>(
+ "Vssub32", /*sew*/ 32, instruction_, VssubHelper<int32_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vssub64VV) {
+ SetSemanticFunction(&Vssub);
+ BinaryOpTestHelperVV<int64_t, int64_t, int64_t>(
+ "Vssub64", /*sew*/ 64, instruction_, VssubHelper<int64_t>);
+}
+
+// Vector-Scalar
+TEST_F(RiscVCheriotVectorInstructionsTest, Vssub8VX) {
+ SetSemanticFunction(&Vssub);
+ BinaryOpTestHelperVX<int8_t, int8_t, int8_t>(
+ "Vssub8", /*sew*/ 8, instruction_, VssubHelper<int8_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vssub16VX) {
+ SetSemanticFunction(&Vssub);
+ BinaryOpTestHelperVX<int16_t, int16_t, int16_t>(
+ "Vssub16", /*sew*/ 16, instruction_, VssubHelper<int16_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vssub32VX) {
+ SetSemanticFunction(&Vssub);
+ BinaryOpTestHelperVX<int32_t, int32_t, int32_t>(
+ "Vssub32", /*sew*/ 32, instruction_, VssubHelper<int32_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vssub64VX) {
+ SetSemanticFunction(&Vssub);
+ BinaryOpTestHelperVX<int64_t, int64_t, int64_t>(
+ "Vssub64", /*sew*/ 64, instruction_, VssubHelper<int64_t>);
+}
+
+template <typename T>
+T VadcHelper(T vs2, T vs1, bool mask) {
+ return vs2 + vs1 + mask;
+}
+
+// Vector add with carry.
+// Vector-Vector.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vadc8VV) {
+ SetSemanticFunction(&Vadc);
+ BinaryOpWithMaskTestHelperVV<uint8_t, uint8_t, uint8_t>(
+ "Vadc", /*sew*/ 8, instruction_, VadcHelper<uint8_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vadc16VV) {
+ SetSemanticFunction(&Vadc);
+ BinaryOpWithMaskTestHelperVV<uint16_t, uint16_t, uint16_t>(
+ "Vadc", /*sew*/ 16, instruction_, VadcHelper<uint16_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vadc32VV) {
+ SetSemanticFunction(&Vadc);
+ BinaryOpWithMaskTestHelperVV<uint32_t, uint32_t, uint32_t>(
+ "Vadc", /*sew*/ 32, instruction_, VadcHelper<uint32_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vadc64VV) {
+ SetSemanticFunction(&Vadc);
+ BinaryOpWithMaskTestHelperVV<uint64_t, uint64_t, uint64_t>(
+ "Vadc", /*sew*/ 64, instruction_, VadcHelper<uint64_t>);
+}
+// Vector-Scalar.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vadc8VX) {
+ SetSemanticFunction(&Vadc);
+ BinaryOpWithMaskTestHelperVX<uint8_t, uint8_t, uint8_t>(
+ "Vadc", /*sew*/ 8, instruction_, VadcHelper<uint8_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vadc16VX) {
+ SetSemanticFunction(&Vadc);
+ BinaryOpWithMaskTestHelperVX<uint16_t, uint16_t, uint16_t>(
+ "Vadc", /*sew*/ 16, instruction_, VadcHelper<uint16_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vadc32VX) {
+ SetSemanticFunction(&Vadc);
+ BinaryOpWithMaskTestHelperVX<uint32_t, uint32_t, uint32_t>(
+ "Vadc", /*sew*/ 32, instruction_, VadcHelper<uint32_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vadc64VX) {
+ SetSemanticFunction(&Vadc);
+ BinaryOpWithMaskTestHelperVX<uint64_t, uint64_t, uint64_t>(
+ "Vadc", /*sew*/ 64, instruction_, VadcHelper<uint64_t>);
+}
+
+template <typename T>
+uint8_t VmadcHelper(T vs2, T vs1, bool mask_value) {
+ T cin = ((vs2 & 0b1) + (vs1 & 0b1) + mask_value);
+ cin >>= 1;
+ vs2 >>= 1;
+ vs1 >>= 1;
+ T sum = vs2 + vs1 + cin;
+ sum >>= sizeof(T) * 8 - 1;
+ return sum;
+}
+
+// Vector compute carry from add with carry.
+// Vector-Vector.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmadc8VV) {
+ SetSemanticFunction(&Vmadc);
+ BinaryMaskOpWithMaskTestHelperVV<uint8_t, uint8_t>(
+ "Vmadc", /*sew*/ 8, instruction_, VmadcHelper<uint8_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmadc16VV) {
+ SetSemanticFunction(&Vmadc);
+ BinaryMaskOpWithMaskTestHelperVV<uint16_t, uint16_t>(
+ "Vmadc", /*sew*/ 16, instruction_, VmadcHelper<uint16_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmadc32VV) {
+ SetSemanticFunction(&Vmadc);
+ BinaryMaskOpWithMaskTestHelperVV<uint32_t, uint32_t>(
+ "Vmadc", /*sew*/ 32, instruction_, VmadcHelper<uint32_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmadc64VV) {
+ SetSemanticFunction(&Vmadc);
+ BinaryMaskOpWithMaskTestHelperVV<uint64_t, uint64_t>(
+ "Vmadc", /*sew*/ 64, instruction_, VmadcHelper<uint64_t>);
+}
+// Vector-Scalar.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmadc8VX) {
+ SetSemanticFunction(&Vmadc);
+ BinaryMaskOpWithMaskTestHelperVX<uint8_t, uint8_t>(
+ "Vmadc", /*sew*/ 8, instruction_, VmadcHelper<uint8_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmadc16VX) {
+ SetSemanticFunction(&Vmadc);
+ BinaryMaskOpWithMaskTestHelperVX<uint16_t, uint16_t>(
+ "Vmadc", /*sew*/ 16, instruction_, VmadcHelper<uint16_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmadc32VX) {
+ SetSemanticFunction(&Vmadc);
+ BinaryMaskOpWithMaskTestHelperVX<uint32_t, uint32_t>(
+ "Vmadc", /*sew*/ 32, instruction_, VmadcHelper<uint32_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmadc64VX) {
+ SetSemanticFunction(&Vmadc);
+ BinaryMaskOpWithMaskTestHelperVX<uint64_t, uint64_t>(
+ "Vmadc", /*sew*/ 64, instruction_, VmadcHelper<uint64_t>);
+}
+
+template <typename T>
+T VsbcHelper(T vs2, T vs1, bool mask) {
+ return vs2 - vs1 - mask;
+}
+// Vector subtract with borrow.
+// Vector-Vector.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsbc8VV) {
+ SetSemanticFunction(&Vsbc);
+ BinaryOpWithMaskTestHelperVV<uint8_t, uint8_t, uint8_t>(
+ "Vsbc", /*sew*/ 8, instruction_, VsbcHelper<uint8_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsbc16VV) {
+ SetSemanticFunction(&Vsbc);
+ BinaryOpWithMaskTestHelperVV<uint16_t, uint16_t, uint16_t>(
+ "Vsbc", /*sew*/ 16, instruction_, VsbcHelper<uint16_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsbc32VV) {
+ SetSemanticFunction(&Vsbc);
+ BinaryOpWithMaskTestHelperVV<uint32_t, uint32_t, uint32_t>(
+ "Vsbc", /*sew*/ 32, instruction_, VsbcHelper<uint32_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsbc64VV) {
+ SetSemanticFunction(&Vsbc);
+ BinaryOpWithMaskTestHelperVV<uint64_t, uint64_t, uint64_t>(
+ "Vsbc", /*sew*/ 64, instruction_, VsbcHelper<uint64_t>);
+}
+// Vector-Scalar.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsbc8VX) {
+ SetSemanticFunction(&Vsbc);
+ BinaryOpWithMaskTestHelperVX<uint8_t, uint8_t, uint8_t>(
+ "Vsbc", /*sew*/ 8, instruction_, VsbcHelper<uint8_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsbc16VX) {
+ SetSemanticFunction(&Vsbc);
+ BinaryOpWithMaskTestHelperVX<uint16_t, uint16_t, uint16_t>(
+ "Vsbc", /*sew*/ 16, instruction_, VsbcHelper<uint16_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsbc32VX) {
+ SetSemanticFunction(&Vsbc);
+ BinaryOpWithMaskTestHelperVX<uint32_t, uint32_t, uint32_t>(
+ "Vsbc", /*sew*/ 32, instruction_, VsbcHelper<uint32_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsbc64VX) {
+ SetSemanticFunction(&Vsbc);
+ BinaryOpWithMaskTestHelperVX<uint64_t, uint64_t, uint64_t>(
+ "Vsbc", /*sew*/ 64, instruction_, VsbcHelper<uint64_t>);
+}
+
+template <typename T>
+uint8_t VmsbcHelper(T vs2, T vs1, bool mask_value) {
+ if (vs2 == vs1) return mask_value;
+ if (vs2 < vs1) return 1;
+ return 0;
+}
+
+// Vector compute carry from subtract with borrow.
+// Vector-Vector.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmsbc8VV) {
+ SetSemanticFunction(&Vmsbc);
+ BinaryMaskOpWithMaskTestHelperVV<uint8_t, uint8_t>(
+ "Vmsbc", /*sew*/ 8, instruction_, VmsbcHelper<uint8_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmsbc16VV) {
+ SetSemanticFunction(&Vmsbc);
+ BinaryMaskOpWithMaskTestHelperVV<uint16_t, uint16_t>(
+ "Vmsbc", /*sew*/ 16, instruction_, VmsbcHelper<uint16_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmsbc32VV) {
+ SetSemanticFunction(&Vmsbc);
+ BinaryMaskOpWithMaskTestHelperVV<uint32_t, uint32_t>(
+ "Vmsbc", /*sew*/ 32, instruction_, VmsbcHelper<uint32_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmsbc64VV) {
+ SetSemanticFunction(&Vmsbc);
+ BinaryMaskOpWithMaskTestHelperVV<uint64_t, uint64_t>(
+ "Vmsbc", /*sew*/ 64, instruction_, VmsbcHelper<uint64_t>);
+}
+// Vector-Scalar.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmsbc8VX) {
+ SetSemanticFunction(&Vmsbc);
+ BinaryMaskOpWithMaskTestHelperVX<uint8_t, uint8_t>(
+ "Vmsbc", /*sew*/ 8, instruction_, VmsbcHelper<uint8_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmsbc16VX) {
+ SetSemanticFunction(&Vmsbc);
+ BinaryMaskOpWithMaskTestHelperVX<uint16_t, uint16_t>(
+ "Vmsbc", /*sew*/ 16, instruction_, VmsbcHelper<uint16_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmsbc32VX) {
+ SetSemanticFunction(&Vmsbc);
+ BinaryMaskOpWithMaskTestHelperVX<uint32_t, uint32_t>(
+ "Vmsbc", /*sew*/ 32, instruction_, VmsbcHelper<uint32_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmsbc64VX) {
+ SetSemanticFunction(&Vmsbc);
+ BinaryMaskOpWithMaskTestHelperVX<uint64_t, uint64_t>(
+ "Vmsbc", /*sew*/ 64, instruction_, VmsbcHelper<uint64_t>);
+}
+
+// Vector merge.
+template <typename T>
+T VmergeHelper(T vs2, T vs1, bool mask_value) {
+ return mask_value ? vs1 : vs2;
+}
+// Vector-Vector.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmerge8VV) {
+ SetSemanticFunction(&Vmerge);
+ BinaryOpWithMaskTestHelperVV<uint8_t, uint8_t, uint8_t>(
+ "Vmerge", /*sew*/ 8, instruction_, VmergeHelper<uint8_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmerge16VV) {
+ SetSemanticFunction(&Vmerge);
+ BinaryOpWithMaskTestHelperVV<uint16_t, uint16_t, uint16_t>(
+ "Vmerge", /*sew*/ 16, instruction_, VmergeHelper<uint16_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmerge32VV) {
+ SetSemanticFunction(&Vmerge);
+ BinaryOpWithMaskTestHelperVV<uint32_t, uint32_t, uint32_t>(
+ "mergec", /*sew*/ 32, instruction_, VmergeHelper<uint32_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmerge64VV) {
+ SetSemanticFunction(&Vmerge);
+ BinaryOpWithMaskTestHelperVV<uint64_t, uint64_t, uint64_t>(
+ "Vmerge", /*sew*/ 64, instruction_, VmergeHelper<uint64_t>);
+}
+// Vector-Scalar.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmerge8VX) {
+ SetSemanticFunction(&Vmerge);
+ BinaryOpWithMaskTestHelperVX<uint8_t, uint8_t, uint8_t>(
+ "Vmerge", /*sew*/ 8, instruction_, VmergeHelper<uint8_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmerge16VX) {
+ SetSemanticFunction(&Vmerge);
+ BinaryOpWithMaskTestHelperVX<uint16_t, uint16_t, uint16_t>(
+ "Vmerge", /*sew*/ 16, instruction_, VmergeHelper<uint16_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmerge32VX) {
+ SetSemanticFunction(&Vmerge);
+ BinaryOpWithMaskTestHelperVX<uint32_t, uint32_t, uint32_t>(
+ "mergec", /*sew*/ 32, instruction_, VmergeHelper<uint32_t>);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmerge64VX) {
+ SetSemanticFunction(&Vmerge);
+ BinaryOpWithMaskTestHelperVX<uint64_t, uint64_t, uint64_t>(
+ "Vmerge", /*sew*/ 64, instruction_, VmergeHelper<uint64_t>);
+}
+
+// This wrapper function factors out the main body of the Vmvr test.
+void VmvrWrapper(int num_reg, RiscVCheriotVectorInstructionsTest *tester,
+ Instruction *inst) {
+ tester->SetSemanticFunction(absl::bind_front(&Vmvr, num_reg));
+ // Number of elements per vector register.
+ constexpr int vs2_size = kVectorLengthInBytes / sizeof(uint64_t);
+ // Input values for 8 registers.
+ uint64_t vs2_value[vs2_size * 8];
+ auto vs2_span = Span<uint64_t>(vs2_value);
+ tester->AppendVectorRegisterOperands({kVs2}, {kVd});
+ // Initialize input values.
+ tester->FillArrayWithRandomValues<uint64_t>(vs2_span);
+ for (int i = 0; i < 8; i++) {
+ auto vs2_name = absl::StrCat("v", kVs2 + i);
+ tester->SetVectorRegisterValues<uint64_t>(
+ {{vs2_name, vs2_span.subspan(vs2_size * i, vs2_size)}});
+ }
+ tester->ClearVectorRegisterGroup(kVd, 8);
+ inst->Execute();
+ EXPECT_FALSE(tester->rv_vector()->vector_exception());
+ int count = 0;
+ for (int reg = kVd; reg < kVd + 8; reg++) {
+ auto dest_span = tester->vreg()[reg]->data_buffer()->Get<uint64_t>();
+ for (int i = 0; i < kVectorLengthInBytes / sizeof(uint64_t); i++) {
+ if (reg < kVd + num_reg) {
+ EXPECT_EQ(vs2_span[count], dest_span[i])
+ << "count: " << count << " i: " << i;
+ } else {
+ EXPECT_EQ(0, dest_span[i]) << "count: " << count << " i: " << i;
+ }
+ count++;
+ }
+ }
+}
+
+// Vector move register.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmvr1) {
+ VmvrWrapper(1, this, instruction_);
+}
+
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmvr2) {
+ VmvrWrapper(2, this, instruction_);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmvr4) {
+ VmvrWrapper(4, this, instruction_);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vmvr8) {
+ VmvrWrapper(8, this, instruction_);
+}
+
+// Templated helper functions for Vssr testing.
+template <typename T>
+T VssrHelper(RiscVCheriotVectorInstructionsTest *tester, T vs2, T vs1,
+ int rounding_mode) {
+ using UT = typename MakeUnsigned<T>::type;
+ int max_shift = (sizeof(T) << 3) - 1;
+ int shift_amount = static_cast<int>(vs1 & max_shift);
+ // Extract the bits that will be lost + 1.
+ UT lost_bits = vs2;
+ if (shift_amount < max_shift) {
+ lost_bits = vs2 & ~(std::numeric_limits<UT>::max() << (shift_amount + 1));
+ }
+ T result = vs2 >> shift_amount;
+ result += static_cast<T>(tester->RoundBits(shift_amount + 1, lost_bits));
+ return result;
+}
+
+// These wrapper functions simplify the test bodies, and make it a little
+// easier to avoid errors due to type and sew specifications.
+template <typename T>
+void VssrVVWrapper(absl::string_view base_name, Instruction *inst,
+ RiscVCheriotVectorInstructionsTest *tester) {
+ // Iterate across rounding modes.
+ for (int rm = 0; rm < 4; rm++) {
+ tester->rv_vector()->set_vxrm(rm);
+ tester->BinaryOpTestHelperVV<T, T, T>(
+ absl::StrCat("Vssrl_", rm), /*sew*/ sizeof(T) * 8, inst,
+ [rm, tester](T vs2, T vs1) -> T {
+ return VssrHelper<T>(tester, vs2, vs1, rm);
+ });
+ }
+}
+template <typename T>
+void VssrVXWrapper(absl::string_view base_name, Instruction *inst,
+ RiscVCheriotVectorInstructionsTest *tester) {
+ // Iterate across rounding modes.
+ for (int rm = 0; rm < 4; rm++) {
+ tester->rv_vector()->set_vxrm(rm);
+ tester->BinaryOpTestHelperVX<T, T, T>(
+ absl::StrCat("Vssrl_", rm), /*sew*/ sizeof(T) * 8, inst,
+ [rm, tester](T vs2, T vs1) -> T {
+ return VssrHelper<T>(tester, vs2, vs1, rm);
+ });
+ }
+}
+// Vector shift right logical with rounding.
+// Vector-Vector.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vssrl8VV) {
+ SetSemanticFunction(&Vssrl);
+ VssrVVWrapper<uint8_t>("Vssrl", instruction_, this);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vssrl16VV) {
+ SetSemanticFunction(&Vssrl);
+ VssrVVWrapper<uint16_t>("Vssrl", instruction_, this);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vssrl32VV) {
+ SetSemanticFunction(&Vssrl);
+ VssrVVWrapper<uint32_t>("Vssrl", instruction_, this);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vssrl64VV) {
+ SetSemanticFunction(&Vssrl);
+ VssrVVWrapper<uint64_t>("Vssrl", instruction_, this);
+}
+// Vector-Scalar.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vssrl8VX) {
+ SetSemanticFunction(&Vssrl);
+ VssrVXWrapper<uint8_t>("Vssrl", instruction_, this);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vssrl16VX) {
+ SetSemanticFunction(&Vssrl);
+ VssrVXWrapper<uint16_t>("Vssrl", instruction_, this);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vssrl32VX) {
+ SetSemanticFunction(&Vssrl);
+ VssrVXWrapper<uint32_t>("Vssrl", instruction_, this);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vssrl64VX) {
+ SetSemanticFunction(&Vssrl);
+ VssrVXWrapper<uint64_t>("Vssrl", instruction_, this);
+}
+
+// Vector shift right arithmetic with rounding.
+// Vector-Vector.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vssra8VV) {
+ SetSemanticFunction(&Vssra);
+ VssrVVWrapper<int8_t>("Vssra", instruction_, this);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vssra16VV) {
+ SetSemanticFunction(&Vssra);
+ VssrVVWrapper<int16_t>("Vssal", instruction_, this);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vssra32VV) {
+ SetSemanticFunction(&Vssra);
+ VssrVVWrapper<int32_t>("Vssal", instruction_, this);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vssra64VV) {
+ SetSemanticFunction(&Vssra);
+ VssrVVWrapper<int64_t>("Vssal", instruction_, this);
+}
+// Vector-Scalar.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vssra8VX) {
+ SetSemanticFunction(&Vssra);
+ VssrVXWrapper<int8_t>("Vssra", instruction_, this);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vssra16VX) {
+ SetSemanticFunction(&Vssra);
+ VssrVXWrapper<int16_t>("Vssal", instruction_, this);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vssra32VX) {
+ SetSemanticFunction(&Vssra);
+ VssrVXWrapper<int32_t>("Vssal", instruction_, this);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vssra64VX) {
+ SetSemanticFunction(&Vssra);
+ VssrVXWrapper<int64_t>("Vssal", instruction_, this);
+}
+
+// Templated helper functions for Vnclip/Vnclipu instructions.
+template <typename T, typename WideT>
+T VnclipHelper(RiscVCheriotVectorInstructionsTest *tester, WideT vs2, T vs1,
+ int rm, CheriotVectorState *rv_vector) {
+ auto vs1_16 = static_cast<WideT>(vs1);
+ auto shifted = VssrHelper<WideT>(tester, vs2, vs1_16, rm);
+ if (shifted < std::numeric_limits<T>::min()) {
+ rv_vector->set_vxsat(true);
+ return std::numeric_limits<T>::min();
+ }
+ if (shifted > std::numeric_limits<T>::max()) {
+ rv_vector->set_vxsat(true);
+ return std::numeric_limits<T>::max();
+ }
+ return static_cast<T>(shifted);
+}
+
+template <typename T>
+void VnclipVVWrapper(absl::string_view base_name, Instruction *inst,
+ RiscVCheriotVectorInstructionsTest *tester) {
+ using WT = typename WideType<T>::type;
+ for (int rm = 0; rm < 4; rm++) {
+ tester->rv_vector()->set_vxrm(rm);
+ tester->BinaryOpTestHelperVV<T, WT, T>(
+ absl::StrCat(base_name, "_", rm), sizeof(T) * 8, inst,
+ [rm, tester](WT vs2, T vs1) -> T {
+ return VnclipHelper<T, WT>(tester, vs2, vs1, rm, tester->rv_vector());
+ });
+ }
+}
+template <typename T>
+void VnclipVXWrapper(absl::string_view base_name, Instruction *inst,
+ RiscVCheriotVectorInstructionsTest *tester) {
+ using WT = typename WideType<T>::type;
+ for (int rm = 0; rm < 4; rm++) {
+ tester->rv_vector()->set_vxrm(rm);
+ tester->BinaryOpTestHelperVV<T, WT, T>(
+ absl::StrCat(base_name, "_", rm), sizeof(T) * 8, inst,
+ [rm, tester](WT vs2, T vs1) -> T {
+ return VnclipHelper<T, WT>(tester, vs2, vs1, rm, tester->rv_vector());
+ });
+ }
+}
+// Vector shift right logical with rounding and saturation.
+// Vector-Vector.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vnclip8VV) {
+ SetSemanticFunction(&Vnclip);
+ VnclipVVWrapper<int8_t>("Vnclip", instruction_, this);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vnclip16VV) {
+ SetSemanticFunction(&Vnclip);
+ VnclipVVWrapper<int16_t>("Vnclip", instruction_, this);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vnclip32VV) {
+ SetSemanticFunction(&Vnclip);
+ VnclipVVWrapper<int32_t>("Vnclip", instruction_, this);
+}
+// Vector-Scalar.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vnclip8VX) {
+ SetSemanticFunction(&Vnclip);
+ VnclipVXWrapper<int8_t>("Vnclip", instruction_, this);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vnclip16VX) {
+ SetSemanticFunction(&Vnclip);
+ VnclipVXWrapper<int16_t>("Vnclip", instruction_, this);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vnclip32VX) {
+ SetSemanticFunction(&Vnclip);
+ VnclipVXWrapper<int32_t>("Vnclip", instruction_, this);
+}
+
+// Vector shift right arithmetic with rounding and saturation.
+// Vector-Vector.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vnclipu8VV) {
+ SetSemanticFunction(&Vnclipu);
+ VnclipVVWrapper<uint8_t>("Vnclipu", instruction_, this);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vnclipu16VV) {
+ SetSemanticFunction(&Vnclipu);
+ VnclipVVWrapper<uint16_t>("Vnclipu", instruction_, this);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vnclipu32VV) {
+ SetSemanticFunction(&Vnclipu);
+ SetSemanticFunction(&Vnclipu);
+ VnclipVVWrapper<uint32_t>("Vnclipu", instruction_, this);
+}
+// Vector-Scalar.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vnclipu8VX) {
+ SetSemanticFunction(&Vnclipu);
+ VnclipVXWrapper<uint8_t>("Vnclipu", instruction_, this);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vnclipu16VX) {
+ SetSemanticFunction(&Vnclipu);
+ VnclipVXWrapper<uint16_t>("Vnclipu", instruction_, this);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vnclipu32VX) {
+ SetSemanticFunction(&Vnclipu);
+ VnclipVXWrapper<uint32_t>("Vnclipu", instruction_, this);
+}
+
+// Vector fractional multiply with rounding and saturation.
+template <typename T>
+T VsmulHelper(RiscVCheriotVectorInstructionsTest *tester, T vs2, T vs1, int rm,
+ CheriotVectorState *rv_vector) {
+ using WT = typename WideType<T>::type;
+ WT vs2_w = static_cast<WT>(vs2);
+ WT vs1_w = static_cast<WT>(vs1);
+ WT prod = vs2_w * vs1_w;
+ WT res = VssrHelper<WT>(tester, prod, sizeof(T) * 8 - 1, rm);
+ if (res > std::numeric_limits<T>::max()) {
+ return std::numeric_limits<T>::max();
+ }
+ if (res < std::numeric_limits<T>::min()) {
+ return std::numeric_limits<T>::min();
+ }
+ return static_cast<T>(res);
+}
+
+template <typename T>
+void VsmulVVWrapper(absl::string_view base_name, Instruction *inst,
+ RiscVCheriotVectorInstructionsTest *tester) {
+ for (int rm = 0; rm < 4; rm++) {
+ tester->rv_vector()->set_vxrm(rm);
+ tester->BinaryOpTestHelperVV<T, T, T>(
+ absl::StrCat(base_name, "_", rm), sizeof(T) * 8, inst,
+ [rm, tester](T vs2, T vs1) -> T {
+ return VsmulHelper<T>(tester, vs2, vs1, rm, tester->rv_vector());
+ });
+ }
+}
+template <typename T>
+void VsmulVXWrapper(absl::string_view base_name, Instruction *inst,
+ RiscVCheriotVectorInstructionsTest *tester) {
+ for (int rm = 0; rm < 4; rm++) {
+ tester->rv_vector()->set_vxrm(rm);
+ tester->BinaryOpTestHelperVV<T, T, T>(
+ absl::StrCat(base_name, "_", rm), sizeof(T) * 8, inst,
+ [rm, tester](T vs2, T vs1) -> T {
+ return VsmulHelper<T>(tester, vs2, vs1, rm, tester->rv_vector());
+ });
+ }
+}
+// Vector-Vector.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsmpy8VV) {
+ SetSemanticFunction(&Vsmul);
+ VsmulVVWrapper<int8_t>("Vsmul", instruction_, this);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsmpy16VV) {
+ SetSemanticFunction(&Vsmul);
+ VsmulVVWrapper<int16_t>("Vsuly", instruction_, this);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsmpy32VV) {
+ SetSemanticFunction(&Vsmul);
+ VsmulVVWrapper<int32_t>("Vsuly", instruction_, this);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsmpy64VV) {
+ SetSemanticFunction(&Vsmul);
+ VsmulVVWrapper<int64_t>("Vsuly", instruction_, this);
+}
+// Vector-Scalar.
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsmpy8VX) {
+ SetSemanticFunction(&Vsmul);
+ VsmulVXWrapper<int8_t>("Vsmul", instruction_, this);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsmpy16VX) {
+ SetSemanticFunction(&Vsmul);
+ VsmulVXWrapper<int16_t>("Vsuly", instruction_, this);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsmpy32VX) {
+ SetSemanticFunction(&Vsmul);
+ VsmulVXWrapper<int32_t>("Vsuly", instruction_, this);
+}
+TEST_F(RiscVCheriotVectorInstructionsTest, Vsmpy64VX) {
+ SetSemanticFunction(&Vsmul);
+ VsmulVXWrapper<int64_t>("Vsuly", instruction_, this);
+}
+} // namespace
diff --git a/cheriot/test/riscv_cheriot_vector_opm_instructions_test.cc b/cheriot/test/riscv_cheriot_vector_opm_instructions_test.cc
new file mode 100644
index 0000000..9ca5087
--- /dev/null
+++ b/cheriot/test/riscv_cheriot_vector_opm_instructions_test.cc
@@ -0,0 +1,1650 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cheriot/riscv_cheriot_vector_opm_instructions.h"
+
+#include <cstdint>
+#include <functional>
+#include <ios>
+#include <type_traits>
+
+#include "absl/base/casts.h"
+#include "absl/log/check.h"
+#include "absl/strings/string_view.h"
+#include "cheriot/test/riscv_cheriot_vector_instructions_test_base.h"
+#include "googlemock/include/gmock/gmock.h"
+#include "mpact/sim/generic/instruction.h"
+
+namespace {
+
+using Instruction = ::mpact::sim::generic::Instruction;
+using ::mpact::sim::generic::WideType;
+
+using ::mpact::sim::cheriot::Vaadd;
+using ::mpact::sim::cheriot::Vaaddu;
+using ::mpact::sim::cheriot::Vasub;
+using ::mpact::sim::cheriot::Vasubu;
+using ::mpact::sim::cheriot::Vdiv;
+using ::mpact::sim::cheriot::Vdivu;
+using ::mpact::sim::cheriot::Vmacc;
+using ::mpact::sim::cheriot::Vmadd;
+using ::mpact::sim::cheriot::Vmand;
+using ::mpact::sim::cheriot::Vmandnot;
+using ::mpact::sim::cheriot::Vmnand;
+using ::mpact::sim::cheriot::Vmnor;
+using ::mpact::sim::cheriot::Vmor;
+using ::mpact::sim::cheriot::Vmornot;
+using ::mpact::sim::cheriot::Vmul;
+using ::mpact::sim::cheriot::Vmulh;
+using ::mpact::sim::cheriot::Vmulhsu;
+using ::mpact::sim::cheriot::Vmulhu;
+using ::mpact::sim::cheriot::Vmxnor;
+using ::mpact::sim::cheriot::Vmxor;
+using ::mpact::sim::cheriot::Vnmsac;
+using ::mpact::sim::cheriot::Vnmsub;
+using ::mpact::sim::cheriot::Vrem;
+using ::mpact::sim::cheriot::Vremu;
+using ::mpact::sim::cheriot::Vwadd;
+using ::mpact::sim::cheriot::Vwaddu;
+using ::mpact::sim::cheriot::Vwadduw;
+using ::mpact::sim::cheriot::Vwaddw;
+using ::mpact::sim::cheriot::Vwmacc;
+using ::mpact::sim::cheriot::Vwmaccsu;
+using ::mpact::sim::cheriot::Vwmaccu;
+using ::mpact::sim::cheriot::Vwmaccus;
+using ::mpact::sim::cheriot::Vwmul;
+using ::mpact::sim::cheriot::Vwmulsu;
+using ::mpact::sim::cheriot::Vwmulu;
+using ::mpact::sim::cheriot::Vwsub;
+using ::mpact::sim::cheriot::Vwsubu;
+using ::mpact::sim::cheriot::Vwsubuw;
+using ::mpact::sim::cheriot::Vwsubw;
+
+// Derived test class - adds a test helper function for testing the logical
+// mask operation instructions.
+class RiscVCheriotVectorOpmInstructionsTest
+ : public RiscVCheriotVectorInstructionsTestBase {
+ protected:
+ void BinaryLogicalMaskOpTestHelper(absl::string_view name,
+ std::function<bool(bool, bool)> op) {
+ uint8_t vs2_value[kVectorLengthInBytes];
+ uint8_t vs1_value[kVectorLengthInBytes];
+ uint8_t vd_value[kVectorLengthInBytes];
+ FillArrayWithRandomValues<uint8_t>(vs2_value);
+ FillArrayWithRandomValues<uint8_t>(vs1_value);
+ FillArrayWithRandomValues<uint8_t>(vd_value);
+ AppendVectorRegisterOperands({kVs2, kVs1}, {kVd});
+ for (int vstart : {0, 7, 32, 100, 250, 384}) {
+ for (int vlen_pct : {10, 20, 50, 100}) {
+ int vlen =
+ (kVectorLengthInBytes * 8 - vstart) * vlen_pct / 100 + vstart;
+ CHECK_LE(vlen, kVectorLengthInBytes * 8);
+ // Configure vector unit for different lmul settings.
+ uint32_t vtype = (kSewSettingsByByteSize[1] << 3) | kLmulSettings[6];
+ ConfigureVectorUnit(vtype, vlen);
+ vlen = rv_vector_->vector_length();
+ rv_vector_->set_vstart(vstart);
+ SetVectorRegisterValues<uint8_t>({{kVs2Name, vs2_value},
+ {kVs1Name, vs1_value},
+ {kVdName, vd_value}});
+ instruction_->Execute();
+ auto dst_span = vreg_[kVd]->data_buffer()->Get<uint8_t>();
+ for (int i = 0; i < kVectorLengthInBytes * 8; i++) {
+ int mask_index = i >> 3;
+ int mask_offset = i & 0b111;
+ bool result = (dst_span[mask_index] >> mask_offset) & 0b1;
+ if ((i < vstart) || (i >= vlen)) {
+ bool vd = (vd_value[mask_index] >> mask_offset) & 0b1;
+ EXPECT_EQ(result, vd) << "[" << i << "] " << std::hex
+ << "vd: " << (int)vd_value[mask_index]
+ << " dst: " << (int)dst_span[mask_index];
+ } else {
+ bool vs2 = (vs2_value[mask_index] >> mask_offset) & 0b1;
+ bool vs1 = (vs1_value[mask_index] >> mask_offset) & 0b1;
+ EXPECT_EQ(result, op(vs2, vs1))
+ << "[" << i << "]: " << "op(" << vs2 << ", " << vs1 << ")";
+ }
+ }
+ }
+ }
+ }
+};
+
+// Helper functions for averaging add and subtract.
+template <typename T>
+T VaaddHelper(RiscVCheriotVectorOpmInstructionsTest *tester, T vs2, T vs1) {
+ // Create two sums, lower nibble, and the upper part. Then combine after
+ // rounding.
+ T vs2_l = vs2 & 0xf;
+ T vs1_l = vs1 & 0xf;
+ T res_l = vs2_l + vs1_l;
+ T res = (vs2 >> 4) + (vs1 >> 4);
+ res_l += tester->RoundBits<T>(2, res_l) << 1;
+ // Add carry.
+ res += (res_l >> 4);
+ // Use unsigned type to avoid undefined behavior of left-shifting negative
+ // numbers.
+ using UT = typename std::make_unsigned<T>::type;
+ res = (absl::bit_cast<UT>(res) << 3) | ((res_l >> 1) & 0b111);
+ return res;
+}
+
+template <typename T>
+T VasubHelper(RiscVCheriotVectorOpmInstructionsTest *tester, T vs2, T vs1) {
+ // Create two diffs, lower nibble, and the upper part. Then combine after
+ // rounding.
+ T vs2_l = vs2 & 0xf;
+ T vs1_l = vs1 & 0xf;
+ T res_l = vs2_l - vs1_l;
+ T res_h = (vs2 >> 4) - (vs1 >> 4);
+ // Subtract borrow.
+ res_h -= ((res_l >> 4) & 0b1);
+ // Use unsigned type to avoid undefined behavior of left-shifting negative
+ // numbers.
+ using UT = typename std::make_unsigned<T>::type;
+ T res = (absl::bit_cast<UT>(res_h) << 3) | ((res_l >> 1) & 0b111);
+ res += tester->RoundBits<T>(2, res_l);
+ return res;
+}
+
+// Vaaddu vector-vector test helper function.
+template <typename T>
+inline void VaadduVVHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ tester->SetSemanticFunction(&Vaaddu);
+ tester->BinaryOpTestHelperVV<T, T, T>(
+ absl::StrCat("Vaaddu", sizeof(T) * 8, "vv"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [tester](T val0, T val1) -> T {
+ return VaaddHelper(tester, val0, val1);
+ });
+}
+
+// Vaaddu vector-scalar test helper function.
+template <typename T>
+inline void VaadduVXHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ tester->SetSemanticFunction(&Vaaddu);
+ tester->BinaryOpTestHelperVX<T, T, T>(
+ absl::StrCat("Vaaddu", sizeof(T) * 8, "vx"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [tester](T val0, T val1) -> T {
+ return VaaddHelper(tester, val0, val1);
+ });
+}
+
+// Test Vaaddu instructions.
+TEST_F(RiscVCheriotVectorOpmInstructionsTest, Vaaddu) {
+ // vector-vector.
+ VaadduVVHelper<uint8_t>(this);
+ ResetInstruction();
+ VaadduVVHelper<uint16_t>(this);
+ ResetInstruction();
+ VaadduVVHelper<uint32_t>(this);
+ ResetInstruction();
+ VaadduVVHelper<uint64_t>(this);
+ ResetInstruction();
+ // vector-scalar.
+ VaadduVXHelper<uint8_t>(this);
+ ResetInstruction();
+ VaadduVXHelper<uint16_t>(this);
+ ResetInstruction();
+ VaadduVXHelper<uint32_t>(this);
+ ResetInstruction();
+ VaadduVXHelper<uint64_t>(this);
+}
+
+// Vaadd vector-vector test helper function.
+template <typename T>
+inline void VaaddVVHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ tester->SetSemanticFunction(&Vaadd);
+ tester->BinaryOpTestHelperVV<T, T, T>(
+ absl::StrCat("Vaaddu", sizeof(T) * 8, "vv"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [tester](T val0, T val1) -> T {
+ return VaaddHelper(tester, val0, val1);
+ });
+}
+
+// Vaadd vector-vector test helper function.
+template <typename T>
+inline void VaaddVXHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ tester->SetSemanticFunction(&Vaadd);
+ tester->BinaryOpTestHelperVX<T, T, T>(
+ absl::StrCat("Vaaddu", sizeof(T) * 8, "vx"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [tester](T val0, T val1) -> T {
+ return VaaddHelper(tester, val0, val1);
+ });
+}
+
+// Test Vaadd (signed) instructions.
+TEST_F(RiscVCheriotVectorOpmInstructionsTest, Vaadd) {
+ // Vector-vector.
+ VaaddVVHelper<int8_t>(this);
+ ResetInstruction();
+ VaaddVVHelper<int16_t>(this);
+ ResetInstruction();
+ VaaddVVHelper<int32_t>(this);
+ ResetInstruction();
+ VaaddVVHelper<int64_t>(this);
+ // Vector-scalar.
+ ResetInstruction();
+ VaaddVXHelper<int8_t>(this);
+ ResetInstruction();
+ VaaddVXHelper<int16_t>(this);
+ ResetInstruction();
+ VaaddVXHelper<int32_t>(this);
+ ResetInstruction();
+ VaaddVXHelper<int64_t>(this);
+}
+
+// Vasubu vector-vector test helper function.
+template <typename T>
+inline void VasubuVVHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ tester->SetSemanticFunction(&Vasubu);
+ tester->BinaryOpTestHelperVV<T, T, T>(
+ absl::StrCat("Vasubu", sizeof(T) * 8, "vv"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [tester](T val0, T val1) -> T {
+ return VasubHelper(tester, val0, val1);
+ });
+}
+// Vasubu vector-scalar test helper function.
+template <typename T>
+inline void VasubuVXHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ tester->SetSemanticFunction(&Vasubu);
+ tester->BinaryOpTestHelperVX<T, T, T>(
+ absl::StrCat("Vasubu", sizeof(T) * 8, "vx"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [tester](T val0, T val1) -> T {
+ return VasubHelper(tester, val0, val1);
+ });
+}
+
+// Test Vasubu (unsigned) instructions.
+TEST_F(RiscVCheriotVectorOpmInstructionsTest, Vasubu) {
+ // Vector-vector.
+ VasubuVVHelper<uint8_t>(this);
+ ResetInstruction();
+ VasubuVVHelper<uint16_t>(this);
+ ResetInstruction();
+ VasubuVVHelper<uint32_t>(this);
+ ResetInstruction();
+ VasubuVVHelper<uint64_t>(this);
+ ResetInstruction();
+ // Vector-scalar.
+ VasubuVXHelper<uint8_t>(this);
+ ResetInstruction();
+ VasubuVXHelper<uint16_t>(this);
+ ResetInstruction();
+ VasubuVXHelper<uint32_t>(this);
+ ResetInstruction();
+ VasubuVXHelper<uint64_t>(this);
+}
+
+// Vasub vector-vector test helper function.
+template <typename T>
+inline void VasubVVHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ tester->SetSemanticFunction(&Vasub);
+ tester->BinaryOpTestHelperVV<T, T, T>(
+ absl::StrCat("Vasub", sizeof(T) * 8, "vv"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [tester](T val0, T val1) -> T {
+ return VasubHelper(tester, val0, val1);
+ });
+}
+// Vasub vector-scalar test helper function.
+template <typename T>
+inline void VasubVXHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ tester->SetSemanticFunction(&Vasub);
+ tester->BinaryOpTestHelperVX<T, T, T>(
+ absl::StrCat("Vasub", sizeof(T) * 8, "vx"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [tester](T val0, T val1) -> T {
+ return VasubHelper(tester, val0, val1);
+ });
+}
+
+// Test Vasub (signed) instructions.
+TEST_F(RiscVCheriotVectorOpmInstructionsTest, Vasub) {
+ // Vector-vector.
+ VasubVVHelper<int8_t>(this);
+ ResetInstruction();
+ VasubVVHelper<int16_t>(this);
+ ResetInstruction();
+ VasubVVHelper<int32_t>(this);
+ ResetInstruction();
+ VasubVVHelper<int64_t>(this);
+ ResetInstruction();
+ // Vector-scalar.
+ VasubVXHelper<int8_t>(this);
+ ResetInstruction();
+ VasubVXHelper<int16_t>(this);
+ ResetInstruction();
+ VasubVXHelper<int32_t>(this);
+ ResetInstruction();
+ VasubVXHelper<int64_t>(this);
+}
+
+// Testing instructions that perform logical operations on vector masks.
+TEST_F(RiscVCheriotVectorOpmInstructionsTest, Vmandnot) {
+ SetSemanticFunction(&Vmandnot);
+ BinaryLogicalMaskOpTestHelper(
+ "Vmandnot", [](bool vs2, bool vs1) -> bool { return vs2 && !vs1; });
+}
+
+TEST_F(RiscVCheriotVectorOpmInstructionsTest, Vmand) {
+ SetSemanticFunction(&Vmand);
+ BinaryLogicalMaskOpTestHelper(
+ "Vmand", [](bool vs2, bool vs1) -> bool { return vs2 && vs1; });
+}
+
+TEST_F(RiscVCheriotVectorOpmInstructionsTest, Vmor) {
+ SetSemanticFunction(&Vmor);
+ BinaryLogicalMaskOpTestHelper(
+ "Vmor", [](bool vs2, bool vs1) -> bool { return vs2 || vs1; });
+}
+
+TEST_F(RiscVCheriotVectorOpmInstructionsTest, Vmxor) {
+ SetSemanticFunction(&Vmxor);
+ BinaryLogicalMaskOpTestHelper("Vmxor", [](bool vs2, bool vs1) -> bool {
+ return (vs1 && !vs2) || (!vs1 && vs2);
+ });
+}
+
+TEST_F(RiscVCheriotVectorOpmInstructionsTest, Vmornot) {
+ SetSemanticFunction(&Vmornot);
+ BinaryLogicalMaskOpTestHelper(
+ "Vmornot", [](bool vs2, bool vs1) -> bool { return vs2 || !vs1; });
+}
+
+TEST_F(RiscVCheriotVectorOpmInstructionsTest, Vmnand) {
+ SetSemanticFunction(&Vmnand);
+ BinaryLogicalMaskOpTestHelper(
+ "Vmnand", [](bool vs2, bool vs1) -> bool { return !(vs2 && vs1); });
+}
+
+TEST_F(RiscVCheriotVectorOpmInstructionsTest, Vmnor) {
+ SetSemanticFunction(&Vmnor);
+ BinaryLogicalMaskOpTestHelper(
+ "Vmnor", [](bool vs2, bool vs1) -> bool { return !(vs2 || vs1); });
+}
+
+TEST_F(RiscVCheriotVectorOpmInstructionsTest, Vmxnor) {
+ SetSemanticFunction(&Vmxnor);
+ BinaryLogicalMaskOpTestHelper("Vmxnor", [](bool vs2, bool vs1) -> bool {
+ return !((vs1 && !vs2) || (!vs1 && vs2));
+ });
+}
+
+// Vdivu vector-vector test helper function.
+template <typename T>
+inline void VdivuVVHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ tester->SetSemanticFunction(&Vdivu);
+ tester->BinaryOpTestHelperVV<T, T, T>(
+ absl::StrCat("Vdivu", sizeof(T) * 8, "vv"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [](T vs2, T vs1) -> T {
+ if (vs1 == 0) return ~vs1;
+ return vs2 / vs1;
+ });
+}
+// Vdivu vector-scalar test helper function.
+template <typename T>
+inline void VdivuVXHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ tester->SetSemanticFunction(&Vdivu);
+ tester->BinaryOpTestHelperVX<T, T, T>(
+ absl::StrCat("Vdivu", sizeof(T) * 8, "vx"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [](T vs2, T vs1) -> T {
+ if (vs1 == 0) return ~vs1;
+ return vs2 / vs1;
+ });
+}
+
+// Test vdivu instructions.
+TEST_F(RiscVCheriotVectorOpmInstructionsTest, Vdivu) {
+ // Vector-vector.
+ VdivuVVHelper<uint8_t>(this);
+ ResetInstruction();
+ VdivuVVHelper<uint16_t>(this);
+ ResetInstruction();
+ VdivuVVHelper<uint32_t>(this);
+ ResetInstruction();
+ VdivuVVHelper<uint64_t>(this);
+ ResetInstruction();
+ // Vector-scalar.
+ VdivuVXHelper<uint8_t>(this);
+ ResetInstruction();
+ VdivuVXHelper<uint16_t>(this);
+ ResetInstruction();
+ VdivuVXHelper<uint32_t>(this);
+ ResetInstruction();
+ VdivuVXHelper<uint64_t>(this);
+}
+
+// Vdiv vector-vector test helper function.
+template <typename T>
+inline void VdivVVHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ tester->SetSemanticFunction(&Vdiv);
+ tester->BinaryOpTestHelperVV<T, T, T>(
+ absl::StrCat("Vdiv", sizeof(T) * 8, "vv"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [](T vs2, T vs1) -> T {
+ if (vs1 == 0) return ~vs1;
+ return vs2 / vs1;
+ });
+}
+// Vdiv vector-scalar test helper function.
+template <typename T>
+inline void VdivVXHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ tester->SetSemanticFunction(&Vdiv);
+ tester->BinaryOpTestHelperVX<T, T, T>(
+ absl::StrCat("Vdiv", sizeof(T) * 8, "vx"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [](T vs2, T vs1) -> T {
+ if (vs1 == 0) return ~vs1;
+ return vs2 / vs1;
+ });
+}
+
+// Test vector-vector vdiv instructions.
+TEST_F(RiscVCheriotVectorOpmInstructionsTest, Vdiv) {
+ // Vector-vector.
+ VdivVVHelper<int8_t>(this);
+ ResetInstruction();
+ VdivVVHelper<int16_t>(this);
+ ResetInstruction();
+ VdivVVHelper<int32_t>(this);
+ ResetInstruction();
+ VdivVVHelper<int64_t>(this);
+ ResetInstruction();
+ // Vector-scalar.
+ VdivVXHelper<int8_t>(this);
+ ResetInstruction();
+ VdivVXHelper<int16_t>(this);
+ ResetInstruction();
+ VdivVXHelper<int32_t>(this);
+ ResetInstruction();
+ VdivVXHelper<int64_t>(this);
+}
+
+// Vremu vector-vector test helper function.
+template <typename T>
+inline void VremuVVHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ tester->SetSemanticFunction(&Vremu);
+ tester->BinaryOpTestHelperVV<T, T, T>(
+ absl::StrCat("Vremu", sizeof(T) * 8, "vv"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [](T vs2, T vs1) -> T {
+ if (vs1 == 0) return vs2;
+ return vs2 % vs1;
+ });
+}
+// Vremu vector-scalar test helper function.
+template <typename T>
+inline void VremuVXHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ tester->SetSemanticFunction(&Vremu);
+ tester->BinaryOpTestHelperVX<T, T, T>(
+ absl::StrCat("Vremu", sizeof(T) * 8, "vx"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [](T vs2, T vs1) -> T {
+ if (vs1 == 0) return vs2;
+ return vs2 % vs1;
+ });
+}
+
+// Test vremu instructions.
+TEST_F(RiscVCheriotVectorOpmInstructionsTest, Vremu) {
+ // Vector-vector.
+ VremuVVHelper<uint8_t>(this);
+ ResetInstruction();
+ VremuVVHelper<uint16_t>(this);
+ ResetInstruction();
+ VremuVVHelper<uint32_t>(this);
+ ResetInstruction();
+ VremuVVHelper<uint64_t>(this);
+ ResetInstruction();
+ // Vector-scalar.
+ VremuVXHelper<uint8_t>(this);
+ ResetInstruction();
+ VremuVXHelper<uint16_t>(this);
+ ResetInstruction();
+ VremuVXHelper<uint32_t>(this);
+ ResetInstruction();
+ VremuVXHelper<uint64_t>(this);
+}
+
+// Vrem vector-vector test helper function.
+template <typename T>
+inline void VremVVHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ tester->SetSemanticFunction(&Vrem);
+ tester->BinaryOpTestHelperVV<T, T, T>(
+ absl::StrCat("Vrem", sizeof(T) * 8, "vv"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [](T vs2, T vs1) -> T {
+ if (vs1 == 0) return vs2;
+ return vs2 % vs1;
+ });
+}
+// Vrem vector-scalar test helper function.
+template <typename T>
+inline void VremVXHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ tester->SetSemanticFunction(&Vrem);
+ tester->BinaryOpTestHelperVX<T, T, T>(
+ absl::StrCat("Vrem", sizeof(T) * 8, "vx"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [](T vs2, T vs1) -> T {
+ if (vs1 == 0) return vs2;
+ return vs2 % vs1;
+ });
+}
+
+// Test vector-vector vrem instructions.
+TEST_F(RiscVCheriotVectorOpmInstructionsTest, Vrem) {
+ // vector-vector.
+ VremVVHelper<int8_t>(this);
+ ResetInstruction();
+ VremVVHelper<int16_t>(this);
+ ResetInstruction();
+ VremVVHelper<int32_t>(this);
+ ResetInstruction();
+ VremVVHelper<int64_t>(this);
+ ResetInstruction();
+ // vector-scalar.
+ VremVXHelper<int8_t>(this);
+ ResetInstruction();
+ VremVXHelper<int16_t>(this);
+ ResetInstruction();
+ VremVXHelper<int32_t>(this);
+ ResetInstruction();
+ VremVXHelper<int64_t>(this);
+}
+
+// Vmulhu vector-vector test helper function.
+template <typename T>
+inline void VmulhuVVHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ tester->SetSemanticFunction(&Vmulhu);
+ tester->BinaryOpTestHelperVV<T, T, T>(
+ absl::StrCat("Vmulhu", sizeof(T) * 8, "vv"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [](T vs2, T vs1) -> T {
+ absl::uint128 vs2_w = static_cast<absl::uint128>(vs2);
+ absl::uint128 vs1_w = static_cast<absl::uint128>(vs1);
+ absl::uint128 vd_w = (vs2_w * vs1_w) >> (sizeof(T) * 8);
+ return static_cast<T>(vd_w);
+ });
+}
+// Vmulhu vector-scalar test helper function.
+template <typename T>
+inline void VmulhuVXHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ tester->SetSemanticFunction(&Vmulhu);
+ tester->BinaryOpTestHelperVX<T, T, T>(
+ absl::StrCat("Vmulhu", sizeof(T) * 8, "vx"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [](T vs2, T vs1) -> T {
+ absl::uint128 vs2_w = static_cast<absl::uint128>(vs2);
+ absl::uint128 vs1_w = static_cast<absl::uint128>(vs1);
+ absl::uint128 vd_w = (vs2_w * vs1_w) >> (sizeof(T) * 8);
+ return static_cast<T>(vd_w);
+ });
+}
+
+// Test vector-vector vmulhu instructions.
+TEST_F(RiscVCheriotVectorOpmInstructionsTest, Vmulhu) {
+ // vector-vector.
+ VmulhuVVHelper<uint8_t>(this);
+ ResetInstruction();
+ VmulhuVVHelper<uint16_t>(this);
+ ResetInstruction();
+ VmulhuVVHelper<uint32_t>(this);
+ ResetInstruction();
+ VmulhuVVHelper<uint64_t>(this);
+ ResetInstruction();
+ // vector-scalar.
+ VmulhuVXHelper<uint8_t>(this);
+ ResetInstruction();
+ VmulhuVXHelper<uint16_t>(this);
+ ResetInstruction();
+ VmulhuVXHelper<uint32_t>(this);
+ ResetInstruction();
+ VmulhuVXHelper<uint64_t>(this);
+}
+
+// Vmulh vector-vector test helper function.
+template <typename T>
+inline void VmulhVVHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ tester->SetSemanticFunction(&Vmulh);
+ tester->BinaryOpTestHelperVV<T, T, T>(
+ absl::StrCat("Vmulh", sizeof(T) * 8, "vv"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [](T vs2, T vs1) -> T {
+ absl::int128 vs2_w = static_cast<absl::int128>(vs2);
+ absl::int128 vs1_w = static_cast<absl::int128>(vs1);
+ absl::int128 vd_w = (vs2_w * vs1_w) >> (sizeof(T) * 8);
+ return static_cast<T>(vd_w);
+ });
+}
+
+// Vmulh vector-scalar test helper function.
+template <typename T>
+inline void VmulhVXHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ tester->SetSemanticFunction(&Vmulh);
+ tester->BinaryOpTestHelperVX<T, T, T>(
+ absl::StrCat("Vmulh", sizeof(T) * 8, "vx"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [](T vs2, T vs1) -> T {
+ absl::int128 vs2_w = static_cast<absl::int128>(vs2);
+ absl::int128 vs1_w = static_cast<absl::int128>(vs1);
+ absl::int128 vd_w = (vs2_w * vs1_w) >> (sizeof(T) * 8);
+ return static_cast<T>(vd_w);
+ });
+}
+
+// Test vector-vector vmulh instructions.
+TEST_F(RiscVCheriotVectorOpmInstructionsTest, Vmulh) {
+ // vector-vector.
+ VmulhVVHelper<int8_t>(this);
+ ResetInstruction();
+ VmulhVVHelper<int16_t>(this);
+ ResetInstruction();
+ VmulhVVHelper<int32_t>(this);
+ ResetInstruction();
+ VmulhVVHelper<int64_t>(this);
+ ResetInstruction();
+ // vector-scalar.
+ VmulhVXHelper<int8_t>(this);
+ ResetInstruction();
+ VmulhVXHelper<int16_t>(this);
+ ResetInstruction();
+ VmulhVXHelper<int32_t>(this);
+ ResetInstruction();
+ VmulhVXHelper<int64_t>(this);
+}
+
+// Vmul vector-vector test helper function.
+template <typename T>
+inline void VmulVVHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ using WT = typename WideType<T>::type;
+ tester->SetSemanticFunction(&Vmul);
+ tester->BinaryOpTestHelperVV<T, T, T>(
+ absl::StrCat("Vmul", sizeof(T) * 8, "vv"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [](T vs2, T vs1) -> T {
+ return static_cast<T>(static_cast<WT>(vs2) * static_cast<WT>(vs1));
+ });
+}
+
+// Vmul vector-scalar test helper function.
+template <typename T>
+inline void VmulVXHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ using WT = typename WideType<T>::type;
+ tester->SetSemanticFunction(&Vmul);
+ tester->BinaryOpTestHelperVX<T, T, T>(
+ absl::StrCat("Vmul", sizeof(T) * 8, "vx"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [](T vs2, T vs1) -> T {
+ return static_cast<T>(static_cast<WT>(vs2) * static_cast<WT>(vs1));
+ });
+}
+
+// Test vmulh instructions.
+TEST_F(RiscVCheriotVectorOpmInstructionsTest, Vmul) {
+ // vector-vector.
+ VmulVVHelper<int8_t>(this);
+ ResetInstruction();
+ VmulVVHelper<int16_t>(this);
+ ResetInstruction();
+ VmulVVHelper<int32_t>(this);
+ ResetInstruction();
+ VmulVVHelper<int64_t>(this);
+ ResetInstruction();
+ // vector-scalar.
+ VmulVXHelper<int8_t>(this);
+ ResetInstruction();
+ VmulVXHelper<int16_t>(this);
+ ResetInstruction();
+ VmulVXHelper<int32_t>(this);
+ ResetInstruction();
+ VmulVXHelper<int64_t>(this);
+}
+
+// Vmulhsu vector-vector test helper function.
+template <typename T>
+inline void VmulhsuVVHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ using ST = typename std::make_signed<T>::type;
+ tester->SetSemanticFunction(&Vmulhsu);
+ tester->BinaryOpTestHelperVV<T, ST, T>(
+ absl::StrCat("Vmulhsu", sizeof(T) * 8, "vv"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [](ST vs2, T vs1) -> T {
+ absl::int128 vs2_w = static_cast<absl::int128>(vs2);
+ absl::int128 vs1_w = static_cast<absl::int128>(vs1);
+ absl::int128 res = (vs2_w * vs1_w) >> (sizeof(T) * 8);
+ return static_cast<ST>(res);
+ });
+}
+
+// Vmulhsu vector-scalar test helper function.
+template <typename T>
+inline void VmulhsuVXHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ using ST = typename std::make_signed<T>::type;
+ tester->SetSemanticFunction(&Vmulhsu);
+ tester->BinaryOpTestHelperVX<T, ST, T>(
+ absl::StrCat("Vmulhsu", sizeof(T) * 8, "vx"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [](ST vs2, T vs1) -> T {
+ absl::int128 vs2_w = static_cast<absl::int128>(vs2);
+ absl::int128 vs1_w = static_cast<absl::int128>(vs1);
+ absl::int128 res = (vs2_w * vs1_w) >> (sizeof(T) * 8);
+ return static_cast<ST>(res);
+ });
+}
+
+// Test vmulhsu instructions.
+TEST_F(RiscVCheriotVectorOpmInstructionsTest, Vmulhsu) {
+ // vector-vector
+ VmulhsuVVHelper<uint8_t>(this);
+ ResetInstruction();
+ VmulhsuVVHelper<uint16_t>(this);
+ ResetInstruction();
+ VmulhsuVVHelper<uint32_t>(this);
+ ResetInstruction();
+ VmulhsuVVHelper<uint64_t>(this);
+ ResetInstruction();
+ // vector-scalar
+ VmulhsuVXHelper<uint8_t>(this);
+ ResetInstruction();
+ VmulhsuVXHelper<uint16_t>(this);
+ ResetInstruction();
+ VmulhsuVXHelper<uint32_t>(this);
+ ResetInstruction();
+ VmulhsuVXHelper<uint64_t>(this);
+}
+
+// Vmadd vector-vector test helper function.
+template <typename T>
+inline void VmaddVVHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ tester->SetSemanticFunction(&Vmadd);
+ tester->TernaryOpTestHelperVV<T, T, T>(
+ absl::StrCat("Vmadd", sizeof(T) * 8, "vv"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [](T vs2, T vs1, T vd) {
+ if (sizeof(T) < 4) {
+ uint32_t vs1_32 = vs1;
+ uint32_t vs2_32 = vs2;
+ uint32_t vd_32 = vd;
+ return static_cast<T>((vs1_32 * vd_32) + vs2_32);
+ }
+ T res = vs1 * vd + vs2;
+ return res;
+ });
+}
+
+// Vmadd vector-scalar test helper function.
+template <typename T>
+inline void VmaddVXHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ tester->SetSemanticFunction(&Vmadd);
+ tester->TernaryOpTestHelperVX<T, T, T>(
+ absl::StrCat("Vmadd", sizeof(T) * 8, "vx"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [](T vs2, T vs1, T vd) {
+ if (sizeof(T) < 4) {
+ uint32_t vs1_32 = vs1;
+ uint32_t vs2_32 = vs2;
+ uint32_t vd_32 = vd;
+ return static_cast<T>((vs1_32 * vd_32) + vs2_32);
+ }
+ T res = vs1 * vd + vs2;
+ return res;
+ });
+}
+
+// Test vmadd instructions.
+TEST_F(RiscVCheriotVectorOpmInstructionsTest, Vmadd) {
+ // vector-vector
+ VmaddVVHelper<uint8_t>(this);
+ ResetInstruction();
+ VmaddVVHelper<uint16_t>(this);
+ ResetInstruction();
+ VmaddVVHelper<uint32_t>(this);
+ ResetInstruction();
+ VmaddVVHelper<uint64_t>(this);
+ ResetInstruction();
+ // vector-scalar
+ VmaddVXHelper<uint8_t>(this);
+ ResetInstruction();
+ VmaddVXHelper<uint16_t>(this);
+ ResetInstruction();
+ VmaddVXHelper<uint32_t>(this);
+ ResetInstruction();
+ VmaddVXHelper<uint64_t>(this);
+}
+
+// Vnmsub vector-vector test helper function.
+template <typename T>
+inline void VnmsubVVHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ tester->SetSemanticFunction(&Vnmsub);
+ tester->TernaryOpTestHelperVV<T, T, T>(
+ absl::StrCat("Vnmsub", sizeof(T) * 8, "vv"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [](T vs2, T vs1, T vd) {
+ if (sizeof(T) < 4) {
+ uint32_t vs1_32 = vs1;
+ uint32_t vs2_32 = vs2;
+ uint32_t vd_32 = vd;
+ return static_cast<T>(-(vs1_32 * vd_32) + vs2_32);
+ }
+ T res = -(vs1 * vd) + vs2;
+ return res;
+ });
+}
+
+// Vnmsub vector-scalar test helper function.
+template <typename T>
+inline void VnmsubVXHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ tester->SetSemanticFunction(&Vnmsub);
+ tester->TernaryOpTestHelperVX<T, T, T>(
+ absl::StrCat("Vnmsub", sizeof(T) * 8, "vx"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [](T vs2, T vs1, T vd) {
+ if (sizeof(T) < 4) {
+ uint32_t vs1_32 = vs1;
+ uint32_t vs2_32 = vs2;
+ uint32_t vd_32 = vd;
+ return static_cast<T>(-(vs1_32 * vd_32) + vs2_32);
+ }
+ T res = -(vs1 * vd) + vs2;
+ return res;
+ });
+}
+
+// Test vnmsub instructions.
+TEST_F(RiscVCheriotVectorOpmInstructionsTest, Vnmsub) {
+ // vector-vector
+ VnmsubVVHelper<uint8_t>(this);
+ ResetInstruction();
+ VnmsubVVHelper<uint16_t>(this);
+ ResetInstruction();
+ VnmsubVVHelper<uint32_t>(this);
+ ResetInstruction();
+ VnmsubVVHelper<uint64_t>(this);
+ ResetInstruction();
+ // vector-scalar
+ VnmsubVXHelper<uint8_t>(this);
+ ResetInstruction();
+ VnmsubVXHelper<uint16_t>(this);
+ ResetInstruction();
+ VnmsubVXHelper<uint32_t>(this);
+ ResetInstruction();
+ VnmsubVXHelper<uint64_t>(this);
+}
+
+// Vmacc vector-vector test helper function.
+template <typename T>
+inline void VmaccVVHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ tester->SetSemanticFunction(&Vmacc);
+ tester->TernaryOpTestHelperVV<T, T, T>(
+ absl::StrCat("Vmacc", sizeof(T) * 8, "vv"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [](T vs2, T vs1, T vd) {
+ if (sizeof(T) < 4) {
+ uint32_t vs1_32 = vs1;
+ uint32_t vs2_32 = vs2;
+ uint32_t vd_32 = vd;
+ return static_cast<T>((vs1_32 * vs2_32) + vd_32);
+ }
+ T res = (vs1 * vs2) + vd;
+ return res;
+ });
+}
+
+// Vmacc vector-scalar test helper function.
+template <typename T>
+inline void VmaccVXHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ tester->SetSemanticFunction(&Vmacc);
+ tester->TernaryOpTestHelperVX<T, T, T>(
+ absl::StrCat("Vmacc", sizeof(T) * 8, "vx"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [](T vs2, T vs1, T vd) {
+ if (sizeof(T) < 4) {
+ uint32_t vs1_32 = vs1;
+ uint32_t vs2_32 = vs2;
+ uint32_t vd_32 = vd;
+ return static_cast<T>((vs1_32 * vs2_32) + vd_32);
+ }
+ T res = (vs1 * vs2) + vd;
+ return res;
+ });
+}
+
+// Test vmacc instructions.
+TEST_F(RiscVCheriotVectorOpmInstructionsTest, Vmacc) {
+ // vector-vector
+ VmaccVVHelper<uint8_t>(this);
+ ResetInstruction();
+ VmaccVVHelper<uint16_t>(this);
+ ResetInstruction();
+ VmaccVVHelper<uint32_t>(this);
+ ResetInstruction();
+ VmaccVVHelper<uint64_t>(this);
+ ResetInstruction();
+ // vector-scalar
+ VmaccVXHelper<uint8_t>(this);
+ ResetInstruction();
+ VmaccVXHelper<uint16_t>(this);
+ ResetInstruction();
+ VmaccVXHelper<uint32_t>(this);
+ ResetInstruction();
+ VmaccVXHelper<uint64_t>(this);
+}
+
+// Vnmsac vector-vector test helper function.
+template <typename T>
+inline void VnmsacVVHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ tester->SetSemanticFunction(&Vnmsac);
+ tester->TernaryOpTestHelperVV<T, T, T>(
+ absl::StrCat("Vnmsac", sizeof(T) * 8, "vv"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [](T vs2, T vs1, T vd) {
+ if (sizeof(T) < 4) {
+ uint32_t vs1_32 = vs1;
+ uint32_t vs2_32 = vs2;
+ uint32_t vd_32 = vd;
+ return static_cast<T>(-(vs1_32 * vs2_32) + vd_32);
+ }
+ T res = -(vs1 * vs2) + vd;
+ return res;
+ });
+}
+
+// Vnmsac vector-scalar test helper function.
+template <typename T>
+inline void VnmsacVXHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ tester->SetSemanticFunction(&Vnmsac);
+ tester->TernaryOpTestHelperVX<T, T, T>(
+ absl::StrCat("Vnmsac", sizeof(T) * 8, "vx"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [](T vs2, T vs1, T vd) {
+ if (sizeof(T) < 4) {
+ uint32_t vs1_32 = vs1;
+ uint32_t vs2_32 = vs2;
+ uint32_t vd_32 = vd;
+ return static_cast<T>(-(vs1_32 * vs2_32) + vd_32);
+ }
+ T res = -(vs1 * vs2) + vd;
+ return res;
+ });
+}
+
+// Test vnmsac instructions.
+TEST_F(RiscVCheriotVectorOpmInstructionsTest, Vnmsac) {
+ // vector-vector
+ VnmsacVVHelper<uint8_t>(this);
+ ResetInstruction();
+ VnmsacVVHelper<uint16_t>(this);
+ ResetInstruction();
+ VnmsacVVHelper<uint32_t>(this);
+ ResetInstruction();
+ VnmsacVVHelper<uint64_t>(this);
+ ResetInstruction();
+ // vector-scalar
+ VnmsacVXHelper<uint8_t>(this);
+ ResetInstruction();
+ VnmsacVXHelper<uint16_t>(this);
+ ResetInstruction();
+ VnmsacVXHelper<uint32_t>(this);
+ ResetInstruction();
+ VnmsacVXHelper<uint64_t>(this);
+}
+
+// Vwaddu vector-vector test helper function.
+template <typename T>
+inline void VwadduVVHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ using WT = typename WideType<T>::type;
+ tester->SetSemanticFunction(&Vwaddu);
+ tester->BinaryOpTestHelperVV<WT, T, T>(
+ absl::StrCat("Vwaddu", sizeof(T) * 8, "vv"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [](T vs2, T vs1) -> WT {
+ return static_cast<WT>(vs2) + static_cast<WT>(vs1);
+ });
+}
+
+// Vwaddu vector-scalar test helper function.
+template <typename T>
+inline void VwadduVXHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ using WT = typename WideType<T>::type;
+ tester->SetSemanticFunction(&Vwaddu);
+ tester->BinaryOpTestHelperVX<WT, T, T>(
+ absl::StrCat("Vwaddu", sizeof(T) * 8, "vx"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [](T vs2, T vs1) -> WT {
+ return static_cast<WT>(vs2) + static_cast<WT>(vs1);
+ });
+}
+
+// Vector widening unsigned add. (sew * 2) = sew + sew
+// There is no test for sew == 64 bits, as this is a widening operation,
+// and 64 bit values are the max sized vector elements.
+TEST_F(RiscVCheriotVectorOpmInstructionsTest, Vwaddu) {
+ // vector-vector.
+ VwadduVVHelper<uint8_t>(this);
+ ResetInstruction();
+ VwadduVVHelper<uint16_t>(this);
+ ResetInstruction();
+ VwadduVVHelper<uint32_t>(this);
+ ResetInstruction();
+ // vector-scalar.
+ VwadduVXHelper<uint8_t>(this);
+ ResetInstruction();
+ VwadduVXHelper<uint16_t>(this);
+ ResetInstruction();
+ VwadduVXHelper<uint32_t>(this);
+}
+
+// Vwsubu vector-vector test helper function.
+template <typename T>
+inline void VwsubuVVHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ using WT = typename WideType<T>::type;
+ tester->SetSemanticFunction(&Vwsubu);
+ tester->BinaryOpTestHelperVV<WT, T, T>(
+ absl::StrCat("Vwsubu", sizeof(T) * 8, "vv"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [](T vs2, T vs1) -> WT {
+ return static_cast<WT>(vs2) - static_cast<WT>(vs1);
+ });
+}
+
+// Vwsubu vector-scalar test helper function.
+template <typename T>
+inline void VwsubuVXHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ using WT = typename WideType<T>::type;
+ tester->SetSemanticFunction(&Vwsubu);
+ tester->BinaryOpTestHelperVX<WT, T, T>(
+ absl::StrCat("Vwsubu", sizeof(T) * 8, "vx"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [](T vs2, T vs1) -> WT {
+ return static_cast<WT>(vs2) - static_cast<WT>(vs1);
+ });
+}
+
+// Vector widening unsigned subtract. (sew * 2) = sew + sew
+// There is no test for sew == 64 bits, as this is a widening operation,
+// and 64 bit values are the max sized vector elements.
+TEST_F(RiscVCheriotVectorOpmInstructionsTest, Vwsubu) {
+ // vector-vector.
+ VwsubuVVHelper<uint8_t>(this);
+ ResetInstruction();
+ VwsubuVVHelper<uint16_t>(this);
+ ResetInstruction();
+ VwsubuVVHelper<uint32_t>(this);
+ ResetInstruction();
+ // vector-scalar.
+ VwsubuVXHelper<uint8_t>(this);
+ ResetInstruction();
+ VwsubuVXHelper<uint16_t>(this);
+ ResetInstruction();
+ VwsubuVXHelper<uint32_t>(this);
+}
+
+// Vwadd vector-vector test helper function.
+template <typename T>
+inline void VwaddVVHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ using WT = typename WideType<T>::type;
+ tester->SetSemanticFunction(&Vwadd);
+ tester->BinaryOpTestHelperVV<WT, T, T>(
+ absl::StrCat("Vwadd", sizeof(T) * 8, "vv"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [](T vs2, T vs1) -> WT {
+ return static_cast<WT>(vs2) + static_cast<WT>(vs1);
+ });
+}
+
+// Vwadd vector-scalar test helper function.
+template <typename T>
+inline void VwaddVXHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ using WT = typename WideType<T>::type;
+ tester->SetSemanticFunction(&Vwadd);
+ tester->BinaryOpTestHelperVX<WT, T, T>(
+ absl::StrCat("Vwadd", sizeof(T) * 8, "vx"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [](T vs2, T vs1) -> WT {
+ return static_cast<WT>(vs2) + static_cast<WT>(vs1);
+ });
+}
+
+// Vector videning signed addition. (sew * 2) = sew + sew.
+// There is no test for sew == 64 bits, as this is a widening operation,
+// and 64 bit values are the max sized vector elements.
+TEST_F(RiscVCheriotVectorOpmInstructionsTest, Vwadd) {
+ // vector-vector.
+ VwaddVVHelper<int8_t>(this);
+ ResetInstruction();
+ VwaddVVHelper<int16_t>(this);
+ ResetInstruction();
+ VwaddVVHelper<int32_t>(this);
+ ResetInstruction();
+ // vector-scalar.
+ VwaddVXHelper<int8_t>(this);
+ ResetInstruction();
+ VwaddVXHelper<int16_t>(this);
+ ResetInstruction();
+ VwaddVXHelper<int32_t>(this);
+}
+
+// Vwsub vector-vector test helper function.
+template <typename T>
+inline void VwsubVVHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ using WT = typename WideType<T>::type;
+ tester->SetSemanticFunction(&Vwsub);
+ tester->BinaryOpTestHelperVV<WT, T, T>(
+ absl::StrCat("Vwsub", sizeof(T) * 8, "vv"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [](T vs2, T vs1) -> WT {
+ WT vs2_w = vs2;
+ WT vs1_w = vs1;
+ WT res = vs2_w - vs1_w;
+ return res;
+ });
+}
+
+// Vwsub vector-scalar test helper function.
+template <typename T>
+inline void VwsubVXHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ using WT = typename WideType<T>::type;
+ tester->SetSemanticFunction(&Vwsub);
+ tester->BinaryOpTestHelperVX<WT, T, T>(
+ absl::StrCat("Vwsub", sizeof(T) * 8, "vx"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [](T vs2, T vs1) -> WT {
+ WT vs2_w = vs2;
+ WT vs1_w = vs1;
+ WT res = vs2_w - vs1_w;
+ return res;
+ });
+}
+
+// Vector widening unsigned subtract. (sew * 2) = sew + sew
+// There is no test for sew == 64 bits, as this is a widening operation,
+// and 64 bit values are the max sized vector elements.
+TEST_F(RiscVCheriotVectorOpmInstructionsTest, Vwsub) {
+ // vector-vector.
+ VwsubVVHelper<int8_t>(this);
+ ResetInstruction();
+ VwsubVVHelper<int16_t>(this);
+ ResetInstruction();
+ VwsubVVHelper<int32_t>(this);
+ ResetInstruction();
+ // vector-scalar.
+ VwsubVXHelper<int8_t>(this);
+ ResetInstruction();
+ VwsubVXHelper<int16_t>(this);
+ ResetInstruction();
+ VwsubVXHelper<int32_t>(this);
+}
+
+// Vwadduw vector-vector test helper function.
+template <typename T>
+inline void VwadduwVVHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ using WT = typename WideType<T>::type;
+ tester->SetSemanticFunction(&Vwadduw);
+ tester->BinaryOpTestHelperVV<WT, WT, T>(
+ absl::StrCat("Vwadduw", sizeof(T) * 8, "vv"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(),
+ [](WT vs2, T vs1) -> WT { return vs2 + static_cast<WT>(vs1); });
+}
+
+// Vwadduw vector-scalar test helper function.
+template <typename T>
+inline void VwadduwVXHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ using WT = typename WideType<T>::type;
+ tester->SetSemanticFunction(&Vwadduw);
+ tester->BinaryOpTestHelperVX<WT, WT, T>(
+ absl::StrCat("Vwadduw", sizeof(T) * 8, "vx"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(),
+ [](WT vs2, T vs1) -> WT { return vs2 + static_cast<WT>(vs1); });
+}
+
+// Vector widening unsigned add. (sew * 2) = (sew * 2) + sew
+// There is no test for sew == 64 bits, as this is a widening operation,
+// and 64 bit values are the max sized vector elements.
+TEST_F(RiscVCheriotVectorOpmInstructionsTest, Vwadduw) {
+ // vector-vector.
+ VwadduwVVHelper<uint8_t>(this);
+ ResetInstruction();
+ VwadduwVVHelper<uint16_t>(this);
+ ResetInstruction();
+ VwadduwVVHelper<uint32_t>(this);
+ ResetInstruction();
+ // vector-scalar.
+ VwadduwVXHelper<uint8_t>(this);
+ ResetInstruction();
+ VwadduwVXHelper<uint16_t>(this);
+ ResetInstruction();
+ VwadduwVXHelper<uint32_t>(this);
+}
+
+// Vwsubuw vector-vector test helper function.
+template <typename T>
+inline void VwsubuwVVHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ using WT = typename WideType<T>::type;
+ tester->SetSemanticFunction(&Vwsubuw);
+ tester->BinaryOpTestHelperVV<WT, WT, T>(
+ absl::StrCat("Vwsubuw", sizeof(T) * 8, "vv"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(),
+ [](WT vs2, T vs1) -> WT { return vs2 - static_cast<WT>(vs1); });
+}
+
+// Vwsubuw vector-scalar test helper function.
+template <typename T>
+inline void VwsubuwVXHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ using WT = typename WideType<T>::type;
+ tester->SetSemanticFunction(&Vwsubuw);
+ tester->BinaryOpTestHelperVX<WT, WT, T>(
+ absl::StrCat("Vwsubuw", sizeof(T) * 8, "vx"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(),
+ [](WT vs2, T vs1) -> WT { return vs2 - static_cast<WT>(vs1); });
+}
+
+// Vector widening unsigned subtract. (sew * 2) = (sew * 2) + sew
+// There is no test for sew == 64 bits, as this is a widening operation,
+// and 64 bit values are the max sized vector elements.
+TEST_F(RiscVCheriotVectorOpmInstructionsTest, Vwsubuw) {
+ // vector-vector.
+ VwsubuwVVHelper<uint8_t>(this);
+ ResetInstruction();
+ VwsubuwVVHelper<uint16_t>(this);
+ ResetInstruction();
+ VwsubuwVVHelper<uint32_t>(this);
+ ResetInstruction();
+ // vector-scalar.
+ VwsubuwVXHelper<uint8_t>(this);
+ ResetInstruction();
+ VwsubuwVXHelper<uint16_t>(this);
+ ResetInstruction();
+ VwsubuwVXHelper<uint32_t>(this);
+}
+
+// Vwaddw vector-vector test helper function.
+template <typename T>
+inline void VwaddwVVHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ using WT = typename WideType<T>::type;
+ tester->SetSemanticFunction(&Vwaddw);
+ tester->BinaryOpTestHelperVV<WT, WT, T>(
+ absl::StrCat("Vwaddw", sizeof(T) * 8, "vv"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(),
+ [](WT vs2, T vs1) -> WT { return vs2 + static_cast<WT>(vs1); });
+}
+
+// Vwaddw vector-scalar test helper function.
+template <typename T>
+inline void VwaddwVXHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ using WT = typename WideType<T>::type;
+ tester->SetSemanticFunction(&Vwaddw);
+ tester->BinaryOpTestHelperVX<WT, WT, T>(
+ absl::StrCat("Vwaddw", sizeof(T) * 8, "vx"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(),
+ [](WT vs2, T vs1) -> WT { return vs2 + static_cast<WT>(vs1); });
+}
+
+// Vector widening signed add. (sew * 2) = (sew * 2) + sew
+// There is no test for sew == 64 bits, as this is a widening operation,
+// and 64 bit values are the max sized vector elements.
+TEST_F(RiscVCheriotVectorOpmInstructionsTest, Vwaddw) {
+ // vector-vector.
+ VwaddwVVHelper<int8_t>(this);
+ ResetInstruction();
+ VwaddwVVHelper<int16_t>(this);
+ ResetInstruction();
+ VwaddwVVHelper<int32_t>(this);
+ ResetInstruction();
+ // vector-scalar.
+ VwaddwVXHelper<int8_t>(this);
+ ResetInstruction();
+ VwaddwVXHelper<int16_t>(this);
+ ResetInstruction();
+ VwaddwVXHelper<int32_t>(this);
+}
+
+// Vwsubw vector-vector test helper function.
+template <typename T>
+inline void VwsubwVVHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ using WT = typename WideType<T>::type;
+ tester->SetSemanticFunction(&Vwsubw);
+ tester->BinaryOpTestHelperVV<WT, WT, T>(
+ absl::StrCat("Vwsubw", sizeof(T) * 8, "vv"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(),
+ [](WT vs2, T vs1) -> WT { return vs2 - static_cast<WT>(vs1); });
+}
+
+// Vwsubw vector-scalar test helper function.
+template <typename T>
+inline void VwsubwVXHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ using WT = typename WideType<T>::type;
+ tester->SetSemanticFunction(&Vwsubw);
+ tester->BinaryOpTestHelperVX<WT, WT, T>(
+ absl::StrCat("Vwsubw", sizeof(T) * 8, "vx"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(),
+ [](WT vs2, T vs1) -> WT { return vs2 - static_cast<WT>(vs1); });
+}
+
+// Vector widening signed subtract. (sew * 2) = (sew * 2) + sew
+// There is no test for sew == 64 bits, as this is a widening operation,
+// and 64 bit values are the max sized vector elements.
+TEST_F(RiscVCheriotVectorOpmInstructionsTest, Vwsubw) {
+ // vector-vector.
+ VwsubwVVHelper<int8_t>(this);
+ ResetInstruction();
+ VwsubwVVHelper<int16_t>(this);
+ ResetInstruction();
+ VwsubwVVHelper<int32_t>(this);
+ ResetInstruction();
+ // vector-scalar.
+ VwsubwVXHelper<int8_t>(this);
+ ResetInstruction();
+ VwsubwVXHelper<int16_t>(this);
+ ResetInstruction();
+ VwsubwVXHelper<int32_t>(this);
+}
+
+// Vwmul vector-vector test helper function.
+template <typename T>
+inline void VwmuluVVHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ using WT = typename WideType<T>::type;
+ tester->SetSemanticFunction(&Vwmulu);
+ tester->BinaryOpTestHelperVV<WT, T, T>(
+ absl::StrCat("Vwmulu", sizeof(T) * 8, "vv"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [](T vs2, T vs1) -> WT {
+ return static_cast<WT>(vs2) * static_cast<WT>(vs1);
+ });
+}
+
+// Vwmulu vector-scalar test helper function.
+template <typename T>
+inline void VwmuluVXHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ using WT = typename WideType<T>::type;
+ tester->SetSemanticFunction(&Vwmulu);
+ tester->BinaryOpTestHelperVX<WT, T, T>(
+ absl::StrCat("Vwmulu", sizeof(T) * 8, "vx"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [](T vs2, T vs1) -> WT {
+ return static_cast<WT>(vs2) * static_cast<WT>(vs1);
+ });
+}
+
+// Vector widening signed multiply. (sew * 2) = sew + sew
+// There is no test for sew == 64 bits, as this is a widening operation,
+// and 64 bit values are the max sized vector elements.
+TEST_F(RiscVCheriotVectorOpmInstructionsTest, Vwmulu) {
+ // vector-vector.
+ VwmuluVVHelper<uint8_t>(this);
+ ResetInstruction();
+ VwmuluVVHelper<uint16_t>(this);
+ ResetInstruction();
+ VwmuluVVHelper<uint32_t>(this);
+ ResetInstruction();
+ // vector-scalar.
+ VwmuluVXHelper<uint8_t>(this);
+ ResetInstruction();
+ VwmuluVXHelper<uint16_t>(this);
+ ResetInstruction();
+ VwmuluVXHelper<uint32_t>(this);
+}
+
+// Vwmul vector-vector test helper function.
+template <typename T>
+inline void VwmulVVHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ using WT = typename WideType<T>::type;
+ tester->SetSemanticFunction(&Vwmul);
+ tester->BinaryOpTestHelperVV<WT, T, T>(
+ absl::StrCat("Vwmul", sizeof(T) * 8, "vv"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [](T vs2, T vs1) -> WT {
+ return static_cast<WT>(vs2) * static_cast<WT>(vs1);
+ });
+}
+
+// Vwmul vector-scalar test helper function.
+template <typename T>
+inline void VwmulVXHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ using WT = typename WideType<T>::type;
+ tester->SetSemanticFunction(&Vwmul);
+ tester->BinaryOpTestHelperVX<WT, T, T>(
+ absl::StrCat("Vwmul", sizeof(T) * 8, "vx"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [](T vs2, T vs1) -> WT {
+ return static_cast<WT>(vs2) * static_cast<WT>(vs1);
+ });
+}
+
+// Vector widening signed multiply. (sew * 2) = sew + sew
+// There is no test for sew == 64 bits, as this is a widening operation,
+// and 64 bit values are the max sized vector elements.
+TEST_F(RiscVCheriotVectorOpmInstructionsTest, Vwmul) {
+ // vector-vector.
+ VwmulVVHelper<int8_t>(this);
+ ResetInstruction();
+ VwmulVVHelper<int16_t>(this);
+ ResetInstruction();
+ VwmulVVHelper<int32_t>(this);
+ ResetInstruction();
+ // vector-scalar.
+ VwmulVXHelper<int8_t>(this);
+ ResetInstruction();
+ VwmulVXHelper<int16_t>(this);
+ ResetInstruction();
+ VwmulVXHelper<int32_t>(this);
+}
+
+// Vwmul vector-vector test helper function.
+template <typename T>
+inline void VwmulsuVVHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ using WT = typename WideType<T>::type;
+ using UT = typename std::make_unsigned<T>::type;
+ tester->SetSemanticFunction(&Vwmulsu);
+ tester->BinaryOpTestHelperVV<WT, T, T>(
+ absl::StrCat("Vwmulsu", sizeof(T) * 8, "vv"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [](T vs2, UT vs1) -> WT {
+ return static_cast<WT>(vs2) * static_cast<WT>(vs1);
+ });
+}
+
+// Vwmulsu vector-scalar test helper function.
+template <typename T>
+inline void VwmulsuVXHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ using WT = typename WideType<T>::type;
+ using UT = typename std::make_unsigned<T>::type;
+ tester->SetSemanticFunction(&Vwmulsu);
+ tester->BinaryOpTestHelperVX<WT, T, T>(
+ absl::StrCat("Vwmulsu", sizeof(T) * 8, "vx"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [](T vs2, UT vs1) -> WT {
+ return static_cast<WT>(vs2) * static_cast<WT>(vs1);
+ });
+}
+
+// Vector widening signed multiply. (sew * 2) = sew + sew
+// There is no test for sew == 64 bits, as this is a widening operation,
+// and 64 bit values are the max sized vector elements.
+TEST_F(RiscVCheriotVectorOpmInstructionsTest, Vwmulsu) {
+ // vector-vector.
+ VwmulsuVVHelper<int8_t>(this);
+ ResetInstruction();
+ VwmulsuVVHelper<int16_t>(this);
+ ResetInstruction();
+ VwmulsuVVHelper<int32_t>(this);
+ ResetInstruction();
+ // vector-scalar.
+ VwmulsuVXHelper<int8_t>(this);
+ ResetInstruction();
+ VwmulsuVXHelper<int16_t>(this);
+ ResetInstruction();
+ VwmulsuVXHelper<int32_t>(this);
+}
+
+// Vmaccu vector-vector test helper function.
+template <typename T>
+inline void VwmaccuVVHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ using WT = typename WideType<T>::type;
+ tester->SetSemanticFunction(&Vwmaccu);
+ tester->TernaryOpTestHelperVV<WT, T, T>(
+ absl::StrCat("Vwmaccu", sizeof(T) * 8, "vv"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [](T vs2, T vs1, WT vd) {
+ return static_cast<WT>(vs2) * static_cast<WT>(vs1) + vd;
+ });
+}
+
+// Vwmaccu vector-scalar test helper function.
+template <typename T>
+inline void VwmaccuVXHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ using WT = typename WideType<T>::type;
+ tester->SetSemanticFunction(&Vwmaccu);
+ tester->TernaryOpTestHelperVX<WT, T, T>(
+ absl::StrCat("Vwmaccu", sizeof(T) * 8, "vx"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [](T vs2, T vs1, WT vd) {
+ return static_cast<WT>(vs2) * static_cast<WT>(vs1) + vd;
+ });
+}
+
+// Test vwmaccu instructions.
+TEST_F(RiscVCheriotVectorOpmInstructionsTest, Vwmaccu) {
+ // vector-vector
+ VwmaccuVVHelper<uint8_t>(this);
+ ResetInstruction();
+ VwmaccuVVHelper<uint16_t>(this);
+ ResetInstruction();
+ VwmaccuVVHelper<uint32_t>(this);
+ ResetInstruction();
+ // vector-scalar
+ VwmaccuVXHelper<uint8_t>(this);
+ ResetInstruction();
+ VwmaccuVXHelper<uint16_t>(this);
+ ResetInstruction();
+ VwmaccuVXHelper<uint32_t>(this);
+}
+
+// Vmacc vector-vector test helper function.
+template <typename T>
+inline void VwmaccVVHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ using WT = typename WideType<T>::type;
+ tester->SetSemanticFunction(&Vwmacc);
+ tester->TernaryOpTestHelperVV<WT, T, T>(
+ absl::StrCat("Vwmacc", sizeof(T) * 8, "vv"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [](T vs2, T vs1, WT vd) -> WT {
+ WT vs1_w = vs1;
+ WT vs2_w = vs2;
+ WT prod = vs1_w * vs2_w;
+ using UWT = typename std::make_unsigned<WT>::type;
+ WT res = absl::bit_cast<UWT>(prod) + absl::bit_cast<UWT>(vd);
+ return res;
+ });
+}
+
+// Vwmacc vector-scalar test helper function.
+template <typename T>
+inline void VwmaccVXHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ using WT = typename WideType<T>::type;
+ tester->SetSemanticFunction(&Vwmacc);
+ tester->TernaryOpTestHelperVX<WT, T, T>(
+ absl::StrCat("Vwmacc", sizeof(T) * 8, "vx"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [](T vs2, T vs1, WT vd) -> WT {
+ WT vs1_w = vs1;
+ WT vs2_w = vs2;
+ WT prod = vs1_w * vs2_w;
+ using UWT = typename std::make_unsigned<WT>::type;
+ WT res = absl::bit_cast<UWT>(prod) + absl::bit_cast<UWT>(vd);
+ return res;
+ });
+}
+
+// Test vwmacc instructions.
+TEST_F(RiscVCheriotVectorOpmInstructionsTest, Vwmacc) {
+ // vector-vector
+ VwmaccVVHelper<int8_t>(this);
+ ResetInstruction();
+ VwmaccVVHelper<int16_t>(this);
+ ResetInstruction();
+ VwmaccVVHelper<int32_t>(this);
+ ResetInstruction();
+ // vector-scalar
+ VwmaccVXHelper<int8_t>(this);
+ ResetInstruction();
+ VwmaccVXHelper<int16_t>(this);
+ ResetInstruction();
+ VwmaccVXHelper<int32_t>(this);
+}
+
+// Vmaccus vector-vector test helper function.
+template <typename T>
+inline void VwmaccusVVHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ using WT = typename WideType<T>::type;
+ using UT = typename std::make_unsigned<T>::type;
+ tester->SetSemanticFunction(&Vwmaccus);
+ tester->TernaryOpTestHelperVV<WT, T, UT>(
+ absl::StrCat("Vwmaccus", sizeof(T) * 8, "vv"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [](T vs2, UT vs1, WT vd) -> WT {
+ using UWT = typename std::make_unsigned<WT>::type;
+ UWT vs1_w = vs1;
+ WT vs2_w = vs2;
+ WT prod = vs1_w * vs2_w;
+ WT res = absl::bit_cast<UWT>(prod) + absl::bit_cast<UWT>(vd);
+ return res;
+ });
+}
+
+// Vwmaccus vector-scalar test helper function.
+template <typename T>
+inline void VwmaccusVXHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ using WT = typename WideType<T>::type;
+ using UT = typename std::make_unsigned<T>::type;
+ tester->SetSemanticFunction(&Vwmaccus);
+ tester->TernaryOpTestHelperVX<WT, T, UT>(
+ absl::StrCat("Vwmaccus", sizeof(T) * 8, "vx"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [](T vs2, UT vs1, WT vd) -> WT {
+ using UWT = typename std::make_unsigned<WT>::type;
+ UWT vs1_w = vs1;
+ WT vs2_w = vs2;
+ WT prod = vs1_w * vs2_w;
+ WT res = absl::bit_cast<UWT>(prod) + absl::bit_cast<UWT>(vd);
+ return res;
+ });
+}
+
+// Test vwmaccus instructions.
+TEST_F(RiscVCheriotVectorOpmInstructionsTest, Vwmaccus) {
+ // vector-vector
+ VwmaccusVVHelper<int8_t>(this);
+ ResetInstruction();
+ VwmaccusVVHelper<int16_t>(this);
+ ResetInstruction();
+ VwmaccusVVHelper<int32_t>(this);
+ ResetInstruction();
+ // vector-scalar
+ VwmaccusVXHelper<int8_t>(this);
+ ResetInstruction();
+ VwmaccusVXHelper<int16_t>(this);
+ ResetInstruction();
+ VwmaccusVXHelper<int32_t>(this);
+}
+
+// Vmaccsu vector-vector test helper function.
+template <typename T>
+inline void VwmaccsuVVHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ using WT = typename WideType<T>::type;
+ using UT = typename std::make_unsigned<T>::type;
+ tester->SetSemanticFunction(&Vwmaccsu);
+ tester->TernaryOpTestHelperVV<WT, UT, T>(
+ absl::StrCat("Vwmaccsu", sizeof(T) * 8, "vv"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [](UT vs2, T vs1, WT vd) -> WT {
+ using UWT = typename std::make_unsigned<WT>::type;
+ WT vs1_w = vs1;
+ UWT vs2_w = vs2;
+ WT prod = vs1_w * vs2_w;
+ WT res = absl::bit_cast<UWT>(prod) + absl::bit_cast<UWT>(vd);
+ return res;
+ });
+}
+
+// Vwmaccsu vector-scalar test helper function.
+template <typename T>
+inline void VwmaccsuVXHelper(RiscVCheriotVectorOpmInstructionsTest *tester) {
+ using WT = typename WideType<T>::type;
+ using UT = typename std::make_unsigned<T>::type;
+ tester->SetSemanticFunction(&Vwmaccsu);
+ tester->TernaryOpTestHelperVX<WT, UT, T>(
+ absl::StrCat("Vwmaccsu", sizeof(T) * 8, "vx"), /*sew*/ sizeof(T) * 8,
+ tester->instruction(), [](UT vs2, T vs1, WT vd) -> WT {
+ using UWT = typename std::make_unsigned<WT>::type;
+ WT vs1_w = vs1;
+ UWT vs2_w = vs2;
+ WT prod = vs1_w * vs2_w;
+ WT res = absl::bit_cast<UWT>(prod) + absl::bit_cast<UWT>(vd);
+ return res;
+ });
+}
+
+// Test vwmaccsu instructions.
+TEST_F(RiscVCheriotVectorOpmInstructionsTest, Vwmaccsu) {
+ // vector-vector
+ VwmaccsuVVHelper<int8_t>(this);
+ ResetInstruction();
+ VwmaccsuVVHelper<int16_t>(this);
+ ResetInstruction();
+ VwmaccsuVVHelper<int32_t>(this);
+ ResetInstruction();
+ // vector-scalar
+ VwmaccsuVXHelper<int8_t>(this);
+ ResetInstruction();
+ VwmaccsuVXHelper<int16_t>(this);
+ ResetInstruction();
+ VwmaccsuVXHelper<int32_t>(this);
+}
+
+} // namespace
diff --git a/cheriot/test/riscv_cheriot_vector_permute_instructions_test.cc b/cheriot/test/riscv_cheriot_vector_permute_instructions_test.cc
new file mode 100644
index 0000000..8fd7ad5
--- /dev/null
+++ b/cheriot/test/riscv_cheriot_vector_permute_instructions_test.cc
@@ -0,0 +1,616 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cheriot/riscv_cheriot_vector_permute_instructions.h"
+
+#include <cstdint>
+#include <vector>
+
+#include "absl/random/random.h"
+#include "cheriot/cheriot_register.h"
+#include "cheriot/test/riscv_cheriot_vector_instructions_test_base.h"
+#include "googlemock/include/gmock/gmock.h"
+#include "mpact/sim/generic/instruction.h"
+#include "riscv//riscv_register.h"
+
+// This file contains tests for the RiscV vector permute instructions.
+
+namespace {
+
+using ::mpact::sim::cheriot::CheriotRegister;
+using ::mpact::sim::generic::Instruction;
+using ::mpact::sim::riscv::RVVectorRegister;
+
+using ::mpact::sim::cheriot::Vcompress;
+using ::mpact::sim::cheriot::Vrgather;
+using ::mpact::sim::cheriot::Vrgatherei16;
+using ::mpact::sim::cheriot::Vslide1down;
+using ::mpact::sim::cheriot::Vslide1up;
+using ::mpact::sim::cheriot::Vslidedown;
+using ::mpact::sim::cheriot::Vslideup;
+
+class RiscVCheriotVectorPermuteInstructionsTest
+ : public RiscVCheriotVectorInstructionsTestBase {};
+
+// Helper function for vector-vector vrgather instructions.
+template <typename T, typename I>
+void VrgatherVVHelper(RiscVCheriotVectorPermuteInstructionsTest *tester,
+ Instruction *inst) {
+ auto *rv_vector = tester->rv_vector();
+ // Configure vector unit for sew and maximum lmul.
+ uint32_t vtype = 0;
+ int max_regs = 8;
+ if (sizeof(I) > sizeof(T)) {
+ // This happens for vrgatherei16 when sew is 8.
+ vtype = (kSewSettingsByByteSize[sizeof(T)] << 3) | kLmulSettingByLogSize[6];
+ max_regs = 4;
+ } else {
+ vtype = (kSewSettingsByByteSize[sizeof(T)] << 3) | kLmulSettingByLogSize[7];
+ }
+ tester->ConfigureVectorUnit(vtype, 2048);
+
+ int vlen = rv_vector->vector_length();
+ int num_values_per_reg = kVectorLengthInBytes / sizeof(T);
+ int max_index = num_values_per_reg * max_regs;
+ int num_indices_per_reg = kVectorLengthInBytes / sizeof(I);
+ // Initialize vs2 and vs1 to random values.
+ for (int reg = kVs2; reg < kVs2 + max_regs; reg++) {
+ auto span = tester->vreg()[reg]->data_buffer()->Get<T>();
+ for (int i = 0; i < num_values_per_reg; i++) {
+ span[i] = tester->RandomValue<T>();
+ }
+ }
+ for (int reg = kVs1; reg < kVs1 + max_regs; reg++) {
+ auto span = tester->vreg()[reg]->data_buffer()->Get<I>();
+ for (int i = 0; i < num_indices_per_reg; i++) {
+ span[i] =
+ absl::Uniform(absl::IntervalClosed, tester->bitgen(), 0, 2 * vlen);
+ }
+ }
+ tester->SetVectorRegisterValues<uint8_t>({{kVmaskName, kA5Mask}});
+
+ inst->Execute();
+ for (int i = 0; i < vlen; i++) {
+ int value_reg_offset = i / num_values_per_reg;
+ int value_elem_index = i % num_values_per_reg;
+ int index_reg_offset = i / num_indices_per_reg;
+ int index_elem_index = i % num_indices_per_reg;
+ int mask_index = i >> 8;
+ int mask_offset = i & 0b111;
+ bool mask_value = (kA5Mask[mask_index] >> mask_offset) & 0b1;
+ // Get the destination value.
+ T dst = tester->vreg()[kVd + value_reg_offset]->data_buffer()->Get<T>(
+ value_elem_index);
+ if (mask_value) {
+ // If it's an active element, get the index value.
+ I indx = tester->vreg()[kVs1 + index_reg_offset]->data_buffer()->Get<I>(
+ index_elem_index);
+ if (indx >= max_index) { // If the index is out of range, compare to 0.
+ EXPECT_EQ(0, dst);
+ } else { // Else, get the src value at that index and compare.
+ int reg = kVs2 + indx / num_values_per_reg;
+ int element = indx % num_values_per_reg;
+ T src = tester->vreg()[reg]->data_buffer()->Get<T>(element);
+ EXPECT_EQ(src, dst);
+ }
+ } else { // Inactive values are unchanged.
+ T src = tester->vreg()[kVd + value_reg_offset]->data_buffer()->Get<T>(
+ value_elem_index);
+ EXPECT_EQ(src, dst) << "index: " << i << " offset: " << value_reg_offset
+ << " elem: " << value_elem_index;
+ }
+ }
+}
+
+// Helper function for vector-scalar vrgather instructions.
+template <typename T>
+void VrgatherVSHelper(RiscVCheriotVectorPermuteInstructionsTest *tester,
+ Instruction *inst) {
+ auto *rv_vector = tester->rv_vector();
+ // Configure vector unit.
+ uint32_t vtype =
+ (kSewSettingsByByteSize[sizeof(T)] << 3) | kLmulSettingByLogSize[7];
+ tester->ConfigureVectorUnit(vtype, 2048);
+ int vlen = rv_vector->vector_length();
+ int num_values_per_reg = kVectorLengthInBytes / sizeof(T);
+ int max_index = num_values_per_reg * 8;
+ // Initialize vs2 to random values.
+ for (int reg = kVs2; reg < kVs2 + 8; reg++) {
+ auto span = tester->vreg()[reg]->data_buffer()->Get<T>();
+ for (int i = 0; i < num_values_per_reg; i++) {
+ span[i] = tester->RandomValue<T>();
+ }
+ }
+ tester->SetVectorRegisterValues<uint8_t>({{kVmaskName, kA5Mask}});
+ // Try 20 different index values randomly.
+ for (int num = 0; num < 20; num++) {
+ // Set a random index value.
+ CheriotRegister::ValueType index_value =
+ absl::Uniform(absl::IntervalClosed, tester->bitgen(), 0, 2 * vlen);
+ tester->SetRegisterValues<CheriotRegister::ValueType>(
+ {{kRs1Name, index_value}});
+ // Execute the instruction.
+ inst->Execute();
+ for (int i = 0; i < vlen; i++) {
+ int value_reg_offset = i / num_values_per_reg;
+ int value_elem_index = i % num_values_per_reg;
+ int mask_index = i >> 8;
+ int mask_offset = i & 0b111;
+ bool mask_value = (kA5Mask[mask_index] >> mask_offset) & 0b1;
+ // Get the destination value.
+ T dst = tester->vreg()[kVd + value_reg_offset]->data_buffer()->Get<T>(
+ value_elem_index);
+ if (mask_value) { // If it's an active value.
+ // If the index is out of range, it's 0.
+ if (index_value >= max_index) {
+ EXPECT_EQ(0, dst) << "max: " << max_index << " indx: " << index_value;
+ } else { // Otherwise, get the src value from vs2 and compare.
+ int reg = index_value / num_values_per_reg;
+ int element = index_value % num_values_per_reg;
+ T src = tester->vreg()[kVs2 + reg]->data_buffer()->Get<T>(element);
+ EXPECT_EQ(src, dst);
+ }
+ } else { // Inactive values are unchanged.
+ T src = tester->vreg()[kVd + value_reg_offset]->data_buffer()->Get<T>(
+ value_elem_index);
+ EXPECT_EQ(src, dst);
+ }
+ }
+ }
+}
+
+// Test vrgather instruction for 1, 2, 4, and 8 byte SEWs - vector index.
+TEST_F(RiscVCheriotVectorPermuteInstructionsTest, VrgatherVV8) {
+ SetSemanticFunction(&Vrgather);
+ AppendVectorRegisterOperands({kVs2, kVs1, kVmask}, {kVd});
+ VrgatherVVHelper<uint8_t, uint8_t>(this, instruction_);
+}
+
+TEST_F(RiscVCheriotVectorPermuteInstructionsTest, VrgatherVV16) {
+ SetSemanticFunction(&Vrgather);
+ AppendVectorRegisterOperands({kVs2, kVs1, kVmask}, {kVd});
+ VrgatherVVHelper<uint16_t, uint16_t>(this, instruction_);
+}
+
+TEST_F(RiscVCheriotVectorPermuteInstructionsTest, VrgatherVV32) {
+ SetSemanticFunction(&Vrgather);
+ AppendVectorRegisterOperands({kVs2, kVs1, kVmask}, {kVd});
+ VrgatherVVHelper<uint32_t, uint32_t>(this, instruction_);
+}
+
+TEST_F(RiscVCheriotVectorPermuteInstructionsTest, VrgatherVV64) {
+ SetSemanticFunction(&Vrgather);
+ AppendVectorRegisterOperands({kVs2, kVs1, kVmask}, {kVd});
+ VrgatherVVHelper<uint64_t, uint64_t>(this, instruction_);
+}
+
+// Test vrgather instruction for 1, 2, 4, and 8 byte SEWs - scalar index.
+TEST_F(RiscVCheriotVectorPermuteInstructionsTest, VrgatherVS8) {
+ SetSemanticFunction(&Vrgather);
+ AppendVectorRegisterOperands({kVs2}, {});
+ AppendRegisterOperands({kRs1Name}, {});
+ AppendVectorRegisterOperands({kVmask}, {kVd});
+ VrgatherVSHelper<uint8_t>(this, instruction_);
+}
+
+TEST_F(RiscVCheriotVectorPermuteInstructionsTest, VrgatherVS16) {
+ SetSemanticFunction(&Vrgather);
+ AppendVectorRegisterOperands({kVs2}, {});
+ AppendRegisterOperands({kRs1Name}, {});
+ AppendVectorRegisterOperands({kVmask}, {kVd});
+ VrgatherVSHelper<uint16_t>(this, instruction_);
+}
+
+TEST_F(RiscVCheriotVectorPermuteInstructionsTest, VrgatherVS32) {
+ SetSemanticFunction(&Vrgather);
+ AppendVectorRegisterOperands({kVs2}, {});
+ AppendRegisterOperands({kRs1Name}, {});
+ AppendVectorRegisterOperands({kVmask}, {kVd});
+ VrgatherVSHelper<uint32_t>(this, instruction_);
+}
+
+TEST_F(RiscVCheriotVectorPermuteInstructionsTest, VrgatherVS64) {
+ SetSemanticFunction(&Vrgather);
+ AppendVectorRegisterOperands({kVs2}, {});
+ AppendRegisterOperands({kRs1Name}, {});
+ AppendVectorRegisterOperands({kVmask}, {kVd});
+ VrgatherVSHelper<uint64_t>(this, instruction_);
+}
+
+// Test vrgatherei16 instruction for SEW values of 1, 2, 4, and 8 bytes.
+TEST_F(RiscVCheriotVectorPermuteInstructionsTest, Vrgatherei16VV8) {
+ SetSemanticFunction(&Vrgatherei16);
+ AppendVectorRegisterOperands({kVs2, kVs1, kVmask}, {kVd});
+ VrgatherVVHelper<uint8_t, uint16_t>(this, instruction_);
+}
+
+TEST_F(RiscVCheriotVectorPermuteInstructionsTest, Vrgatherei16VV16) {
+ SetSemanticFunction(&Vrgatherei16);
+ AppendVectorRegisterOperands({kVs2, kVs1, kVmask}, {kVd});
+ VrgatherVVHelper<uint16_t, uint16_t>(this, instruction_);
+}
+
+TEST_F(RiscVCheriotVectorPermuteInstructionsTest, Vrgatherei16VV32) {
+ SetSemanticFunction(&Vrgatherei16);
+ AppendVectorRegisterOperands({kVs2, kVs1, kVmask}, {kVd});
+ VrgatherVVHelper<uint32_t, uint16_t>(this, instruction_);
+}
+
+TEST_F(RiscVCheriotVectorPermuteInstructionsTest, Vrgatherei16VV64) {
+ SetSemanticFunction(&Vrgatherei16);
+ AppendVectorRegisterOperands({kVs2, kVs1, kVmask}, {kVd});
+ VrgatherVVHelper<uint64_t, uint16_t>(this, instruction_);
+}
+
+// Helper function for slideup/down instructions.
+template <typename T>
+void SlideHelper(RiscVCheriotVectorPermuteInstructionsTest *tester,
+ Instruction *inst, bool is_slide_up) {
+ auto *rv_vector = tester->rv_vector();
+ uint32_t vtype =
+ (kSewSettingsByByteSize[sizeof(T)] << 3) | kLmulSettingByLogSize[7];
+ tester->ConfigureVectorUnit(vtype, 2048);
+ int vlen = rv_vector->vector_length();
+ int max_vlen = rv_vector->max_vector_length();
+ int num_values_per_reg = kVectorLengthInBytes / sizeof(T);
+ // Initialize vs2 to random values.
+ for (int reg = 0; reg < 8; reg++) {
+ auto src_span = tester->vreg()[kVs2 + reg]->data_buffer()->Get<T>();
+ for (int i = 0; i < num_values_per_reg; i++) {
+ src_span[i] = tester->RandomValue<T>();
+ }
+ }
+ tester->SetVectorRegisterValues<uint8_t>({{kVmaskName, kA5Mask}});
+ // Try 20 different shift values randomly.
+ for (int num = 0; num < 20; num++) {
+ CheriotRegister::ValueType shift_value =
+ absl::Uniform(absl::IntervalClosed, tester->bitgen(), 0, 2 * vlen);
+ tester->SetRegisterValues<CheriotRegister::ValueType>(
+ {{kRs1Name, shift_value}});
+ // Initialize the destination register set.
+ int value_indx = 0;
+ for (int reg = 0; reg < 8; reg++) {
+ auto dst_span = tester->vreg()[kVd + reg]->data_buffer()->Get<T>();
+ for (int i = 0; i < num_values_per_reg; i++) {
+ dst_span[i] = value_indx++;
+ }
+ }
+ inst->Execute();
+ for (int i = 0; i < vlen; i++) {
+ int value_reg_offset = i / num_values_per_reg;
+ int value_elem_index = i % num_values_per_reg;
+ int mask_index = i >> 8;
+ int mask_offset = i & 0b111;
+ bool mask_value = (kA5Mask[mask_index] >> mask_offset) & 0b1;
+ T dst = tester->vreg()[kVd + value_reg_offset]->data_buffer()->Get<T>(
+ value_elem_index);
+ if (is_slide_up) { // For slide up instruction.
+ if ((i < shift_value) || (!mask_value)) {
+ // If it's an inactive element, or the index is less than the shift
+ // amount, the element is unchanged.
+ T value = static_cast<T>(i);
+ EXPECT_EQ(value, dst) << "indx: " << i;
+ } else {
+ // Active elements are shifted up by 'shift_value'.
+ int src_reg_offset = (i - shift_value) / num_values_per_reg;
+ int src_reg_index = (i - shift_value) % num_values_per_reg;
+ T src = tester->vreg()[kVs2 + src_reg_offset]->data_buffer()->Get<T>(
+ src_reg_index);
+ EXPECT_EQ(src, dst) << "indx: " << i;
+ }
+ } else { // This is slide down.
+ if (mask_value) {
+ if (i + shift_value >= max_vlen) {
+ // Active elements originating beyond max_vlen are 0.
+ EXPECT_EQ(0, dst) << "indx: " << i;
+ } else {
+ // Active elements are shifted down by 'shift_value'.
+ int src_reg_offset = (i + shift_value) / num_values_per_reg;
+ int src_reg_index = (i + shift_value) % num_values_per_reg;
+ T src =
+ tester->vreg()[kVs2 + src_reg_offset]->data_buffer()->Get<T>(
+ src_reg_index);
+ EXPECT_EQ(src, dst) << "indx: " << i;
+ }
+ } else {
+ // All inactive elements are unchanged.
+ T src = tester->vreg()[kVd + value_reg_offset]->data_buffer()->Get<T>(
+ value_elem_index);
+ EXPECT_EQ(src, dst) << "indx: " << i;
+ }
+ }
+ }
+ }
+}
+
+// Test vslideup instruction for SEW values of 1, 2, 4, and 8 bytes.
+TEST_F(RiscVCheriotVectorPermuteInstructionsTest, Vslideup8) {
+ SetSemanticFunction(&Vslideup);
+ AppendVectorRegisterOperands({kVs2}, {});
+ AppendRegisterOperands({kRs1Name}, {});
+ AppendVectorRegisterOperands({kVmask}, {kVd});
+ SlideHelper<uint8_t>(this, instruction_, /*is_slide_up*/ true);
+}
+
+TEST_F(RiscVCheriotVectorPermuteInstructionsTest, Vslideup16) {
+ SetSemanticFunction(&Vslideup);
+ AppendVectorRegisterOperands({kVs2}, {});
+ AppendRegisterOperands({kRs1Name}, {});
+ AppendVectorRegisterOperands({kVmask}, {kVd});
+ SlideHelper<uint16_t>(this, instruction_, /*is_slide_up*/ true);
+}
+
+TEST_F(RiscVCheriotVectorPermuteInstructionsTest, Vslideup32) {
+ SetSemanticFunction(&Vslideup);
+ AppendVectorRegisterOperands({kVs2}, {});
+ AppendRegisterOperands({kRs1Name}, {});
+ AppendVectorRegisterOperands({kVmask}, {kVd});
+ SlideHelper<uint32_t>(this, instruction_, /*is_slide_up*/ true);
+}
+
+TEST_F(RiscVCheriotVectorPermuteInstructionsTest, Vslideup64) {
+ SetSemanticFunction(&Vslideup);
+ AppendVectorRegisterOperands({kVs2}, {});
+ AppendRegisterOperands({kRs1Name}, {});
+ AppendVectorRegisterOperands({kVmask}, {kVd});
+ SlideHelper<uint64_t>(this, instruction_, /*is_slide_up*/ true);
+}
+
+// Test vslidedown instruction for SEW values of 1, 2, 4, and 8 bytes.
+TEST_F(RiscVCheriotVectorPermuteInstructionsTest, Vslidedown8) {
+ SetSemanticFunction(&Vslidedown);
+ AppendVectorRegisterOperands({kVs2}, {});
+ AppendRegisterOperands({kRs1Name}, {});
+ AppendVectorRegisterOperands({kVmask}, {kVd});
+ SlideHelper<uint8_t>(this, instruction_, /*is_slide_up*/ false);
+}
+
+TEST_F(RiscVCheriotVectorPermuteInstructionsTest, Vslidedown16) {
+ SetSemanticFunction(&Vslidedown);
+ AppendVectorRegisterOperands({kVs2}, {});
+ AppendRegisterOperands({kRs1Name}, {});
+ AppendVectorRegisterOperands({kVmask}, {kVd});
+ SlideHelper<uint16_t>(this, instruction_, /*is_slide_up*/ false);
+}
+
+TEST_F(RiscVCheriotVectorPermuteInstructionsTest, Vslidedown32) {
+ SetSemanticFunction(&Vslidedown);
+ AppendVectorRegisterOperands({kVs2}, {});
+ AppendRegisterOperands({kRs1Name}, {});
+ AppendVectorRegisterOperands({kVmask}, {kVd});
+ SlideHelper<uint32_t>(this, instruction_, /*is_slide_up*/ false);
+}
+
+TEST_F(RiscVCheriotVectorPermuteInstructionsTest, Vslidedown64) {
+ SetSemanticFunction(&Vslidedown);
+ AppendVectorRegisterOperands({kVs2}, {});
+ AppendRegisterOperands({kRs1Name}, {});
+ AppendVectorRegisterOperands({kVmask}, {kVd});
+ SlideHelper<uint64_t>(this, instruction_, /*is_slide_up*/ false);
+}
+
+template <typename T>
+void Slide1Helper(RiscVCheriotVectorPermuteInstructionsTest *tester,
+ Instruction *inst, bool is_slide_up) {
+ auto *rv_vector = tester->rv_vector();
+ uint32_t vtype =
+ (kSewSettingsByByteSize[sizeof(T)] << 3) | kLmulSettingByLogSize[7];
+ tester->ConfigureVectorUnit(vtype, 2048);
+ int vlen = rv_vector->vector_length();
+ int max_vlen = rv_vector->max_vector_length();
+ int num_values_per_reg = kVectorLengthInBytes / sizeof(T);
+ // Initialize vs2 to random values.
+ for (int reg = kVs2; reg < kVs2 + 8; reg++) {
+ auto span = tester->vreg()[reg]->data_buffer()->Get<T>();
+ for (int i = 0; i < num_values_per_reg; i++) {
+ span[i] = tester->RandomValue<T>();
+ }
+ }
+ tester->SetVectorRegisterValues<uint8_t>({{kVmaskName, kA5Mask}});
+ // Try 20 different shift values randomly.
+ for (int num = 0; num < 20; num++) {
+ CheriotRegister::ValueType fill_in_value =
+ absl::Uniform(absl::IntervalClosed, tester->bitgen(), 0, 2 * vlen);
+ tester->SetRegisterValues<CheriotRegister::ValueType>(
+ {{kRs1Name, fill_in_value}});
+ fill_in_value = static_cast<T>(fill_in_value);
+ inst->Execute();
+ for (int i = 0; i < vlen; i++) {
+ int value_reg_offset = i / num_values_per_reg;
+ int value_elem_index = i % num_values_per_reg;
+ int mask_index = i >> 8;
+ int mask_offset = i & 0b111;
+ bool mask_value = (kA5Mask[mask_index] >> mask_offset) & 0b1;
+ T dst = tester->vreg()[kVd + value_reg_offset]->data_buffer()->Get<T>(
+ value_elem_index);
+ if (is_slide_up) {
+ if (!mask_value) { // Inactive elements are unchanged.
+ T src = tester->vreg()[kVd + value_reg_offset]->data_buffer()->Get<T>(
+ value_elem_index);
+ EXPECT_EQ(src, dst) << "i: " << i;
+ } else {
+ if (i == 0) { // The first value should match the fill-in.
+ EXPECT_EQ(fill_in_value, dst) << "i: " << i;
+ } else { // Other values are shifted by 1.
+ int src_reg_offset = (i - 1) / num_values_per_reg;
+ int src_reg_index = (i - 1) % num_values_per_reg;
+ T src =
+ tester->vreg()[kVs2 + src_reg_offset]->data_buffer()->Get<T>(
+ src_reg_index);
+ EXPECT_EQ(src, dst) << "i: " << i;
+ }
+ }
+ } else { // This is slide down.
+ if (mask_value) {
+ if (i + 1 >= max_vlen) { // The last value should match the fill-in.
+ EXPECT_EQ(fill_in_value, dst);
+ } else { // Other elements are shifted by 1.
+ int src_reg_offset = (i + 1) / num_values_per_reg;
+ int src_reg_index = (i + 1) % num_values_per_reg;
+ T src =
+ tester->vreg()[kVs2 + src_reg_offset]->data_buffer()->Get<T>(
+ src_reg_index);
+ EXPECT_EQ(src, dst) << "i: " << i;
+ }
+ } else { // Inactive elements are unchanged.
+ T src = tester->vreg()[kVd + value_reg_offset]->data_buffer()->Get<T>(
+ value_elem_index);
+ EXPECT_EQ(src, dst) << "i: " << i;
+ }
+ }
+ }
+ }
+}
+
+// Test vslide1up instruction for SEW values of 1, 2, 4, and 8 bytes.
+TEST_F(RiscVCheriotVectorPermuteInstructionsTest, Vslide1up8) {
+ SetSemanticFunction(&Vslide1up);
+ AppendVectorRegisterOperands({kVs2}, {});
+ AppendRegisterOperands({kRs1Name}, {});
+ AppendVectorRegisterOperands({kVmask}, {kVd});
+ Slide1Helper<uint8_t>(this, instruction_, /*is_slide_up*/ true);
+}
+
+TEST_F(RiscVCheriotVectorPermuteInstructionsTest, Vslide1up16) {
+ SetSemanticFunction(&Vslide1up);
+ AppendVectorRegisterOperands({kVs2}, {});
+ AppendRegisterOperands({kRs1Name}, {});
+ AppendVectorRegisterOperands({kVmask}, {kVd});
+ Slide1Helper<uint16_t>(this, instruction_, /*is_slide_up*/ true);
+}
+
+TEST_F(RiscVCheriotVectorPermuteInstructionsTest, Vslide1up32) {
+ SetSemanticFunction(&Vslide1up);
+ AppendVectorRegisterOperands({kVs2}, {});
+ AppendRegisterOperands({kRs1Name}, {});
+ AppendVectorRegisterOperands({kVmask}, {kVd});
+ Slide1Helper<uint32_t>(this, instruction_, /*is_slide_up*/ true);
+}
+
+TEST_F(RiscVCheriotVectorPermuteInstructionsTest, Vslide1up64) {
+ SetSemanticFunction(&Vslide1up);
+ AppendVectorRegisterOperands({kVs2}, {});
+ AppendRegisterOperands({kRs1Name}, {});
+ AppendVectorRegisterOperands({kVmask}, {kVd});
+ Slide1Helper<uint64_t>(this, instruction_, /*is_slide_up*/ true);
+}
+// Test vslide1down instruction for SEW values of 1, 2, 4, and 8 bytes.
+TEST_F(RiscVCheriotVectorPermuteInstructionsTest, Vslide1down8) {
+ SetSemanticFunction(&Vslide1down);
+ AppendVectorRegisterOperands({kVs2}, {});
+ AppendRegisterOperands({kRs1Name}, {});
+ AppendVectorRegisterOperands({kVmask}, {kVd});
+ Slide1Helper<uint8_t>(this, instruction_, /*is_slide_up*/ false);
+}
+
+TEST_F(RiscVCheriotVectorPermuteInstructionsTest, Vslide1down16) {
+ SetSemanticFunction(&Vslide1down);
+ AppendVectorRegisterOperands({kVs2}, {});
+ AppendRegisterOperands({kRs1Name}, {});
+ AppendVectorRegisterOperands({kVmask}, {kVd});
+ Slide1Helper<uint16_t>(this, instruction_, /*is_slide_up*/ false);
+}
+
+TEST_F(RiscVCheriotVectorPermuteInstructionsTest, Vslide1down32) {
+ SetSemanticFunction(&Vslide1down);
+ AppendVectorRegisterOperands({kVs2}, {});
+ AppendRegisterOperands({kRs1Name}, {});
+ AppendVectorRegisterOperands({kVmask}, {kVd});
+ Slide1Helper<uint32_t>(this, instruction_, /*is_slide_up*/ false);
+}
+
+TEST_F(RiscVCheriotVectorPermuteInstructionsTest, Vslide1down64) {
+ SetSemanticFunction(&Vslide1down);
+ AppendVectorRegisterOperands({kVs2}, {});
+ AppendRegisterOperands({kRs1Name}, {});
+ AppendVectorRegisterOperands({kVmask}, {kVd});
+ Slide1Helper<uint64_t>(this, instruction_, /*is_slide_up*/ false);
+}
+
+template <typename T>
+void CompressHelper(RiscVCheriotVectorPermuteInstructionsTest *tester,
+ Instruction *inst) {
+ auto *rv_vector = tester->rv_vector();
+ uint32_t vtype =
+ (kSewSettingsByByteSize[sizeof(T)] << 3) | kLmulSettingByLogSize[7];
+ tester->ConfigureVectorUnit(vtype, 2048);
+ int vlen = rv_vector->vector_length();
+ int num_values_per_reg = kVectorLengthInBytes / sizeof(T);
+ auto vd_span = tester->vreg()[kVd]->data_buffer()->Get<T>();
+ std::vector<T> origin_vd_values(vd_span.begin(), vd_span.end());
+ // Initialize vs2 to random values.
+ for (int reg = kVs2; reg < kVs2 + 8; reg++) {
+ auto span = tester->vreg()[reg]->data_buffer()->Get<T>();
+ for (int i = 0; i < num_values_per_reg; i++) {
+ span[i] = tester->RandomValue<T>();
+ }
+ }
+ tester->SetVectorRegisterValues<uint8_t>({{kVmaskName, kA5Mask}});
+ inst->Execute();
+ // First check all the elements that were compressed (mask bit true).
+ int offset = 0;
+ for (int i = 0; i < vlen; i++) {
+ int value_reg_offset = i / num_values_per_reg;
+ int value_elem_index = i % num_values_per_reg;
+ int mask_index = i >> 8;
+ int mask_offset = i & 0b111;
+ bool mask_value = (kA5Mask[mask_index] >> mask_offset) & 0b1;
+ if (mask_value) {
+ T src = tester->vreg()[kVs2 + value_reg_offset]->data_buffer()->Get<T>(
+ value_elem_index);
+ int dst_reg_index = offset / num_values_per_reg;
+ int dst_element_index = offset % num_values_per_reg;
+ T dst = tester->vreg()[kVd + dst_reg_index]->data_buffer()->Get<T>(
+ dst_element_index);
+ EXPECT_EQ(src, dst) << "index: " << i;
+ offset++;
+ }
+ }
+ // The remaining elements are unchanged.
+ for (int i = offset; i < vlen; i++) {
+ int value_reg_index = i / num_values_per_reg;
+ int value_elem_index = i % num_values_per_reg;
+ T src = origin_vd_values[value_elem_index];
+ T dst = tester->vreg()[kVd + value_reg_index]->data_buffer()->Get<T>(
+ value_elem_index);
+ EXPECT_EQ(src, dst) << "index: " << i;
+ }
+}
+
+// Test compress instruction for SEW of 8, 16, 32, and 64.
+TEST_F(RiscVCheriotVectorPermuteInstructionsTest, Vcompress8) {
+ SetSemanticFunction(&Vcompress);
+ AppendVectorRegisterOperands({kVs2, kVmask}, {kVd});
+ CompressHelper<uint8_t>(this, instruction_);
+}
+
+TEST_F(RiscVCheriotVectorPermuteInstructionsTest, Vcompress16) {
+ SetSemanticFunction(&Vcompress);
+ AppendVectorRegisterOperands({kVs2, kVmask}, {kVd});
+ CompressHelper<uint16_t>(this, instruction_);
+}
+
+TEST_F(RiscVCheriotVectorPermuteInstructionsTest, Vcompress32) {
+ SetSemanticFunction(&Vcompress);
+ AppendVectorRegisterOperands({kVs2, kVmask}, {kVd});
+ CompressHelper<uint32_t>(this, instruction_);
+}
+
+TEST_F(RiscVCheriotVectorPermuteInstructionsTest, Vcompress64) {
+ SetSemanticFunction(&Vcompress);
+ AppendVectorRegisterOperands({kVs2, kVmask}, {kVd});
+ CompressHelper<uint64_t>(this, instruction_);
+}
+
+} // namespace
diff --git a/cheriot/test/riscv_cheriot_vector_reduction_instructions_test.cc b/cheriot/test/riscv_cheriot_vector_reduction_instructions_test.cc
new file mode 100644
index 0000000..b7b6b28
--- /dev/null
+++ b/cheriot/test/riscv_cheriot_vector_reduction_instructions_test.cc
@@ -0,0 +1,412 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cheriot/riscv_cheriot_vector_reduction_instructions.h"
+
+#include <algorithm>
+#include <cstdint>
+#include <functional>
+#include <vector>
+
+#include "absl/random/random.h"
+#include "absl/strings/string_view.h"
+#include "cheriot/test/riscv_cheriot_vector_instructions_test_base.h"
+#include "googlemock/include/gmock/gmock.h"
+#include "mpact/sim/generic/instruction.h"
+#include "mpact/sim/generic/type_helpers.h"
+#include "riscv//riscv_register.h"
+
+namespace {
+using ::absl::Span;
+using ::mpact::sim::generic::Instruction;
+using ::mpact::sim::generic::WideType;
+using ::mpact::sim::riscv::RV32Register;
+using ::mpact::sim::riscv::RVVectorRegister;
+
+using ::mpact::sim::cheriot::Vredand;
+using ::mpact::sim::cheriot::Vredmax;
+using ::mpact::sim::cheriot::Vredmaxu;
+using ::mpact::sim::cheriot::Vredmin;
+using ::mpact::sim::cheriot::Vredminu;
+using ::mpact::sim::cheriot::Vredor;
+using ::mpact::sim::cheriot::Vredsum;
+using ::mpact::sim::cheriot::Vredxor;
+using ::mpact::sim::cheriot::Vwredsum;
+using ::mpact::sim::cheriot::Vwredsumu;
+
+class RiscVCheriotVectorReductionInstructionsTest
+ : public RiscVCheriotVectorInstructionsTestBase {
+ public:
+ template <typename Vd, typename Vs2>
+ void ReductionOpTestHelper(absl::string_view name, int sew, Instruction *inst,
+ std::function<Vd(Vd, Vs2)> operation) {
+ int byte_sew = sew / 8;
+ if (byte_sew != sizeof(Vd) && byte_sew != sizeof(Vs2)) {
+ FAIL() << name << ": selected element width != any operand types"
+ << "sew: " << sew << " Vd: " << sizeof(Vd)
+ << " Vs2: " << sizeof(Vs2);
+ return;
+ }
+ // Number of elements per vector register.
+ constexpr int vs2_size = kVectorLengthInBytes / sizeof(Vs2);
+ // Input values for 8 registers.
+ Vs2 vs2_value[vs2_size * 8];
+ auto vs2_span = Span<Vs2>(vs2_value);
+ Vs2 vs1_value[vs2_size];
+ auto vs1_span = Span<Vs2>(vs1_value);
+ AppendVectorRegisterOperands({kVs2, kVs1, kVmask}, {kVd});
+ // Initialize input values.
+ FillArrayWithRandomValues<Vs2>(vs2_span);
+ vs1_span[0] = RandomValue<Vs2>();
+ auto mask_span = Span<const uint8_t>(kA5Mask);
+ SetVectorRegisterValues<uint8_t>({{kVmaskName, mask_span}});
+ SetVectorRegisterValues<Vs2>({{kVs1Name, Span<const Vs2>(vs1_span)}});
+ // Initialize the accumulator with the value from vs1[0].
+ for (int i = 0; i < 8; i++) {
+ auto vs2_name = absl::StrCat("v", kVs2 + i);
+ SetVectorRegisterValues<Vs2>(
+ {{vs2_name, vs2_span.subspan(vs2_size * i, vs2_size)}});
+ }
+ // Iterate across the different lmul values.
+ for (int lmul_index = 0; lmul_index < 7; lmul_index++) {
+ for (int vlen_count = 0; vlen_count < 4; vlen_count++) {
+ int lmul8 = kLmul8Values[lmul_index];
+ int lmul8_vs2 = lmul8 * sizeof(Vs2) / byte_sew;
+ int lmul8_vd = lmul8 * sizeof(Vd) / byte_sew;
+ int num_values = lmul8 * kVectorLengthInBytes / (8 * byte_sew);
+ // Set vlen, but leave vlen high at least once.
+ int vlen = 1024;
+ if (vlen_count > 0) {
+ vlen =
+ absl::Uniform(absl::IntervalOpenClosed, bitgen_, 0, num_values);
+ }
+ num_values = std::min(num_values, vlen);
+ // Configure vector unit for different lmul settings.
+ uint32_t vtype =
+ (kSewSettingsByByteSize[byte_sew] << 3) | kLmulSettings[lmul_index];
+ ConfigureVectorUnit(vtype, vlen);
+ ClearVectorRegisterGroup(kVd, 8);
+
+ inst->Execute();
+
+ if ((lmul8_vs2 < 1) || (lmul8_vs2 > 64)) {
+ EXPECT_TRUE(rv_vector_->vector_exception());
+ rv_vector_->clear_vector_exception();
+ continue;
+ }
+
+ if ((lmul8_vd < 1) || (lmul8_vd > 64)) {
+ EXPECT_TRUE(rv_vector_->vector_exception());
+ rv_vector_->clear_vector_exception();
+ continue;
+ }
+
+ EXPECT_FALSE(rv_vector_->vector_exception());
+ Vd accumulator = static_cast<Vd>(vs1_span[0]);
+ for (int i = 0; i < num_values; i++) {
+ int mask_index = i >> 3;
+ int mask_offset = i & 0b111;
+ bool mask_value = (mask_span[mask_index] >> mask_offset) & 0b1;
+ if (mask_value) {
+ accumulator = operation(accumulator, vs2_span[i]);
+ }
+ }
+ EXPECT_EQ(accumulator, vreg_[kVd]->data_buffer()->Get<Vd>(0));
+ }
+ }
+ }
+};
+
+// Test functions for vector reduction instruction semantic functions. The
+// vector reduction instructions take two vector source operands and a mask
+// operand, and write to the first element of a destination vector operand.
+
+// Vector sum reduction.
+TEST_F(RiscVCheriotVectorReductionInstructionsTest, Vredsum8) {
+ using T = uint8_t;
+ SetSemanticFunction(&Vredsum);
+ ReductionOpTestHelper<T, T>("Vredsum", /*sew*/ sizeof(T) * 8, instruction_,
+ [](T val0, T val1) -> T { return val0 + val1; });
+}
+TEST_F(RiscVCheriotVectorReductionInstructionsTest, Vredsum16) {
+ using T = uint16_t;
+ SetSemanticFunction(&Vredsum);
+ ReductionOpTestHelper<T, T>("Vredsum", /*sew*/ sizeof(T) * 8, instruction_,
+ [](T val0, T val1) -> T { return val0 + val1; });
+}
+TEST_F(RiscVCheriotVectorReductionInstructionsTest, Vredsum32) {
+ using T = uint32_t;
+ SetSemanticFunction(&Vredsum);
+ ReductionOpTestHelper<T, T>("Vredsum", /*sew*/ sizeof(T) * 8, instruction_,
+ [](T val0, T val1) -> T { return val0 + val1; });
+}
+TEST_F(RiscVCheriotVectorReductionInstructionsTest, Vredsum64) {
+ using T = uint64_t;
+ SetSemanticFunction(&Vredsum);
+ ReductionOpTestHelper<T, T>("Vredsum", /*sew*/ sizeof(T) * 8, instruction_,
+ [](T val0, T val1) -> T { return val0 + val1; });
+}
+
+// Vector and reduction.
+TEST_F(RiscVCheriotVectorReductionInstructionsTest, Vredand8) {
+ using T = uint8_t;
+ SetSemanticFunction(&Vredand);
+ ReductionOpTestHelper<T, T>("Vredand", /*sew*/ sizeof(T) * 8, instruction_,
+ [](T val0, T val1) -> T { return val0 & val1; });
+}
+TEST_F(RiscVCheriotVectorReductionInstructionsTest, Vredand16) {
+ using T = uint16_t;
+ SetSemanticFunction(&Vredand);
+ ReductionOpTestHelper<T, T>("Vredand", /*sew*/ sizeof(T) * 8, instruction_,
+ [](T val0, T val1) -> T { return val0 & val1; });
+}
+TEST_F(RiscVCheriotVectorReductionInstructionsTest, Vredand32) {
+ using T = uint32_t;
+ SetSemanticFunction(&Vredand);
+ ReductionOpTestHelper<T, T>("Vredand", /*sew*/ sizeof(T) * 8, instruction_,
+ [](T val0, T val1) -> T { return val0 & val1; });
+}
+TEST_F(RiscVCheriotVectorReductionInstructionsTest, Vredand64) {
+ using T = uint64_t;
+ SetSemanticFunction(&Vredand);
+ ReductionOpTestHelper<T, T>("Vredand", /*sew*/ sizeof(T) * 8, instruction_,
+ [](T val0, T val1) -> T { return val0 & val1; });
+}
+
+// Vector or reduction.
+TEST_F(RiscVCheriotVectorReductionInstructionsTest, Vredor8) {
+ using T = uint8_t;
+ SetSemanticFunction(&Vredor);
+ ReductionOpTestHelper<T, T>("Vredor", /*sew*/ sizeof(T) * 8, instruction_,
+ [](T val0, T val1) -> T { return val0 | val1; });
+}
+TEST_F(RiscVCheriotVectorReductionInstructionsTest, Vredor16) {
+ using T = uint16_t;
+ SetSemanticFunction(&Vredor);
+ ReductionOpTestHelper<T, T>("Vredor", /*sew*/ sizeof(T) * 8, instruction_,
+ [](T val0, T val1) -> T { return val0 | val1; });
+}
+TEST_F(RiscVCheriotVectorReductionInstructionsTest, Vredor32) {
+ using T = uint32_t;
+ SetSemanticFunction(&Vredor);
+ ReductionOpTestHelper<T, T>("Vredor", /*sew*/ sizeof(T) * 8, instruction_,
+ [](T val0, T val1) -> T { return val0 | val1; });
+}
+TEST_F(RiscVCheriotVectorReductionInstructionsTest, Vredor64) {
+ using T = uint64_t;
+ SetSemanticFunction(&Vredor);
+ ReductionOpTestHelper<T, T>("Vredor", /*sew*/ sizeof(T) * 8, instruction_,
+ [](T val0, T val1) -> T { return val0 | val1; });
+}
+
+// Vector xor reduction.
+TEST_F(RiscVCheriotVectorReductionInstructionsTest, Vredxor8) {
+ using T = uint8_t;
+ SetSemanticFunction(&Vredxor);
+ ReductionOpTestHelper<T, T>("Vredxor", /*sew*/ sizeof(T) * 8, instruction_,
+ [](T val0, T val1) -> T { return val0 ^ val1; });
+}
+TEST_F(RiscVCheriotVectorReductionInstructionsTest, Vredxor16) {
+ using T = uint16_t;
+ SetSemanticFunction(&Vredxor);
+ ReductionOpTestHelper<T, T>("Vredxor", /*sew*/ sizeof(T) * 8, instruction_,
+ [](T val0, T val1) -> T { return val0 ^ val1; });
+}
+TEST_F(RiscVCheriotVectorReductionInstructionsTest, Vredxor32) {
+ using T = uint32_t;
+ SetSemanticFunction(&Vredxor);
+ ReductionOpTestHelper<T, T>("Vredxor", /*sew*/ sizeof(T) * 8, instruction_,
+ [](T val0, T val1) -> T { return val0 ^ val1; });
+}
+TEST_F(RiscVCheriotVectorReductionInstructionsTest, Vredxor64) {
+ using T = uint64_t;
+ SetSemanticFunction(&Vredxor);
+ ReductionOpTestHelper<T, T>("Vredxor", /*sew*/ sizeof(T) * 8, instruction_,
+ [](T val0, T val1) -> T { return val0 ^ val1; });
+}
+
+// Vector unsigned min reduction.
+TEST_F(RiscVCheriotVectorReductionInstructionsTest, Vredminu8) {
+ using T = uint8_t;
+ SetSemanticFunction(&Vredminu);
+ ReductionOpTestHelper<T, T>(
+ "Vredminu", /*sew*/ sizeof(T) * 8, instruction_,
+ [](T val0, T val1) -> T { return val0 < val1 ? val0 : val1; });
+}
+TEST_F(RiscVCheriotVectorReductionInstructionsTest, Vredminu16) {
+ using T = uint16_t;
+ SetSemanticFunction(&Vredminu);
+ ReductionOpTestHelper<T, T>(
+ "Vredminu", /*sew*/ sizeof(T) * 8, instruction_,
+ [](T val0, T val1) -> T { return val0 < val1 ? val0 : val1; });
+}
+TEST_F(RiscVCheriotVectorReductionInstructionsTest, Vredminu32) {
+ using T = uint32_t;
+ SetSemanticFunction(&Vredminu);
+ ReductionOpTestHelper<T, T>(
+ "Vredminu", /*sew*/ sizeof(T) * 8, instruction_,
+ [](T val0, T val1) -> T { return val0 < val1 ? val0 : val1; });
+}
+TEST_F(RiscVCheriotVectorReductionInstructionsTest, Vredminu64) {
+ using T = uint64_t;
+ SetSemanticFunction(&Vredminu);
+ ReductionOpTestHelper<T, T>(
+ "Vredminu", /*sew*/ sizeof(T) * 8, instruction_,
+ [](T val0, T val1) -> T { return val0 < val1 ? val0 : val1; });
+}
+
+// Vector signed min reduction.
+TEST_F(RiscVCheriotVectorReductionInstructionsTest, Vredmin8) {
+ using T = int8_t;
+ SetSemanticFunction(&Vredmin);
+ ReductionOpTestHelper<T, T>(
+ "Vredmin", /*sew*/ sizeof(T) * 8, instruction_,
+ [](T val0, T val1) -> T { return val0 < val1 ? val0 : val1; });
+}
+TEST_F(RiscVCheriotVectorReductionInstructionsTest, Vredmin16) {
+ using T = int16_t;
+ SetSemanticFunction(&Vredmin);
+ ReductionOpTestHelper<T, T>(
+ "Vredmin", /*sew*/ sizeof(T) * 8, instruction_,
+ [](T val0, T val1) -> T { return val0 < val1 ? val0 : val1; });
+}
+TEST_F(RiscVCheriotVectorReductionInstructionsTest, Vredmin32) {
+ using T = int32_t;
+ SetSemanticFunction(&Vredmin);
+ ReductionOpTestHelper<T, T>(
+ "Vredmin", /*sew*/ sizeof(T) * 8, instruction_,
+ [](T val0, T val1) -> T { return val0 < val1 ? val0 : val1; });
+}
+TEST_F(RiscVCheriotVectorReductionInstructionsTest, Vredmin64) {
+ using T = int64_t;
+ SetSemanticFunction(&Vredmin);
+ ReductionOpTestHelper<T, T>(
+ "Vredmin", /*sew*/ sizeof(T) * 8, instruction_,
+ [](T val0, T val1) -> T { return val0 < val1 ? val0 : val1; });
+}
+
+// Vector unsigned max reduction.
+TEST_F(RiscVCheriotVectorReductionInstructionsTest, Vredmaxu8) {
+ using T = uint8_t;
+ SetSemanticFunction(&Vredmaxu);
+ ReductionOpTestHelper<T, T>(
+ "Vredmaxu", /*sew*/ sizeof(T) * 8, instruction_,
+ [](T val0, T val1) -> T { return val0 > val1 ? val0 : val1; });
+}
+TEST_F(RiscVCheriotVectorReductionInstructionsTest, Vredmaxu16) {
+ using T = uint16_t;
+ SetSemanticFunction(&Vredmaxu);
+ ReductionOpTestHelper<T, T>(
+ "Vredmaxu", /*sew*/ sizeof(T) * 8, instruction_,
+ [](T val0, T val1) -> T { return val0 > val1 ? val0 : val1; });
+}
+TEST_F(RiscVCheriotVectorReductionInstructionsTest, Vredmaxu32) {
+ using T = uint32_t;
+ SetSemanticFunction(&Vredmaxu);
+ ReductionOpTestHelper<T, T>(
+ "Vredmaxu", /*sew*/ sizeof(T) * 8, instruction_,
+ [](T val0, T val1) -> T { return val0 > val1 ? val0 : val1; });
+}
+TEST_F(RiscVCheriotVectorReductionInstructionsTest, Vredmaxu64) {
+ using T = uint64_t;
+ SetSemanticFunction(&Vredmaxu);
+ ReductionOpTestHelper<T, T>(
+ "Vredmaxu", /*sew*/ sizeof(T) * 8, instruction_,
+ [](T val0, T val1) -> T { return val0 > val1 ? val0 : val1; });
+}
+
+// Vector signed max reduction.
+TEST_F(RiscVCheriotVectorReductionInstructionsTest, Vredmax8) {
+ using T = int8_t;
+ SetSemanticFunction(&Vredmax);
+ ReductionOpTestHelper<T, T>(
+ "Vredmax", /*sew*/ sizeof(T) * 8, instruction_,
+ [](T val0, T val1) -> T { return val0 > val1 ? val0 : val1; });
+}
+TEST_F(RiscVCheriotVectorReductionInstructionsTest, Vredmax16) {
+ using T = int16_t;
+ SetSemanticFunction(&Vredmax);
+ ReductionOpTestHelper<T, T>(
+ "Vredmax", /*sew*/ sizeof(T) * 8, instruction_,
+ [](T val0, T val1) -> T { return val0 > val1 ? val0 : val1; });
+}
+TEST_F(RiscVCheriotVectorReductionInstructionsTest, Vredmax32) {
+ using T = int32_t;
+ SetSemanticFunction(&Vredmax);
+ ReductionOpTestHelper<T, T>(
+ "Vredmax", /*sew*/ sizeof(T) * 8, instruction_,
+ [](T val0, T val1) -> T { return val0 > val1 ? val0 : val1; });
+}
+TEST_F(RiscVCheriotVectorReductionInstructionsTest, Vredmax64) {
+ using T = int64_t;
+ SetSemanticFunction(&Vredmax);
+ ReductionOpTestHelper<T, T>(
+ "Vredmax", /*sew*/ sizeof(T) * 8, instruction_,
+ [](T val0, T val1) -> T { return val0 > val1 ? val0 : val1; });
+}
+
+// Vector widening unsigned sum reduction.
+TEST_F(RiscVCheriotVectorReductionInstructionsTest, Vwredsumu8) {
+ using T = uint8_t;
+ using WT = WideType<T>::type;
+ SetSemanticFunction(&Vwredsumu);
+ ReductionOpTestHelper<WT, T>(
+ "Vredsumu", /*sew*/ sizeof(T) * 8, instruction_,
+ [](WT val0, T val1) -> WT { return val0 + static_cast<WT>(val1); });
+}
+TEST_F(RiscVCheriotVectorReductionInstructionsTest, Vwredsumu16) {
+ using T = uint16_t;
+ using WT = WideType<T>::type;
+ SetSemanticFunction(&Vwredsumu);
+ ReductionOpTestHelper<WT, T>(
+ "Vredsumu", /*sew*/ sizeof(T) * 8, instruction_,
+ [](WT val0, T val1) -> WT { return val0 + static_cast<WT>(val1); });
+}
+TEST_F(RiscVCheriotVectorReductionInstructionsTest, Vwredsumu32) {
+ using T = uint32_t;
+ using WT = WideType<T>::type;
+ SetSemanticFunction(&Vwredsumu);
+ ReductionOpTestHelper<WT, T>(
+ "Vredsumu", /*sew*/ sizeof(T) * 8, instruction_,
+ [](WT val0, T val1) -> WT { return val0 + static_cast<WT>(val1); });
+}
+
+// Vector widening signed sum reduction.
+
+TEST_F(RiscVCheriotVectorReductionInstructionsTest, Vwredsum8) {
+ using T = int8_t;
+ using WT = WideType<T>::type;
+ SetSemanticFunction(&Vwredsum);
+ ReductionOpTestHelper<WT, T>(
+ "Vredsum", /*sew*/ sizeof(T) * 8, instruction_,
+ [](WT val0, T val1) -> WT { return val0 + static_cast<WT>(val1); });
+}
+TEST_F(RiscVCheriotVectorReductionInstructionsTest, Vwredsum16) {
+ using T = int16_t;
+ using WT = WideType<T>::type;
+ SetSemanticFunction(&Vwredsum);
+ ReductionOpTestHelper<WT, T>(
+ "Vredsum", /*sew*/ sizeof(T) * 8, instruction_,
+ [](WT val0, T val1) -> WT { return val0 + static_cast<WT>(val1); });
+}
+TEST_F(RiscVCheriotVectorReductionInstructionsTest, Vwredsum32) {
+ using T = int32_t;
+ using WT = WideType<T>::type;
+ SetSemanticFunction(&Vwredsum);
+ ReductionOpTestHelper<WT, T>(
+ "Vredsum", /*sew*/ sizeof(T) * 8, instruction_,
+ [](WT val0, T val1) -> WT { return val0 + static_cast<WT>(val1); });
+}
+
+} // namespace
diff --git a/cheriot/test/riscv_cheriot_vector_true_test.cc b/cheriot/test/riscv_cheriot_vector_true_test.cc
new file mode 100644
index 0000000..05b73f2
--- /dev/null
+++ b/cheriot/test/riscv_cheriot_vector_true_test.cc
@@ -0,0 +1,66 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <cstdint>
+
+#include "cheriot/cheriot_state.h"
+#include "cheriot/cheriot_vector_state.h"
+#include "cheriot/cheriot_vector_true_operand.h"
+#include "googlemock/include/gmock/gmock.h"
+#include "mpact/sim/util/memory/tagged_flat_demand_memory.h"
+
+namespace {
+
+using ::mpact::sim::cheriot::CheriotState;
+using ::mpact::sim::cheriot::CheriotVectorState;
+using ::mpact::sim::cheriot::CheriotVectorTrueOperand;
+using ::mpact::sim::util::TaggedFlatDemandMemory;
+
+constexpr int kVLengthInBytes = 64;
+// Test fixture.
+class CheriotVectorTrueTest : public testing::Test {
+ protected:
+ CheriotVectorTrueTest() : memory_(8) {
+ state_ = new CheriotState("test", &memory_);
+ vstate_ = new CheriotVectorState(state_, kVLengthInBytes);
+ }
+ ~CheriotVectorTrueTest() override {
+ delete state_;
+ delete vstate_;
+ }
+
+ TaggedFlatDemandMemory memory_;
+ CheriotState *state_;
+ CheriotVectorState *vstate_;
+};
+
+TEST_F(CheriotVectorTrueTest, Initial) {
+ auto *op = new CheriotVectorTrueOperand(state_);
+ for (int i = 0; i < op->shape()[0]; ++i) {
+ EXPECT_EQ(op->AsUint8(i), 0xff) << "element: " << i;
+ }
+ delete op;
+}
+
+TEST_F(CheriotVectorTrueTest, Register) {
+ auto *op = new CheriotVectorTrueOperand(state_);
+ auto *reg = op->GetRegister(0);
+ auto span = reg->data_buffer()->Get<uint8_t>();
+ for (int i = 0; i < op->shape()[0]; ++i) {
+ EXPECT_EQ(span[i], 0xff) << "element: " << i;
+ }
+ delete op;
+}
+
+} // namespace
diff --git a/cheriot/test/riscv_cheriot_vector_unary_instructions_test.cc b/cheriot/test/riscv_cheriot_vector_unary_instructions_test.cc
new file mode 100644
index 0000000..29a4a69
--- /dev/null
+++ b/cheriot/test/riscv_cheriot_vector_unary_instructions_test.cc
@@ -0,0 +1,668 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cheriot/riscv_cheriot_vector_unary_instructions.h"
+
+#include <cstdint>
+#include <cstring>
+#include <ios>
+#include <vector>
+
+#include "absl/random/random.h"
+#include "absl/types/span.h"
+#include "cheriot/cheriot_register.h"
+#include "cheriot/test/riscv_cheriot_vector_instructions_test_base.h"
+#include "googlemock/include/gmock/gmock.h"
+#include "mpact/sim/generic/instruction.h"
+#include "mpact/sim/generic/type_helpers.h"
+#include "riscv//riscv_register.h"
+
+namespace {
+
+using ::absl::Span;
+using ::mpact::sim::cheriot::CheriotRegister;
+using ::mpact::sim::generic::Instruction;
+using ::mpact::sim::generic::SameSignedType;
+using ::mpact::sim::riscv::RVVectorRegister;
+
+using ::mpact::sim::cheriot::Vcpop;
+using ::mpact::sim::cheriot::Vfirst;
+using ::mpact::sim::cheriot::Vid;
+using ::mpact::sim::cheriot::Viota;
+using ::mpact::sim::cheriot::Vmsbf;
+using ::mpact::sim::cheriot::Vmsif;
+using ::mpact::sim::cheriot::Vmsof;
+using ::mpact::sim::cheriot::VmvFromScalar;
+using ::mpact::sim::cheriot::VmvToScalar;
+using ::mpact::sim::cheriot::Vsext2;
+using ::mpact::sim::cheriot::Vsext4;
+using ::mpact::sim::cheriot::Vsext8;
+using ::mpact::sim::cheriot::Vzext2;
+using ::mpact::sim::cheriot::Vzext4;
+using ::mpact::sim::cheriot::Vzext8;
+
+using SignedXregType =
+ SameSignedType<CheriotRegister::ValueType, int64_t>::type;
+
+constexpr uint8_t k5AMask[kVectorLengthInBytes] = {
+ 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
+ 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
+ 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
+ 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
+ 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
+ 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
+};
+
+constexpr uint8_t kE7Mask[kVectorLengthInBytes] = {
+ 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7,
+ 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7,
+ 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7,
+ 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7,
+ 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7,
+ 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7,
+};
+
+constexpr uint8_t kAllOnesMask[kVectorLengthInBytes] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+};
+
+class RiscVCheriotVectorUnaryInstructionsTest
+ : public RiscVCheriotVectorInstructionsTestBase {};
+
+// Test move vector element 0 to scalar register.
+TEST_F(RiscVCheriotVectorUnaryInstructionsTest, VmvToScalar) {
+ SetSemanticFunction(&VmvToScalar);
+ AppendRegisterOperands({}, {kRs1Name});
+ AppendVectorRegisterOperands({kVs2}, {});
+ for (int byte_sew : {1, 2, 4, 8}) {
+ int vlen = kVectorLengthInBytes / byte_sew;
+ uint32_t vtype =
+ (kSewSettingsByByteSize[byte_sew] << 3) | kLmulSettingByLogSize[4];
+ ConfigureVectorUnit(vtype, vlen);
+ // Test 10 different values.
+ for (int i = 0; i < 10; i++) {
+ int64_t value;
+ switch (byte_sew) {
+ case 1: {
+ auto val8 = RandomValue<int8_t>();
+ value = static_cast<int64_t>(val8);
+ SetVectorRegisterValues<int8_t>(
+ {{kVs2Name, absl::Span<const int8_t>(&val8, 1)}});
+ break;
+ }
+ case 2: {
+ auto val16 = RandomValue<int16_t>();
+ value = static_cast<int64_t>(val16);
+ SetVectorRegisterValues<int16_t>(
+ {{kVs2Name, absl::Span<const int16_t>(&val16, 1)}});
+ break;
+ }
+ case 4: {
+ auto val32 = RandomValue<int32_t>();
+ value = static_cast<int64_t>(val32);
+ SetVectorRegisterValues<int32_t>(
+ {{kVs2Name, absl::Span<const int32_t>(&val32, 1)}});
+ break;
+ }
+ case 8: {
+ auto val64 = RandomValue<int64_t>();
+ value = val64;
+ SetVectorRegisterValues<int64_t>(
+ {{kVs2Name, absl::Span<const int64_t>(&val64, 1)}});
+ break;
+ }
+ }
+ instruction_->Execute();
+ EXPECT_EQ(creg_[kRs1]->data_buffer()->Get<SignedXregType>(0),
+ static_cast<SignedXregType>(value));
+ }
+ }
+}
+
+// Test move scalar to vector element 0.
+TEST_F(RiscVCheriotVectorUnaryInstructionsTest, VmvFromScalar) {
+ SetSemanticFunction(&VmvFromScalar);
+ AppendRegisterOperands({kRs1Name}, {});
+ AppendVectorRegisterOperands({}, {kVs2});
+ for (int byte_sew : {1, 2, 4, 8}) {
+ int vlen = kVectorLengthInBytes / byte_sew;
+ uint32_t vtype =
+ (kSewSettingsByByteSize[byte_sew] << 3) | kLmulSettingByLogSize[4];
+ ConfigureVectorUnit(vtype, vlen);
+ // Test 10 different values.
+ for (int i = 0; i < 10; i++) {
+ auto value = RandomValue<SignedXregType>();
+ SetRegisterValues<SignedXregType>({{kRs1Name, value}});
+ instruction_->Execute();
+ switch (byte_sew) {
+ case 1:
+ EXPECT_EQ(vreg_[kVs2]->data_buffer()->Get<int8_t>(0),
+ static_cast<int8_t>(value));
+ break;
+ case 2:
+ EXPECT_EQ(vreg_[kVs2]->data_buffer()->Get<int16_t>(0),
+ static_cast<int16_t>(value));
+ break;
+ case 4:
+ EXPECT_EQ(vreg_[kVs2]->data_buffer()->Get<int32_t>(0),
+ static_cast<int32_t>(value));
+ break;
+ case 8:
+ EXPECT_EQ(vreg_[kVs2]->data_buffer()->Get<int64_t>(0),
+ static_cast<int64_t>(value));
+ break;
+ }
+ }
+ }
+}
+
+// Test vector mask population count.
+TEST_F(RiscVCheriotVectorUnaryInstructionsTest, Vcpop) {
+ uint32_t vtype = (kSewSettingsByByteSize[1] << 3) | kLmulSettingByLogSize[7];
+ SetSemanticFunction(&Vcpop);
+ AppendVectorRegisterOperands({kVs2, kVmask}, {});
+ AppendRegisterOperands({}, {kRdName});
+ for (int vlen : {1, 8, 32, 48, 127, 200}) {
+ ConfigureVectorUnit(vtype, vlen);
+ // All 1s for mask and vector.
+ SetVectorRegisterValues<uint8_t>(
+ {{kVs2Name, kAllOnesMask}, {kVmaskName, kAllOnesMask}});
+ instruction_->Execute();
+ EXPECT_EQ(creg_[kRd]->data_buffer()->Get<CheriotRegister::ValueType>(0),
+ vlen);
+
+ // Mask is inverse of vector. Will result in 0.
+ SetVectorRegisterValues<uint8_t>(
+ {{kVs2Name, kA5Mask}, {kVmaskName, k5AMask}});
+ instruction_->Execute();
+ EXPECT_EQ(creg_[kRd]->data_buffer()->Get<CheriotRegister::ValueType>(0), 0);
+ }
+}
+
+// Test vector mask find first set.
+TEST_F(RiscVCheriotVectorUnaryInstructionsTest, Vfirst) {
+ SetSemanticFunction(&Vfirst);
+ AppendVectorRegisterOperands({kVs2, kVmask}, {});
+ AppendRegisterOperands({}, {kRdName});
+ uint8_t reg_value[kVectorLengthInBytes];
+ // Set vtype to byte vector, and vector lmul to 8.
+ uint32_t vtype = (kSewSettingsByByteSize[1] << 3) | kLmulSettingByLogSize[7];
+ ConfigureVectorUnit(vtype, kVectorLengthInBytes * 8);
+
+ // Clear the reg_value array.
+ std::memset(reg_value, 0, kVectorLengthInBytes);
+ // Set the register values.
+ SetVectorRegisterValues<uint8_t>(
+ {{kVs2Name, reg_value}, {kVmaskName, kAllOnesMask}});
+ // Execute the instruction. The result should be minus 1.
+ instruction_->Execute();
+ EXPECT_EQ(creg_[kRd]->data_buffer()->Get<SignedXregType>(0), -1);
+
+ // Pick a random location 20 times and set that bit to 1. Test first that
+ // the correct value is returned, then clear the mask bit that corresponds to
+ // that value, and ensure that now the result is -1.
+ for (int i = 0; i < 20; i++) {
+ // Clear the reg_value array.
+ std::memset(reg_value, 0, kVectorLengthInBytes);
+ // Get a random value for index to set.
+ uint32_t index = absl::Uniform(absl::IntervalClosed, bitgen_, 0,
+ kVectorLengthInBytes * 8 - 1);
+ // Compute the byte and bit index to be set.
+ auto byte_index = index >> 3;
+ auto bit_index = index & 0b111;
+ // Set the bit in the source register.
+ reg_value[byte_index] |= 1 << bit_index;
+ // Set the register values.
+ SetVectorRegisterValues<uint8_t>(
+ {{kVs2Name, reg_value}, {kVmaskName, kAllOnesMask}});
+ // Execute the instruction. The result should be the index value.
+ instruction_->Execute();
+ EXPECT_EQ(creg_[kRd]->data_buffer()->Get<CheriotRegister::ValueType>(0),
+ index);
+
+ // Clear the mask bit for the bit that was set.
+ auto mask_db = vreg_[kVmask]->data_buffer()->Get<uint8_t>();
+ mask_db[byte_index] &= ~(1 << bit_index);
+ // Execute the instruction. The result should be minus 1.
+ instruction_->Execute();
+ EXPECT_EQ(creg_[kRd]->data_buffer()->Get<SignedXregType>(0), -1);
+ }
+}
+
+// Zero extension from sew/2 to sew. Test for sew of 16, 32 and 64 bits.
+TEST_F(RiscVCheriotVectorUnaryInstructionsTest, Vzext2_16) {
+ SetSemanticFunction(&Vzext2);
+ UnaryOpTestHelperV<uint16_t, uint8_t>(
+ "Vzext2_16", /*sew*/ 16, instruction_,
+ [](uint8_t vs2) -> uint16_t { return vs2; });
+}
+
+TEST_F(RiscVCheriotVectorUnaryInstructionsTest, Vzext2_32) {
+ SetSemanticFunction(&Vzext2);
+ UnaryOpTestHelperV<uint32_t, uint16_t>(
+ "Vzext2_32", /*sew*/ 32, instruction_,
+ [](uint16_t vs2) -> uint32_t { return vs2; });
+}
+
+TEST_F(RiscVCheriotVectorUnaryInstructionsTest, Vzext2_64) {
+ SetSemanticFunction(&Vzext2);
+ UnaryOpTestHelperV<uint64_t, uint32_t>(
+ "Vzext2_64", /*sew*/ 64, instruction_,
+ [](uint32_t vs2) -> uint64_t { return vs2; });
+}
+
+// Sign extension from sew/2 to sew. Testing for sew of 16, 32 and 64.
+TEST_F(RiscVCheriotVectorUnaryInstructionsTest, Vsext2_16) {
+ SetSemanticFunction(&Vsext2);
+ UnaryOpTestHelperV<int16_t, int8_t>("Vsext2_16", /*sew*/ 16, instruction_,
+ [](int8_t vs2) -> int16_t {
+ int16_t res = static_cast<int16_t>(vs2);
+ return res;
+ });
+}
+
+TEST_F(RiscVCheriotVectorUnaryInstructionsTest, Vsext2_32) {
+ SetSemanticFunction(&Vsext2);
+ UnaryOpTestHelperV<int32_t, int16_t>(
+ "Vsext2_32", /*sew*/ 32, instruction_, [](int16_t vs2) -> int32_t {
+ int32_t res = static_cast<int32_t>(vs2);
+ return res;
+ });
+}
+
+TEST_F(RiscVCheriotVectorUnaryInstructionsTest, Vsext2_64) {
+ SetSemanticFunction(&Vsext2);
+ UnaryOpTestHelperV<int64_t, int32_t>(
+ "Vsext2_64", /*sew*/ 64, instruction_, [](int32_t vs2) -> int64_t {
+ int64_t res = static_cast<int64_t>(vs2);
+ return res;
+ });
+}
+
+// Zero extension from sew/4 to sew. Testing for sew of 32 and 64.
+TEST_F(RiscVCheriotVectorUnaryInstructionsTest, Vzext4_32) {
+ SetSemanticFunction(&Vzext4);
+ UnaryOpTestHelperV<uint32_t, uint8_t>(
+ "Vzext4_32", /*sew*/ 32, instruction_,
+ [](uint8_t vs2) -> uint32_t { return vs2; });
+}
+
+TEST_F(RiscVCheriotVectorUnaryInstructionsTest, Vzext4_64) {
+ SetSemanticFunction(&Vzext4);
+ UnaryOpTestHelperV<uint64_t, uint16_t>(
+ "Vzext4_32", /*sew*/ 64, instruction_,
+ [](uint16_t vs2) -> uint64_t { return vs2; });
+}
+
+// Sign extension from sew/4 to sew. Testing for sew of 32 and 64.
+TEST_F(RiscVCheriotVectorUnaryInstructionsTest, Vsext4_32) {
+ SetSemanticFunction(&Vsext4);
+ UnaryOpTestHelperV<int32_t, int8_t>("Vzext4_32", /*sew*/ 32, instruction_,
+ [](int8_t vs2) -> int32_t {
+ int32_t res = static_cast<int32_t>(vs2);
+ return res;
+ });
+}
+
+TEST_F(RiscVCheriotVectorUnaryInstructionsTest, Vsext4_64) {
+ SetSemanticFunction(&Vsext4);
+ UnaryOpTestHelperV<int64_t, int16_t>(
+ "Vzext4_64", /*sew*/ 64, instruction_, [](int16_t vs2) -> int64_t {
+ int64_t res = static_cast<int64_t>(vs2);
+ return res;
+ });
+}
+
+// Zero extension from sew/8 to sew. Testing for sew of 64.
+TEST_F(RiscVCheriotVectorUnaryInstructionsTest, Vzext8_64) {
+ SetSemanticFunction(&Vzext8);
+ UnaryOpTestHelperV<uint64_t, uint8_t>("Vsext8_64", /*sew*/ 64, instruction_,
+ [](uint8_t vs2) -> uint64_t {
+ uint64_t res = vs2;
+ return (res << 56) >> 56;
+ });
+}
+
+// Sign extension from sew/8 to sew. Testing for sew of 64.
+TEST_F(RiscVCheriotVectorUnaryInstructionsTest, Vsext8_64) {
+ SetSemanticFunction(&Vsext8);
+ UnaryOpTestHelperV<int64_t, int8_t>("Vzext8_64", /*sew*/ 64, instruction_,
+ [](int8_t vs2) -> int64_t {
+ int64_t res = static_cast<int64_t>(vs2);
+ return res;
+ });
+}
+
+// Test "set before first mask bit".
+TEST_F(RiscVCheriotVectorUnaryInstructionsTest, Vmsbf) {
+ SetSemanticFunction(&Vmsbf);
+ AppendVectorRegisterOperands({kVs2, kVmask}, {kVd});
+ uint8_t reg_value[kVectorLengthInBytes];
+ // Set vtype to byte vector, and vector lmul to 8.
+ uint32_t vtype = (kSewSettingsByByteSize[1] << 3) | kLmulSettingByLogSize[7];
+ ConfigureVectorUnit(vtype, kVectorLengthInBytes * 8);
+
+ // Clear the reg_value array.
+ std::memset(reg_value, 0, kVectorLengthInBytes);
+ // Set the register values.
+ SetVectorRegisterValues<uint8_t>(
+ {{kVs2Name, reg_value}, {kVmaskName, kAllOnesMask}});
+ // Execute the instruction. The result should be all 1s.
+ instruction_->Execute();
+ auto dest_span = vreg_[kVd]->data_buffer()->Get<uint8_t>();
+ for (int i = 0; i < kVectorLengthInBytes; i++) {
+ EXPECT_EQ(dest_span[i], 0b1111'1111) << "Index: " << i;
+ }
+
+ // Pick a random location 20 times and set that bit to 1. Test first that
+ // the vector mask is produced.
+ for (int i = 0; i < 20; i++) {
+ // Clear the reg_value array.
+ // Set the register values.
+ SetVectorRegisterValues<uint8_t>(
+ {{kVs2Name, kA5Mask}, {kVmaskName, k5AMask}});
+ // Get a random value for which bit index to set.
+ uint32_t index = absl::Uniform(absl::IntervalClosedOpen, bitgen_, 0,
+ kVectorLengthInBytes * 8);
+ // Compute the byte and bit index to be set.
+ auto byte_index = index >> 3;
+ auto bit_index = index & 0b111;
+ // Set the bit in the source register and mask registers.
+ auto mask_span = vreg_[kVmask]->data_buffer()->Get<uint8_t>();
+ auto src_span = vreg_[kVs2]->data_buffer()->Get<uint8_t>();
+ mask_span[byte_index] |= 1 << bit_index;
+ src_span[byte_index] |= 1 << bit_index;
+ // Execute the instruction. The result should be the index value.
+ instruction_->Execute();
+ // Check results.
+ dest_span = vreg_[kVd]->data_buffer()->Get<uint8_t>();
+ // First check all the flag values before the byte where the index is.
+ for (int j = 0; j < byte_index; j++) {
+ EXPECT_EQ(dest_span[j],
+ (0b1111'1111 & mask_span[j]) | (dest_span[j] & ~mask_span[j]));
+ }
+ // Check the flag values of the byte where the index is.
+ EXPECT_EQ(dest_span[byte_index],
+ (((1 << bit_index) - 1) & mask_span[byte_index]) |
+ (dest_span[byte_index] & ~mask_span[byte_index]));
+ // Check the flag values after the byte where the index is.
+ for (int j = byte_index + 1; j < kVectorLengthInBytes; j++) {
+ EXPECT_EQ(dest_span[j], dest_span[j] & ~mask_span[j]);
+ }
+ }
+}
+
+TEST_F(RiscVCheriotVectorUnaryInstructionsTest, Vmsof) {
+ SetSemanticFunction(&Vmsof);
+ AppendVectorRegisterOperands({kVs2, kVmask}, {kVd});
+ uint8_t reg_value[kVectorLengthInBytes];
+ // Set vtype to byte vector, and vector lmul to 8.
+ uint32_t vtype = (kSewSettingsByByteSize[1] << 3) | kLmulSettingByLogSize[7];
+ ConfigureVectorUnit(vtype, kVectorLengthInBytes * 8);
+
+ // Clear the reg_value array.
+ std::memset(reg_value, 0, kVectorLengthInBytes);
+ // Set the register values.
+ SetVectorRegisterValues<uint8_t>(
+ {{kVs2Name, reg_value}, {kVmaskName, kAllOnesMask}});
+ // Execute the instruction. The result should be all 1s.
+ instruction_->Execute();
+ auto dest_span = vreg_[kVd]->data_buffer()->Get<uint8_t>();
+ for (int i = 0; i < kVectorLengthInBytes; i++) {
+ EXPECT_EQ(dest_span[i], 0) << "Index: " << i;
+ }
+
+ // Pick a random location 20 times and set that bit to 1. Test first that
+ // the vector mask is produced.
+ for (int i = 0; i < 20; i++) {
+ // Clear the reg_value array.
+ // Set the register values.
+ SetVectorRegisterValues<uint8_t>(
+ {{kVs2Name, kA5Mask}, {kVmaskName, k5AMask}});
+ // Get a random value for which bit index to set.
+ uint32_t index = absl::Uniform(absl::IntervalClosedOpen, bitgen_, 0,
+ kVectorLengthInBytes * 8);
+ // Compute the byte and bit index to be set.
+ auto byte_index = index >> 3;
+ auto bit_index = index & 0b111;
+ // Set the bit in the source register and mask registers.
+ auto mask_span = vreg_[kVmask]->data_buffer()->Get<uint8_t>();
+ auto src_span = vreg_[kVs2]->data_buffer()->Get<uint8_t>();
+ mask_span[byte_index] |= 1 << bit_index;
+ src_span[byte_index] |= 1 << bit_index;
+ // Execute the instruction. The result should be the index value.
+ instruction_->Execute();
+ // Check results.
+ dest_span = vreg_[kVd]->data_buffer()->Get<uint8_t>();
+ // First check all the flag values before the byte where the index is.
+ for (int j = 0; j < byte_index; j++) {
+ EXPECT_EQ(dest_span[j], dest_span[j] & ~mask_span[j]);
+ }
+ // Check the flag values of the byte where the index is.
+ EXPECT_EQ(dest_span[byte_index],
+ ((1 << bit_index) & mask_span[byte_index]) |
+ (dest_span[byte_index] & ~mask_span[byte_index]))
+ << " dest: " << std::hex << (int)dest_span[byte_index]
+ << " mask: " << (int)mask_span[byte_index];
+ // Check the flag values after the byte where the index is.
+ for (int j = byte_index + 1; j < kVectorLengthInBytes; j++) {
+ EXPECT_EQ(dest_span[j], dest_span[j] & ~mask_span[j]);
+ }
+ }
+}
+
+TEST_F(RiscVCheriotVectorUnaryInstructionsTest, Vmsif) {
+ SetSemanticFunction(&Vmsif);
+ AppendVectorRegisterOperands({kVs2, kVmask}, {kVd});
+ uint8_t reg_value[kVectorLengthInBytes];
+ // Set vtype to byte vector, and vector lmul to 8.
+ uint32_t vtype = (kSewSettingsByByteSize[1] << 3) | kLmulSettingByLogSize[7];
+ ConfigureVectorUnit(vtype, kVectorLengthInBytes * 8);
+
+ // Clear the reg_value array.
+ std::memset(reg_value, 0, kVectorLengthInBytes);
+ // Set the register values.
+ SetVectorRegisterValues<uint8_t>(
+ {{kVs2Name, reg_value}, {kVmaskName, kAllOnesMask}});
+ // Execute the instruction. The result should be all 1s.
+ instruction_->Execute();
+ auto dest_span = vreg_[kVd]->data_buffer()->Get<uint8_t>();
+ for (int i = 0; i < kVectorLengthInBytes; i++) {
+ EXPECT_EQ(dest_span[i], 0b1111'1111) << "Index: " << i;
+ }
+
+ // Pick a random location 20 times and set that bit to 1. Test first that
+ // the vector mask is produced.
+ for (int i = 0; i < 20; i++) {
+ // Clear the reg_value array.
+ // Set the register values.
+ SetVectorRegisterValues<uint8_t>(
+ {{kVs2Name, kA5Mask}, {kVmaskName, k5AMask}});
+ // Get a random value for which bit index to set.
+ uint32_t index = absl::Uniform(absl::IntervalClosedOpen, bitgen_, 0,
+ kVectorLengthInBytes * 8);
+ // Compute the byte and bit index to be set.
+ auto byte_index = index >> 3;
+ auto bit_index = index & 0b111;
+ // Set the bit in the source register and mask registers.
+ auto mask_span = vreg_[kVmask]->data_buffer()->Get<uint8_t>();
+ auto src_span = vreg_[kVs2]->data_buffer()->Get<uint8_t>();
+ mask_span[byte_index] |= 1 << bit_index;
+ src_span[byte_index] |= 1 << bit_index;
+ // Execute the instruction. The result should be the index value.
+ instruction_->Execute();
+ // Check results.
+ dest_span = vreg_[kVd]->data_buffer()->Get<uint8_t>();
+ // First check all the flag values before the byte where the index is.
+ for (int j = 0; j < byte_index; j++) {
+ EXPECT_EQ(dest_span[j],
+ (0b1111'1111 & mask_span[j]) | (dest_span[j] & ~mask_span[j]));
+ }
+ // Check the flag values of the byte where the index is.
+ EXPECT_EQ(dest_span[byte_index],
+ (((1 << (bit_index + 1)) - 1) & mask_span[byte_index]) |
+ (dest_span[byte_index] & ~mask_span[byte_index]));
+ // Check the flag values after the byte where the index is.
+ for (int j = byte_index + 1; j < kVectorLengthInBytes; j++) {
+ EXPECT_EQ(dest_span[j], dest_span[j] & ~mask_span[j]);
+ }
+ }
+}
+
+// Helper function for testing Viota instructions.
+template <typename T>
+void TestViota(RiscVCheriotVectorUnaryInstructionsTest *tester,
+ Instruction *inst) {
+ // Set up vector unit.
+ int byte_sew = sizeof(T);
+ uint32_t vtype =
+ (kSewSettingsByByteSize[byte_sew] << 3) | kLmulSettingByLogSize[7];
+ tester->ConfigureVectorUnit(vtype, 1024);
+ int vlen = tester->rv_vector()->vector_length();
+ int num_reg =
+ (vlen * byte_sew + kVectorLengthInBytes - 1) / kVectorLengthInBytes;
+ int num_per_reg = kVectorLengthInBytes / byte_sew;
+ int num_values_per_reg = kVectorLengthInBytes / sizeof(T);
+
+ // Set up instruction.
+ tester->SetSemanticFunction(&Viota);
+ tester->AppendVectorRegisterOperands({kVs2, kVmask}, {kVd});
+ tester->SetVectorRegisterValues<uint8_t>({{kVmaskName, kE7Mask}});
+ int count = vlen;
+ // Initialize vs2 to random values.
+ auto span = tester->vreg()[kVs2]->data_buffer()->Get<T>();
+ for (int i = 0; i < num_values_per_reg; i++) {
+ span[i] = tester->RandomValue<T>();
+ }
+
+ for (int reg = kVd; reg < kVd + num_reg; reg++) {
+ auto reg_span = tester->vreg()[reg]->data_buffer()->Get<T>();
+ for (int i = 0; i < num_per_reg; i++) {
+ reg_span[i] = static_cast<T>(count--);
+ }
+ }
+
+ // Execute instruction.
+ inst->Execute();
+
+ // Check results.
+ const auto mask_span = tester->vreg()[kVmask]->data_buffer()->Get<uint8_t>();
+ count = 0;
+ for (int i = 0; i < vlen; i++) {
+ int reg = kVd + i / num_per_reg;
+ int reg_index = i % num_per_reg;
+ const auto rs2_span = tester->vreg()[kVs2]->data_buffer()->Get<uint8_t>();
+ auto value = tester->vreg()[reg]->data_buffer()->Get<T>(reg_index);
+ int mask_index = i >> 3;
+ int mask_offset = i & 0b111;
+ int mask_value = (mask_span[mask_index] >> mask_offset) & 0b1;
+ const bool rs2_bit = (rs2_span[mask_index] >> mask_offset) & 0b1;
+ if (mask_value) {
+ EXPECT_EQ(value, static_cast<T>(count)) << "active index: " << i;
+ if (rs2_bit) {
+ count++;
+ }
+ } else {
+ EXPECT_EQ(value, static_cast<T>(vlen - i)) << "inactive index: " << i;
+ }
+ }
+}
+
+// Test the viota instruction.
+TEST_F(RiscVCheriotVectorUnaryInstructionsTest, Viota8) {
+ TestViota<uint8_t>(this, instruction_);
+}
+
+TEST_F(RiscVCheriotVectorUnaryInstructionsTest, Viota16) {
+ TestViota<uint16_t>(this, instruction_);
+}
+
+TEST_F(RiscVCheriotVectorUnaryInstructionsTest, Viota32) {
+ TestViota<uint32_t>(this, instruction_);
+}
+
+TEST_F(RiscVCheriotVectorUnaryInstructionsTest, Viota64) {
+ TestViota<uint64_t>(this, instruction_);
+}
+
+// Helper function for testing Vid instructions.
+template <typename T>
+void TestVid(RiscVCheriotVectorUnaryInstructionsTest *tester,
+ Instruction *inst) {
+ // Initialize the vector unit.
+ int byte_sew = sizeof(T);
+ uint32_t vtype =
+ (kSewSettingsByByteSize[byte_sew] << 3) | kLmulSettingByLogSize[7];
+ int vlen = tester->rv_vector()->vector_length();
+ int num_reg =
+ (vlen * byte_sew + kVectorLengthInBytes - 1) / kVectorLengthInBytes;
+ int num_per_reg = kVectorLengthInBytes / byte_sew;
+ tester->ConfigureVectorUnit(vtype, 1024);
+
+ // Configure the instruction.
+ tester->SetSemanticFunction(&Vid);
+ tester->AppendVectorRegisterOperands({kVmask}, {kVd});
+ tester->SetVectorRegisterValues<uint8_t>({{kVmaskName, kE7Mask}});
+ int count = vlen;
+ for (int reg = kVd; reg < kVd + num_reg; reg++) {
+ auto reg_span = tester->vreg()[reg]->data_buffer()->Get<T>();
+ for (int i = 0; i < num_per_reg; i++) {
+ reg_span[i] = static_cast<T>(count--);
+ }
+ }
+
+ // Execute the instruction.
+ inst->Execute();
+
+ // Check the results.
+ auto mask_span = tester->vreg()[kVmask]->data_buffer()->Get<T>();
+ count = 0;
+ for (int i = 0; i < vlen; i++) {
+ int reg = kVd + i / num_per_reg;
+ int reg_index = i % num_per_reg;
+ auto value = tester->vreg()[reg]->data_buffer()->Get<T>(reg_index);
+ int mask_index = i >> 3;
+ int mask_offset = i & 0b111;
+ int mask_value = (mask_span[mask_index] >> mask_offset) & 0b1;
+ if (mask_value) {
+ EXPECT_EQ(value, static_cast<T>(i)) << "active index: " << i;
+ count++;
+ } else {
+ EXPECT_EQ(value, static_cast<T>(vlen - i)) << "inactive index: " << i;
+ }
+ }
+}
+
+// Vid instructions.
+TEST_F(RiscVCheriotVectorUnaryInstructionsTest, Vid8) {
+ TestVid<uint8_t>(this, instruction_);
+}
+
+TEST_F(RiscVCheriotVectorUnaryInstructionsTest, Vid16) {
+ TestVid<uint16_t>(this, instruction_);
+}
+
+TEST_F(RiscVCheriotVectorUnaryInstructionsTest, Vid32) {
+ TestVid<uint32_t>(this, instruction_);
+}
+
+TEST_F(RiscVCheriotVectorUnaryInstructionsTest, Vid64) {
+ TestVid<uint64_t>(this, instruction_);
+}
+} // namespace