@@ -662,31 +662,38 @@ reg_t processor_t::throw_instruction_address_misaligned(reg_t pc)
662662
663663insn_func_t processor_t::decode_insn (insn_t insn)
664664{
665- if (!extension_enabled (EXT_ZCA) && insn_length (insn.bits ()) % 4 )
666- return &::illegal_instruction;
665+ bool rve = extension_enabled (' E' );
667666
668- // look up opcode in hash table
669- size_t idx = insn.bits () % OPCODE_CACHE_SIZE;
670- auto [hit, desc] = opcode_cache[idx].lookup (insn.bits ());
667+ if (unlikely (!extension_enabled (EXT_ZCA) && insn_length (insn.bits ()) % 4 ))
668+ return &::illegal_instruction;
671669
672- bool rve = extension_enabled (' E' );
670+ auto & oc = opcode_cache[insn.bits () % std::size (opcode_cache)];
671+ if (likely (oc[0 ].first ->matches (insn.bits ())))
672+ return oc[0 ].first ->func (xlen, rve, log_commits_enabled);
673+
674+ // linearly search the opcode cache
675+ for (size_t i = 1 ; i < oc.size (); i++) {
676+ auto desc = oc[i].first ;
677+ if (desc->matches (insn.bits ())) {
678+ oc[i].second ++;
679+
680+ // periodically sort the opcode cache in descending order of frequency
681+ if (unlikely (--opcode_cache_sort_count == 0 )) {
682+ opcode_cache_sort_count = OPCODE_CACHE_SORT_COUNT;
683+
684+ for (auto & oc : opcode_cache) {
685+ std::stable_sort (oc.begin (), oc.end (), [](auto a, auto b) {
686+ if (a.first ->overlap || b.first ->overlap )
687+ return false ;
688+ return a.second > b.second ;
689+ });
690+ }
691+ }
673692
674- if (unlikely (!hit)) {
675- // fall back to linear search
676- auto matching = [insn_bits = insn.bits ()](const insn_desc_t &d) {
677- return (insn_bits & d.mask ) == d.match ;
678- };
679- auto p = std::find_if (custom_instructions.begin (),
680- custom_instructions.end (), matching);
681- if (p == custom_instructions.end ()) {
682- p = std::find_if (instructions.begin (), instructions.end (), matching);
683- assert (p != instructions.end ());
693+ return desc->func (xlen, rve, log_commits_enabled);
684694 }
685- desc = &*p;
686- opcode_cache[idx].replace (insn.bits (), desc);
687695 }
688-
689- return desc->func (xlen, rve, log_commits_enabled);
696+ return &::illegal_instruction;
690697}
691698
692699void processor_t::register_insn (insn_desc_t desc, bool is_custom) {
@@ -701,8 +708,25 @@ void processor_t::register_insn(insn_desc_t desc, bool is_custom) {
701708
702709void processor_t::build_opcode_map ()
703710{
704- for (size_t i = 0 ; i < OPCODE_CACHE_SIZE; i++)
705- opcode_cache[i].reset ();
711+ const insn_bits_t N = std::size (opcode_cache);
712+
713+ auto insert = [&](auto & insn) {
714+ auto stride = std::min (N, insn_bits_t (1 ) << ctz (~insn.mask ));
715+
716+ for (size_t i = insn.match & (stride - 1 ); i < N; i += stride)
717+ if ((insn.match % N) == (i & insn.mask ))
718+ opcode_cache[i].push_back ({&insn, 0 });
719+ };
720+
721+ for (size_t i = 0 ; i < N; i++)
722+ opcode_cache[i].clear ();
723+
724+ std::for_each (custom_instructions.begin (), custom_instructions.end (), insert);
725+ std::for_each (instructions.begin (), instructions.end (), insert);
726+
727+ for (size_t i = 0 ; i < N; i++)
728+ if (opcode_cache[i].empty ())
729+ opcode_cache[i].push_back ({&insn_desc_t ::illegal_instruction, 0 });
706730}
707731
708732void processor_t::register_extension (extension_t *x) {
@@ -752,7 +776,8 @@ void processor_t::register_base_instructions()
752776 logged_rv32i_##name, \
753777 logged_rv64i_##name, \
754778 logged_rv32e_##name, \
755- logged_rv64e_##name \
779+ logged_rv64e_##name, \
780+ name##_overlapping \
756781 }; \
757782 register_base_insn (insn); \
758783 }
@@ -775,9 +800,6 @@ void processor_t::register_base_instructions()
775800 #undef DEFINE_INSN
776801 #undef DEFINE_INSN_UNCOND
777802
778- // terminate instruction list with a catch-all
779- register_base_insn (insn_desc_t ::illegal_instruction);
780-
781803 build_opcode_map ();
782804}
783805
0 commit comments