Skip to content

Commit

Permalink
Merge pull request #164 from fwsGonzo/improve_tcc_unknown_instr
Browse files Browse the repository at this point in the history
bintr: Improve TCC execution of unknown instructions
  • Loading branch information
fwsGonzo committed Jun 22, 2024
2 parents 68f745f + 108ba64 commit 315d4d3
Show file tree
Hide file tree
Showing 7 changed files with 53 additions and 37 deletions.
10 changes: 5 additions & 5 deletions lib/libriscv/cpu.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,10 +144,10 @@ namespace riscv
static const instruction_t& get_unimplemented_instruction() noexcept;

// Set current exception
void set_current_exception(unsigned exception) noexcept { m_current_exception = exception + 1; }
void clear_current_exception() noexcept { m_current_exception = 0; }
bool has_current_exception() const noexcept { return m_current_exception != 0; }
exceptions current_exception() const noexcept { return exceptions(m_current_exception-1); }
void set_current_exception(std::exception_ptr&& ptr) noexcept { m_current_exception = std::move(ptr); }
void clear_current_exception() noexcept { m_current_exception = nullptr; }
bool has_current_exception() const noexcept { return m_current_exception != nullptr; }
auto& current_exception() const noexcept { return m_current_exception; }

private:
Registers<W> m_regs;
Expand All @@ -162,7 +162,7 @@ namespace riscv
const unsigned m_cpuid;

// The current exception (used by eg. TCC which doesn't create unwinding tables)
unsigned m_current_exception = 0;
std::exception_ptr m_current_exception = nullptr;

// The default execute fault simply triggers the exception
execute_fault_t m_fault = [] (auto& cpu, auto&) {
Expand Down
14 changes: 7 additions & 7 deletions lib/libriscv/cpu_dispatch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -348,13 +348,8 @@ INSTRUCTION(RV32I_BC_STOP, rv32i_stop) {
if constexpr (libtcc_enabled)
{
// We need to check if we have a current exception
if (CPU().has_current_exception())
{
// We have an exception, so we need to handle it
const auto except_num = CPU().current_exception();
CPU().clear_current_exception();
CPU().trigger_exception(except_num, CPU().pc());
}
if (UNLIKELY(CPU().has_current_exception()))
goto handle_rethrow_exception;
}

registers().pc = pc;
Expand All @@ -380,6 +375,11 @@ new_execute_segment: {
pc = (decoder - exec_decoder) << DecoderCache<W>::SHIFT;
registers().pc = pc;
trigger_exception(ILLEGAL_OPCODE, decoder->instr);
handle_rethrow_exception:
// We have an exception, so we need to rethrow it
const auto except = CPU().current_exception();
CPU().clear_current_exception();
std::rethrow_exception(except);

} // CPU::simulate_XXX()

Expand Down
1 change: 1 addition & 0 deletions lib/libriscv/tr_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ static struct CallbackTable {
void (*unknown_syscall)(CPU*, addr_t);
void (*system)(CPU*, uint32_t);
unsigned (*execute)(CPU*, uint32_t);
unsigned (*execute_handler)(CPU*, unsigned, uint32_t);
handler* handlers;
void (*exception) (CPU*, addr_t, int);
void (*trace) (CPU*, const char*, addr_t, uint32_t);
Expand Down
1 change: 1 addition & 0 deletions lib/libriscv/tr_api.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ namespace riscv {
void (*unknown_syscall)(CPU<W>&, address_type<W>);
void (*system)(CPU<W>&, uint32_t);
unsigned (*execute)(CPU<W>&, uint32_t);
unsigned (*execute_handler)(CPU<W>&, unsigned, uint32_t);
void (**handlers)(CPU<W>&, uint32_t);
void (*trigger_exception)(CPU<W>&, address_type<W>, int);
void (*trace)(CPU<W>&, const char*, address_type<W>, uint32_t);
Expand Down
12 changes: 7 additions & 5 deletions lib/libriscv/tr_emit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@
// the order of intruction handlers, so we need to lazily get the handler index by
// calling the execute function the first time.
#ifdef RISCV_LIBTCC
#define UNKNOWN_INSTRUCTION() \
code += "if (api.execute(cpu, " + std::to_string(instr.whole) + "))\n" \
" return (ReturnValues){0, 0};\n"; // Exception thrown
#define UNKNOWN_INSTRUCTION() { \
auto* handler = cpu.decode(instr).handler; \
const auto index = DecoderData<W>::handler_index_for(handler); \
code += "if (api.execute_handler(cpu, " + std::to_string(index) + ", " + std::to_string(instr.whole) + "))\n" \
" return (ReturnValues){0, 0};\n"; } // Exception thrown
#else
#define UNKNOWN_INSTRUCTION() { \
code += "{ static int handler_idx = 0;\n"; \
Expand Down Expand Up @@ -1416,13 +1418,13 @@ void Emitter<W>::emit()
UNKNOWN_INSTRUCTION();
}
} else { // FPCLASSIFY etc.
code += "api.execute(cpu, " + std::to_string(instr.whole) + ");\n";
UNKNOWN_INSTRUCTION();
} break;
} // fpfunc
} else UNKNOWN_INSTRUCTION();
} break; // RV32F_FPFUNC
case RV32A_ATOMIC: // General handler for atomics
code += "api.execute(cpu, " + std::to_string(instr.whole) + ");\n";
UNKNOWN_INSTRUCTION();
break;
case RV32V_OP: { // General handler for vector instructions
#ifdef RISCV_EXT_VECTOR
Expand Down
5 changes: 5 additions & 0 deletions lib/libriscv/tr_tcc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ namespace riscv
tcc_define_symbol(state, "ARCH", "HOST_UNKNOWN");
tcc_set_options(state, "-std=c99 -O2");

#ifdef _WIN32
// Look for some headers in the win32 directory
tcc_add_include_path(state, "win32");
#endif

if (!libtcc1.empty())
tcc_add_library_path(state, libtcc1.c_str());
#ifdef LIBTCC_LIBRARY_PATH
Expand Down
47 changes: 27 additions & 20 deletions lib/libriscv/tr_translate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -634,8 +634,8 @@ bool CPU<W>::initialize_translated_segment(DecodedExecuteSegment<W>&, void* dyli
case 8: return cpu.machine().memory.template read<uint64_t>(addr);
default: throw MachineException(ILLEGAL_OPERATION, "Invalid memory read size", size);
}
} catch (const MachineException& e) {
cpu.set_current_exception(e.type());
} catch (...) {
cpu.set_current_exception(std::current_exception());
cpu.machine().stop();
return 0;
}
Expand All @@ -659,8 +659,8 @@ bool CPU<W>::initialize_translated_segment(DecodedExecuteSegment<W>&, void* dyli
case 8: cpu.machine().memory.template write<uint64_t>(addr, value); break;
default: throw MachineException(ILLEGAL_OPERATION, "Invalid memory write size", size);
}
} catch (const MachineException& e) {
cpu.set_current_exception(e.type());
} catch (...) {
cpu.set_current_exception(std::current_exception());
cpu.machine().stop();
}
} else {
Expand Down Expand Up @@ -696,12 +696,8 @@ bool CPU<W>::initialize_translated_segment(DecodedExecuteSegment<W>&, void* dyli
const auto current_pc = cpu.registers().pc;
cpu.machine().system_call(sysno);
return cpu.registers().pc != current_pc || cpu.machine().stopped();
} catch (const MachineException& e) {
cpu.set_current_exception(e.type());
cpu.machine().stop();
return false;
} catch (const std::exception& e) {
cpu.set_current_exception(SYSTEM_CALL_FAILED);
} catch (...) {
cpu.set_current_exception(std::current_exception());
cpu.machine().stop();
return false;
}
Expand All @@ -716,11 +712,8 @@ bool CPU<W>::initialize_translated_segment(DecodedExecuteSegment<W>&, void* dyli
if constexpr (libtcc_enabled) {
try {
cpu.machine().system(rv32i_instruction{instr});
} catch (const MachineException& e) {
cpu.set_current_exception(e.type());
cpu.machine().stop();
} catch (const std::exception& e) {
cpu.set_current_exception(SYSTEM_CALL_FAILED);
} catch (...) {
cpu.set_current_exception(std::current_exception());
cpu.machine().stop();
}
} else {
Expand All @@ -733,8 +726,8 @@ bool CPU<W>::initialize_translated_segment(DecodedExecuteSegment<W>&, void* dyli
try {
cpu.decode(rvi).handler(cpu, rvi);
return 0;
} catch (const MachineException& e) {
cpu.set_current_exception(e.type());
} catch (...) {
cpu.set_current_exception(std::current_exception());
return 1;
}
} else {
Expand All @@ -743,16 +736,30 @@ bool CPU<W>::initialize_translated_segment(DecodedExecuteSegment<W>&, void* dyli
return DecoderData<W>::handler_index_for(handler);
}
},
.execute_handler = [] (CPU<W>& cpu, unsigned index, uint32_t instr) -> unsigned {
const rv32i_instruction rvi{instr};
try {
DecoderData<W>::get_handlers()[index](cpu, rvi);
return 0;
} catch (...) {
cpu.set_current_exception(std::current_exception());
return 1;
}
},
.handlers = (void (**)(CPU<W>&, uint32_t)) DecoderData<W>::get_handlers(),
.trigger_exception = [] (CPU<W>& cpu, address_type<W> pc, int e) {
cpu.registers().pc = pc; // XXX: Set PC to the failing instruction (?)
if constexpr (libtcc_enabled) {
// If we're using libtcc, we can't throw C++ exceptions because
// there's no unwinding support. But we can mark an exception
// in the CPU state and return back to dispatch.
cpu.set_current_exception(e);
// Trigger a slow-path in dispatch (which will check for exceptions)
cpu.machine().stop();
try {
cpu.trigger_exception(e);
} catch (...) {
cpu.set_current_exception(std::current_exception());
// Trigger a slow-path in dispatch (which will check for exceptions)
cpu.machine().stop();
}
} else {
cpu.trigger_exception(e);
}
Expand Down

0 comments on commit 315d4d3

Please sign in to comment.