From 527748c6b35cf23477a88b8b9d70b82d28fe6293 Mon Sep 17 00:00:00 2001 From: Eduardo Bart Date: Mon, 20 Nov 2023 12:30:22 -0300 Subject: [PATCH] feat: add soft yield runtime config using hints of SRAIW instruction --- src/clua-cartesi.cpp | 1 + src/clua-machine-util.cpp | 1 + src/grpc-virtual-machine.cpp | 3 ++- src/i-state-access.h | 5 +++++ src/interpret.cpp | 10 +++++++-- src/interpret.h | 1 + src/json-util.cpp | 4 +++- src/jsonrpc-discover.json | 4 ++++ src/jsonrpc-remote-machine.cpp | 2 ++ src/machine-c-api.cpp | 1 + src/machine-c-api.h | 2 ++ src/machine-runtime-config.h | 1 + src/machine-state.h | 3 +++ src/machine.cpp | 2 ++ src/state-access.h | 4 ++++ src/tests/machine-test.lua | 35 ++++++++++++++++++++++++++---- src/tests/util.lua | 4 ++-- uarch/uarch-machine-state-access.h | 5 +++++ 18 files changed, 78 insertions(+), 10 deletions(-) diff --git a/src/clua-cartesi.cpp b/src/clua-cartesi.cpp index 569c65c7b..5ea054687 100644 --- a/src/clua-cartesi.cpp +++ b/src/clua-cartesi.cpp @@ -118,6 +118,7 @@ CM_API int luaopen_cartesi(lua_State *L) { clua_setintegerfield(L, CM_BREAK_REASON_HALTED, "BREAK_REASON_HALTED", -1); clua_setintegerfield(L, CM_BREAK_REASON_YIELDED_MANUALLY, "BREAK_REASON_YIELDED_MANUALLY", -1); clua_setintegerfield(L, CM_BREAK_REASON_YIELDED_AUTOMATICALLY, "BREAK_REASON_YIELDED_AUTOMATICALLY", -1); + clua_setintegerfield(L, CM_BREAK_REASON_YIELDED_SOFTLY, "BREAK_REASON_YIELDED_SOFTLY", -1); clua_setintegerfield(L, CM_BREAK_REASON_REACHED_TARGET_MCYCLE, "BREAK_REASON_REACHED_TARGET_MCYCLE", -1); clua_setintegerfield(L, CM_UARCH_BREAK_REASON_REACHED_TARGET_CYCLE, "UARCH_BREAK_REASON_REACHED_TARGET_CYCLE", -1); clua_setintegerfield(L, CM_UARCH_BREAK_REASON_UARCH_HALTED, "UARCH_BREAK_REASON_UARCH_HALTED", -1); diff --git a/src/clua-machine-util.cpp b/src/clua-machine-util.cpp index e24558a66..785f97ff7 100644 --- a/src/clua-machine-util.cpp +++ b/src/clua-machine-util.cpp @@ -1277,6 +1277,7 @@ cm_machine_runtime_config *clua_check_cm_machine_runtime_config(lua_State *L, in check_cm_htif_runtime_config(L, tabidx, &config->htif); config->skip_root_hash_check = opt_boolean_field(L, tabidx, "skip_root_hash_check"); config->skip_version_check = opt_boolean_field(L, tabidx, "skip_version_check"); + config->soft_yield = opt_boolean_field(L, tabidx, "soft_yield"); managed.release(); lua_pop(L, 1); return config; diff --git a/src/grpc-virtual-machine.cpp b/src/grpc-virtual-machine.cpp index 8fcb1e1a4..852283ad1 100644 --- a/src/grpc-virtual-machine.cpp +++ b/src/grpc-virtual-machine.cpp @@ -313,8 +313,9 @@ interpreter_break_reason grpc_virtual_machine::do_run(uint64_t mcycle_end) { return interpreter_break_reason::yielded_manually; } else if (response.iflags_x()) { return interpreter_break_reason::yielded_automatically; + } else if (response.mcycle() != mcycle_end) { + return interpreter_break_reason::yielded_softly; } else { - assert(response.mcycle() == mcycle_end); return interpreter_break_reason::reached_target_mcycle; } } diff --git a/src/i-state-access.h b/src/i-state-access.h index e6f5a9fa3..f8dafd01d 100644 --- a/src/i-state-access.h +++ b/src/i-state-access.h @@ -734,6 +734,11 @@ class i_state_access { // CRTP return derived().do_flush_tlb_vaddr(vaddr); } + /// \brief Returns true if soft yield HINT instruction is enabled at runtime + bool get_soft_yield() { + return derived().do_get_soft_yield(); + } + #ifdef DUMP_COUNTERS auto &get_statistics() { return derived().do_get_statistics(); diff --git a/src/interpret.cpp b/src/interpret.cpp index 4e03b6849..f6fc8a6bb 100644 --- a/src/interpret.cpp +++ b/src/interpret.cpp @@ -2979,6 +2979,11 @@ static FORCE_INLINE execute_status execute_SRLIW(STATE_ACCESS &a, uint64_t &pc, template static FORCE_INLINE execute_status execute_SRAIW(STATE_ACCESS &a, uint64_t &pc, uint32_t insn) { dump_insn(a, pc, insn, "sraiw"); + // When rd=0 the instruction is a HINT, and we consider it as a soft yield when rs1 == 31 + if (unlikely(insn_get_rd(insn) == 0 && insn_get_rs1(insn) == 31 && a.get_soft_yield())) { + // Force the main interpreter loop to break + return advance_to_next_insn(a, pc, execute_status::success_and_yield); + } return execute_arithmetic_immediate(a, pc, insn, [](uint64_t rs1, int32_t imm) -> uint64_t { const int32_t rs1w = static_cast(rs1) >> (imm & 0b11111); return static_cast(rs1w); @@ -5642,8 +5647,9 @@ interpreter_break_reason interpret(STATE_ACCESS &a, uint64_t mcycle_end) { return interpreter_break_reason::yielded_manually; } else if (a.read_iflags_X()) { return interpreter_break_reason::yielded_automatically; - } else { // Reached mcycle_end - assert(a.read_mcycle() == mcycle_end); // LCOV_EXCL_LINE + } else if (a.read_mcycle() != mcycle_end) { + return interpreter_break_reason::yielded_softly; + } else { // Reached mcycle_end return interpreter_break_reason::reached_target_mcycle; } } diff --git a/src/interpret.h b/src/interpret.h index 4397a625a..b9af6a90a 100644 --- a/src/interpret.h +++ b/src/interpret.h @@ -42,6 +42,7 @@ enum class interpreter_break_reason { halted, yielded_manually, yielded_automatically, + yielded_softly, reached_target_mcycle }; diff --git a/src/json-util.cpp b/src/json-util.cpp index 860f412e1..f2ca4c0e2 100644 --- a/src/json-util.cpp +++ b/src/json-util.cpp @@ -192,7 +192,7 @@ interpreter_break_reason interpreter_break_reason_from_name(const std::string &n using ibr = interpreter_break_reason; const static std::unordered_map g_ibr_name = {{"failed", ibr::failed}, {"halted", ibr::halted}, {"yielded_manually", ibr::yielded_manually}, {"yielded_automatically", ibr::yielded_automatically}, - {"reached_target_mcycle", ibr::reached_target_mcycle}}; + {"yielded_softly", ibr::yielded_softly}, {"reached_target_mcycle", ibr::reached_target_mcycle}}; auto got = g_ibr_name.find(name); if (got == g_ibr_name.end()) { throw std::domain_error{"invalid interpreter break reason"}; @@ -436,6 +436,7 @@ void ju_get_opt_field(const nlohmann::json &j, const K &key, machine_runtime_con ju_get_field(j[key], "htif"s, value.htif, path + to_string(key) + "/"); ju_get_opt_field(j[key], "skip_root_hash_check"s, value.skip_root_hash_check, path + to_string(key) + "/"); ju_get_opt_field(j[key], "skip_version_check"s, value.skip_version_check, path + to_string(key) + "/"); + ju_get_opt_field(j[key], "soft_yield"s, value.soft_yield, path + to_string(key) + "/"); } template void ju_get_opt_field(const nlohmann::json &j, const uint64_t &key, machine_runtime_config &value, @@ -1257,6 +1258,7 @@ void to_json(nlohmann::json &j, const machine_runtime_config &runtime) { {"htif", runtime.htif}, {"skip_root_hash_check", runtime.skip_root_hash_check}, {"skip_version_check", runtime.skip_version_check}, + {"soft_yield", runtime.soft_yield}, }; } diff --git a/src/jsonrpc-discover.json b/src/jsonrpc-discover.json index 14581145b..e2455d917 100644 --- a/src/jsonrpc-discover.json +++ b/src/jsonrpc-discover.json @@ -1084,6 +1084,7 @@ "halted", "yielded_manually", "yielded_automatically", + "yielded_softly", "reached_target_mcycle" ] }, @@ -1672,6 +1673,9 @@ }, "skip_version_check": { "type": "boolean" + }, + "soft_yield": { + "type": "boolean" } } }, diff --git a/src/jsonrpc-remote-machine.cpp b/src/jsonrpc-remote-machine.cpp index c6738784f..c846d5f79 100644 --- a/src/jsonrpc-remote-machine.cpp +++ b/src/jsonrpc-remote-machine.cpp @@ -756,6 +756,8 @@ static std::string interpreter_break_reason_name(cartesi::interpreter_break_reas return "yielded_manually"; case R::yielded_automatically: return "yielded_automatically"; + case R::yielded_softly: + return "yielded_softly"; case R::reached_target_mcycle: return "reached_target_mcycle"; } diff --git a/src/machine-c-api.cpp b/src/machine-c-api.cpp index fc80358d2..c1153ec2e 100644 --- a/src/machine-c-api.cpp +++ b/src/machine-c-api.cpp @@ -368,6 +368,7 @@ cartesi::machine_runtime_config convert_from_c(const cm_machine_runtime_config * new_cpp_machine_runtime_config.htif = cartesi::htif_runtime_config{c_config->htif.no_console_putchar}; new_cpp_machine_runtime_config.skip_root_hash_check = c_config->skip_root_hash_check; new_cpp_machine_runtime_config.skip_version_check = c_config->skip_version_check; + new_cpp_machine_runtime_config.soft_yield = c_config->soft_yield; return new_cpp_machine_runtime_config; } diff --git a/src/machine-c-api.h b/src/machine-c-api.h index 4cae7c3cf..ccb9a12d7 100644 --- a/src/machine-c-api.h +++ b/src/machine-c-api.h @@ -94,6 +94,7 @@ typedef enum { // NOLINT(modernize-use-using) CM_BREAK_REASON_HALTED, CM_BREAK_REASON_YIELDED_MANUALLY, CM_BREAK_REASON_YIELDED_AUTOMATICALLY, + CM_BREAK_REASON_YIELDED_SOFTLY, CM_BREAK_REASON_REACHED_TARGET_MCYCLE } CM_BREAK_REASON; @@ -366,6 +367,7 @@ typedef struct { // NOLINT(modernize-use-using) cm_htif_runtime_config htif; bool skip_root_hash_check; bool skip_version_check; + bool soft_yield; } cm_machine_runtime_config; /// \brief Machine instance handle diff --git a/src/machine-runtime-config.h b/src/machine-runtime-config.h index 9cc20307c..a98013865 100644 --- a/src/machine-runtime-config.h +++ b/src/machine-runtime-config.h @@ -38,6 +38,7 @@ struct machine_runtime_config { htif_runtime_config htif{}; bool skip_root_hash_check{}; bool skip_version_check{}; + bool soft_yield{}; }; /// \brief CONCURRENCY constants diff --git a/src/machine-state.h b/src/machine-state.h index c042a6ee1..f7135a74f 100644 --- a/src/machine-state.h +++ b/src/machine-state.h @@ -115,6 +115,9 @@ struct machine_state { uint64_t iyield; ///< CSR iyield. } htif; + /// Soft yield + bool soft_yield; + /// Map of physical memory ranges boost::container::static_vector pmas; diff --git a/src/machine.cpp b/src/machine.cpp index 6de5ee927..ea2ae6a5c 100644 --- a/src/machine.cpp +++ b/src/machine.cpp @@ -303,6 +303,8 @@ machine::machine(const machine_config &c, const machine_runtime_config &r) : throw std::invalid_argument{"mimpid mismatch, emulator version is incompatible"}; } + m_s.soft_yield = r.soft_yield; + // General purpose registers for (int i = 1; i < X_REG_COUNT; i++) { write_x(i, m_c.processor.x[i]); diff --git a/src/state-access.h b/src/state-access.h index fecb938ca..e1c29a364 100644 --- a/src/state-access.h +++ b/src/state-access.h @@ -604,6 +604,10 @@ class state_access : public i_state_access { do_flush_tlb_type(); } + bool do_get_soft_yield() { + return m_m.get_state().soft_yield; + } + #ifdef DUMP_COUNTERS machine_statistics &do_get_statistics() { return m_m.get_state().stats; diff --git a/src/tests/machine-test.lua b/src/tests/machine-test.lua index 70e87924c..203cb9ef5 100755 --- a/src/tests/machine-test.lua +++ b/src/tests/machine-test.lua @@ -148,11 +148,11 @@ local function connect() end local remote -local function build_machine(type, config) +local function build_machine(type, config, runtime_config) config = config or { ram = { length = 1 << 20 }, } - local runtime = { + runtime_config = runtime_config or { concurrency = { update_merkle_tree = 0, }, @@ -160,9 +160,9 @@ local function build_machine(type, config) local new_machine if type ~= "local" then if not remote then remote = connect() end - new_machine = assert(remote.machine(config, runtime)) + new_machine = assert(remote.machine(config, runtime_config)) else - new_machine = assert(cartesi.machine(config, runtime)) + new_machine = assert(cartesi.machine(config, runtime_config)) end return new_machine end @@ -303,6 +303,33 @@ do_test("mcycle and root hash should match", function(machine) assert(root_hash == calculated_end_hash, "machine hash does not match after on end cycle") end) +if machine_type == "local" then + print("\n\ntesting soft yield") + test_util.make_do_test(build_machine, machine_type, { + ram = { length = 1 << 20 }, + }, { + soft_yield = true, + })("check soft yield", function(machine) + -- The following is a RISC-V bytecode that cause a soft yield immediately, + local function sraiw(rd, rs1, shamt) return 0x4000501b | (rd << 7) | (rs1 << 15) | (shamt << 20) end + local soft_yield_insn = sraiw(0, 31, 7) + + machine:write_memory(machine:read_pc(), string.pack(" = build_machine(type, config) + local machine = build_machine(type, config, runtime_config) f(machine) print("<<<<<<<<<<<<<<<< passed >>>>>>>>>>>>>>>") end diff --git a/uarch/uarch-machine-state-access.h b/uarch/uarch-machine-state-access.h index 7a563e97f..5f6a8ef27 100644 --- a/uarch/uarch-machine-state-access.h +++ b/uarch/uarch-machine-state-access.h @@ -721,6 +721,11 @@ class uarch_machine_state_access : public i_state_access(); do_flush_tlb_type(); } + + bool do_get_soft_yield() { + // Soft yield is meaningless in microarchitecture + return false; + } }; } // namespace cartesi