|
|
|
@ -1064,6 +1064,12 @@ reg_t processor_t::get_csr(int which, insn_t insn, bool write, bool peek) |
|
|
|
throw trap_illegal_instruction(insn.bits()); |
|
|
|
} |
|
|
|
|
|
|
|
const insn_desc_t insn_desc_t::illegal_instruction = { |
|
|
|
0, 0, |
|
|
|
&::illegal_instruction, &::illegal_instruction, &::illegal_instruction, &::illegal_instruction, |
|
|
|
&::illegal_instruction, &::illegal_instruction, &::illegal_instruction, &::illegal_instruction |
|
|
|
}; |
|
|
|
|
|
|
|
reg_t illegal_instruction(processor_t UNUSED *p, insn_t insn, reg_t UNUSED pc) |
|
|
|
{ |
|
|
|
// The illegal instruction can be longer than ILEN bits, where the tval will
|
|
|
|
@ -1072,51 +1078,30 @@ reg_t illegal_instruction(processor_t UNUSED *p, insn_t insn, reg_t UNUSED pc) |
|
|
|
throw trap_illegal_instruction(insn.bits() & 0xffffffffULL); |
|
|
|
} |
|
|
|
|
|
|
|
static insn_desc_t |
|
|
|
propagate_instruction_in_vector(std::vector<insn_desc_t> &instructions, |
|
|
|
std::vector<insn_desc_t>::iterator it) { |
|
|
|
assert(it != instructions.end()); |
|
|
|
insn_desc_t desc = *it; |
|
|
|
if (it->mask != 0 && it != instructions.begin() && |
|
|
|
std::next(it) != instructions.end()) { |
|
|
|
if (it->match != std::prev(it)->match && |
|
|
|
it->match != std::next(it)->match) { |
|
|
|
// move to front of opcode list to reduce miss penalty
|
|
|
|
while (--it >= instructions.begin()) |
|
|
|
*std::next(it) = *it; |
|
|
|
instructions[0] = desc; |
|
|
|
} |
|
|
|
} |
|
|
|
return desc; |
|
|
|
} |
|
|
|
|
|
|
|
insn_func_t processor_t::decode_insn(insn_t insn) |
|
|
|
{ |
|
|
|
// look up opcode in hash table
|
|
|
|
size_t idx = insn.bits() % OPCODE_CACHE_SIZE; |
|
|
|
insn_desc_t desc = opcode_cache[idx]; |
|
|
|
auto [hit, desc] = opcode_cache[idx].lookup(insn.bits()); |
|
|
|
|
|
|
|
bool rve = extension_enabled('E'); |
|
|
|
|
|
|
|
if (unlikely(insn.bits() != desc.match)) { |
|
|
|
if (unlikely(!hit)) { |
|
|
|
// fall back to linear search
|
|
|
|
auto matching = [insn_bits = insn.bits()](const insn_desc_t &d) { |
|
|
|
return (insn_bits & d.mask) == d.match; |
|
|
|
}; |
|
|
|
auto p = std::find_if(custom_instructions.begin(), |
|
|
|
custom_instructions.end(), matching); |
|
|
|
if (p != custom_instructions.end()) { |
|
|
|
desc = propagate_instruction_in_vector(custom_instructions, p); |
|
|
|
} else { |
|
|
|
if (p == custom_instructions.end()) { |
|
|
|
p = std::find_if(instructions.begin(), instructions.end(), matching); |
|
|
|
assert(p != instructions.end()); |
|
|
|
desc = propagate_instruction_in_vector(instructions, p); |
|
|
|
} |
|
|
|
opcode_cache[idx] = desc; |
|
|
|
opcode_cache[idx].match = insn.bits(); |
|
|
|
desc = &*p; |
|
|
|
opcode_cache[idx].replace(insn.bits(), desc); |
|
|
|
} |
|
|
|
|
|
|
|
return desc.func(xlen, rve, log_commits_enabled); |
|
|
|
return desc->func(xlen, rve, log_commits_enabled); |
|
|
|
} |
|
|
|
|
|
|
|
void processor_t::register_insn(insn_desc_t desc, bool is_custom) { |
|
|
|
@ -1131,19 +1116,8 @@ void processor_t::register_insn(insn_desc_t desc, bool is_custom) { |
|
|
|
|
|
|
|
void processor_t::build_opcode_map() |
|
|
|
{ |
|
|
|
struct cmp { |
|
|
|
bool operator()(const insn_desc_t& lhs, const insn_desc_t& rhs) { |
|
|
|
if (lhs.match == rhs.match) |
|
|
|
return lhs.mask > rhs.mask; |
|
|
|
return lhs.match > rhs.match; |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
std::sort(instructions.begin(), instructions.end(), cmp()); |
|
|
|
std::sort(custom_instructions.begin(), custom_instructions.end(), cmp()); |
|
|
|
|
|
|
|
for (size_t i = 0; i < OPCODE_CACHE_SIZE; i++) |
|
|
|
opcode_cache[i] = insn_desc_t::illegal(); |
|
|
|
opcode_cache[i].reset(); |
|
|
|
} |
|
|
|
|
|
|
|
void processor_t::register_extension(extension_t *x) { |
|
|
|
@ -1165,15 +1139,12 @@ void processor_t::register_base_instructions() |
|
|
|
{ |
|
|
|
#define DECLARE_INSN(name, match, mask) \ |
|
|
|
insn_bits_t name##_match = (match), name##_mask = (mask); \ |
|
|
|
bool name##_supported = true; |
|
|
|
isa_extension_t name##_ext = NUM_ISA_EXTENSIONS; \ |
|
|
|
bool name##_overlapping = false; |
|
|
|
|
|
|
|
#include "encoding.h" |
|
|
|
#undef DECLARE_INSN |
|
|
|
|
|
|
|
#define DECLARE_OVERLAP_INSN(name, ext) { name##_supported = isa->extension_enabled(ext); } |
|
|
|
#include "overlap_list.h" |
|
|
|
#undef DECLARE_OVERLAP_INSN |
|
|
|
|
|
|
|
#define DEFINE_INSN(name) \ |
|
|
|
extern reg_t fast_rv32i_##name(processor_t*, insn_t, reg_t); \ |
|
|
|
extern reg_t fast_rv64i_##name(processor_t*, insn_t, reg_t); \ |
|
|
|
@ -1182,8 +1153,14 @@ void processor_t::register_base_instructions() |
|
|
|
extern reg_t logged_rv32i_##name(processor_t*, insn_t, reg_t); \ |
|
|
|
extern reg_t logged_rv64i_##name(processor_t*, insn_t, reg_t); \ |
|
|
|
extern reg_t logged_rv32e_##name(processor_t*, insn_t, reg_t); \ |
|
|
|
extern reg_t logged_rv64e_##name(processor_t*, insn_t, reg_t); \ |
|
|
|
if (name##_supported) { \ |
|
|
|
extern reg_t logged_rv64e_##name(processor_t*, insn_t, reg_t); |
|
|
|
#include "insn_list.h" |
|
|
|
#undef DEFINE_INSN |
|
|
|
|
|
|
|
// add overlapping instructions first, in order
|
|
|
|
#define DECLARE_OVERLAP_INSN(name, ext) \ |
|
|
|
name##_overlapping = true; \ |
|
|
|
if (isa->extension_enabled(ext)) \ |
|
|
|
register_base_insn((insn_desc_t) { \ |
|
|
|
name##_match, \ |
|
|
|
name##_mask, \ |
|
|
|
@ -1194,11 +1171,31 @@ void processor_t::register_base_instructions() |
|
|
|
logged_rv32i_##name, \ |
|
|
|
logged_rv64i_##name, \ |
|
|
|
logged_rv32e_##name, \ |
|
|
|
logged_rv64e_##name}); \ |
|
|
|
} |
|
|
|
logged_rv64e_##name}); |
|
|
|
#include "overlap_list.h" |
|
|
|
#undef DECLARE_OVERLAP_INSN |
|
|
|
|
|
|
|
// add all other instructions. since they are non-overlapping, the order
|
|
|
|
// does not affect correctness, but more frequent instructions should
|
|
|
|
// appear earlier to improve search time on opcode_cache misses.
|
|
|
|
#define DEFINE_INSN(name) \ |
|
|
|
if (!name##_overlapping) \ |
|
|
|
register_base_insn((insn_desc_t) { \ |
|
|
|
name##_match, \ |
|
|
|
name##_mask, \ |
|
|
|
fast_rv32i_##name, \ |
|
|
|
fast_rv64i_##name, \ |
|
|
|
fast_rv32e_##name, \ |
|
|
|
fast_rv64e_##name, \ |
|
|
|
logged_rv32i_##name, \ |
|
|
|
logged_rv64i_##name, \ |
|
|
|
logged_rv32e_##name, \ |
|
|
|
logged_rv64e_##name}); |
|
|
|
#include "insn_list.h" |
|
|
|
#undef DEFINE_INSN |
|
|
|
|
|
|
|
// terminate instruction list with a catch-all
|
|
|
|
register_base_insn(insn_desc_t::illegal()); |
|
|
|
register_base_insn(insn_desc_t::illegal_instruction); |
|
|
|
|
|
|
|
build_opcode_map(); |
|
|
|
} |
|
|
|
|