libretro: adjust psxclock description
[pcsx_rearmed.git] / deps / lightrec / blockcache.c
index 2182f29..d72d64e 100644 (file)
@@ -7,6 +7,8 @@
 #include "debug.h"
 #include "lightrec-private.h"
 #include "memmanager.h"
+#include "reaper.h"
+#include "recompiler.h"
 
 #include <stdbool.h>
 #include <stdlib.h>
@@ -99,21 +101,67 @@ void lightrec_unregister_block(struct blockcache *cache, struct block *block)
                }
        }
 
-       pr_err("Block at PC 0x%x is not in cache\n", block->pc);
+       pr_err("Block at "PC_FMT" is not in cache\n", block->pc);
 }
 
-void lightrec_free_block_cache(struct blockcache *cache)
+static bool lightrec_block_is_old(const struct lightrec_state *state,
+                                 const struct block *block)
+{
+       u32 diff = state->current_cycle - block->precompile_date;
+
+       return diff > (1 << 27); /* About 4 seconds */
+}
+
+static void lightrec_free_blocks(struct blockcache *cache,
+                                const struct block *except, bool all)
 {
+       struct lightrec_state *state = cache->state;
        struct block *block, *next;
+       bool outdated = all;
        unsigned int i;
+       u8 old_flags;
 
        for (i = 0; i < LUT_SIZE; i++) {
                for (block = cache->lut[i]; block; block = next) {
                        next = block->next;
-                       lightrec_free_block(cache->state, block);
+
+                       if (except && block == except)
+                               continue;
+
+                       if (!all) {
+                               outdated = lightrec_block_is_old(state, block) ||
+                                       lightrec_block_is_outdated(state, block);
+                       }
+
+                       if (!outdated)
+                               continue;
+
+                       old_flags = block_set_flags(block, BLOCK_IS_DEAD);
+
+                       if (!(old_flags & BLOCK_IS_DEAD)) {
+                               if (ENABLE_THREADED_COMPILER)
+                                       lightrec_recompiler_remove(state->rec, block);
+
+                               pr_debug("Freeing outdated block at "PC_FMT"\n", block->pc);
+                               remove_from_code_lut(cache, block);
+                               lightrec_unregister_block(cache, block);
+                               lightrec_free_block(state, block);
+                       }
                }
        }
+}
+
+void lightrec_remove_outdated_blocks(struct blockcache *cache,
+                                    const struct block *except)
+{
+       pr_info("Running out of code space. Cleaning block cache...\n");
 
+       lightrec_free_blocks(cache, except, false);
+}
+
+void lightrec_free_block_cache(struct blockcache *cache)
+{
+       lightrec_free_blocks(cache, NULL, true);
        lightrec_free(cache->state, MEM_FOR_LIGHTREC, sizeof(*cache), cache);
 }
 
@@ -150,11 +198,27 @@ u32 lightrec_calculate_block_hash(const struct block *block)
        return hash;
 }
 
+static void lightrec_reset_lut_offset(struct lightrec_state *state, void *d)
+{
+       u32 pc = (u32)(uintptr_t) d;
+       struct block *block;
+       void *addr;
+
+       block = lightrec_find_block(state->block_cache, pc);
+       if (!block)
+               return;
+
+       if (block_has_flag(block, BLOCK_IS_DEAD))
+               return;
+
+       addr = block->function ?: state->get_next_block;
+       lut_write(state, lut_offset(pc), addr);
+}
+
 bool lightrec_block_is_outdated(struct lightrec_state *state, struct block *block)
 {
        u32 offset = lut_offset(block->pc);
        bool outdated;
-       void *addr;
 
        if (lut_read(state, offset))
                return false;
@@ -163,12 +227,24 @@ bool lightrec_block_is_outdated(struct lightrec_state *state, struct block *bloc
        if (likely(!outdated)) {
                /* The block was marked as outdated, but the content is still
                 * the same */
-               if (block->function)
-                       addr = block->function;
-               else
-                       addr = state->get_next_block;
 
-               lut_write(state, offset, addr);
+               if (ENABLE_THREADED_COMPILER) {
+                       /*
+                        * When compiling a block that covers ours, the threaded
+                        * compiler will set the LUT entries of the various
+                        * entry points. Therefore we cannot write the LUT here,
+                        * as we would risk overwriting the new entry points.
+                        * Leave it to the reaper to re-install the LUT entries.
+                        */
+
+                       lightrec_reaper_add(state->reaper,
+                                           lightrec_reset_lut_offset,
+                                           (void *)(uintptr_t) block->pc);
+               } else if (block->function) {
+                       lut_write(state, offset, block->function);
+               } else {
+                       lut_write(state, offset, state->get_next_block);
+               }
        }
 
        return outdated;