Skip to content

Commit

Permalink
feat: add soft yield runtime config using hints of SRAIW instruction
Browse files Browse the repository at this point in the history
  • Loading branch information
edubart committed Nov 20, 2023
1 parent 6274ec0 commit 527748c
Show file tree
Hide file tree
Showing 18 changed files with 78 additions and 10 deletions.
1 change: 1 addition & 0 deletions src/clua-cartesi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions src/clua-machine-util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
3 changes: 2 additions & 1 deletion src/grpc-virtual-machine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Expand Down
5 changes: 5 additions & 0 deletions src/i-state-access.h
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
10 changes: 8 additions & 2 deletions src/interpret.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2979,6 +2979,11 @@ static FORCE_INLINE execute_status execute_SRLIW(STATE_ACCESS &a, uint64_t &pc,
template <typename STATE_ACCESS>
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<int32_t>(rs1) >> (imm & 0b11111);
return static_cast<uint64_t>(rs1w);
Expand Down Expand Up @@ -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;
}
}
Expand Down
1 change: 1 addition & 0 deletions src/interpret.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ enum class interpreter_break_reason {
halted,
yielded_manually,
yielded_automatically,
yielded_softly,
reached_target_mcycle
};

Expand Down
4 changes: 3 additions & 1 deletion src/json-util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::string, ibr> 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"};
Expand Down Expand Up @@ -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<uint64_t>(const nlohmann::json &j, const uint64_t &key, machine_runtime_config &value,
Expand Down Expand Up @@ -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},
};
}

Expand Down
4 changes: 4 additions & 0 deletions src/jsonrpc-discover.json
Original file line number Diff line number Diff line change
Expand Up @@ -1084,6 +1084,7 @@
"halted",
"yielded_manually",
"yielded_automatically",
"yielded_softly",
"reached_target_mcycle"
]
},
Expand Down Expand Up @@ -1672,6 +1673,9 @@
},
"skip_version_check": {
"type": "boolean"
},
"soft_yield": {
"type": "boolean"
}
}
},
Expand Down
2 changes: 2 additions & 0 deletions src/jsonrpc-remote-machine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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";
}
Expand Down
1 change: 1 addition & 0 deletions src/machine-c-api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
2 changes: 2 additions & 0 deletions src/machine-c-api.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions src/machine-runtime-config.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions src/machine-state.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<pma_entry, PMA_MAX> pmas;

Expand Down
2 changes: 2 additions & 0 deletions src/machine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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]);
Expand Down
4 changes: 4 additions & 0 deletions src/state-access.h
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,10 @@ class state_access : public i_state_access<state_access, pma_entry> {
do_flush_tlb_type<TLB_WRITE>();
}

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;
Expand Down
35 changes: 31 additions & 4 deletions src/tests/machine-test.lua
Original file line number Diff line number Diff line change
Expand Up @@ -148,21 +148,21 @@ 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,
},
}
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
Expand Down Expand Up @@ -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("<I4", soft_yield_insn))

assert(machine:run(1000) == cartesi.BREAK_REASON_YIELDED_SOFTLY)

-- Check machine state
assert(machine:read_mcycle() == 1, "machine mcycle should be 1")
assert(not machine:read_iflags_H())
assert(not machine:read_iflags_Y())
assert(not machine:read_iflags_X())

-- Check if previous instruction match
local prev_insn = string.unpack("<I4", machine:read_virtual_memory(machine:read_pc() - 4, 4))
assert(prev_insn == soft_yield_insn)
end)
end

print("\n\nwrite something to ram memory and check if hash and proof matches")
do_test("proof and root hash should match", function(machine)
local ram_address_start = 0x80000000
Expand Down
4 changes: 2 additions & 2 deletions src/tests/util.lua
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,10 @@ function test_util.create_test_uarch_program(instructions)
return file_path
end

function test_util.make_do_test(build_machine, type, config)
function test_util.make_do_test(build_machine, type, config, runtime_config)
return function(description, f)
io.write(" " .. description .. "...\n")
local machine <close> = build_machine(type, config)
local machine <close> = build_machine(type, config, runtime_config)
f(machine)
print("<<<<<<<<<<<<<<<< passed >>>>>>>>>>>>>>>")
end
Expand Down
5 changes: 5 additions & 0 deletions uarch/uarch-machine-state-access.h
Original file line number Diff line number Diff line change
Expand Up @@ -721,6 +721,11 @@ class uarch_machine_state_access : public i_state_access<uarch_machine_state_acc
do_flush_tlb_type<TLB_READ>();
do_flush_tlb_type<TLB_WRITE>();
}

bool do_get_soft_yield() {
// Soft yield is meaningless in microarchitecture
return false;
}
};

} // namespace cartesi
Expand Down

0 comments on commit 527748c

Please sign in to comment.