X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=deps%2Flightrec%2Fblockcache.c;h=70c5aebee4edac2a58a936968ba09373a46800c9;hb=0b8caa417c2bef660d6a0c70cbab71f1e997023b;hp=4263431d418074cbc464bc27372ae51305f64f53;hpb=67b91c4b9c44f0d973d2d6a85481e3f437ccc583;p=pcsx_rearmed.git diff --git a/deps/lightrec/blockcache.c b/deps/lightrec/blockcache.c index 4263431d..70c5aebe 100644 --- a/deps/lightrec/blockcache.c +++ b/deps/lightrec/blockcache.c @@ -1,15 +1,6 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later /* - * Copyright (C) 2015-2020 Paul Cercueil - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. + * Copyright (C) 2015-2021 Paul Cercueil */ #include "blockcache.h" @@ -19,6 +10,7 @@ #include #include +#include /* Must be power of two */ #define LUT_SIZE 0x4000 @@ -28,6 +20,11 @@ struct blockcache { struct block * lut[LUT_SIZE]; }; +u16 lightrec_get_lut_entry(const struct block *block) +{ + return (kunseg(block->pc) >> 2) & (LUT_SIZE - 1); +} + struct block * lightrec_find_block(struct blockcache *cache, u32 pc) { struct block *block; @@ -42,22 +39,33 @@ struct block * lightrec_find_block(struct blockcache *cache, u32 pc) return NULL; } -void remove_from_code_lut(struct blockcache *cache, struct block *block) +struct block * lightrec_find_block_from_lut(struct blockcache *cache, + u16 lut_entry, u32 addr_in_block) { - struct lightrec_state *state = block->state; - const struct opcode *op; - u32 offset = lut_offset(block->pc); + struct block *block; + u32 pc; + + addr_in_block = kunseg(addr_in_block); + + for (block = cache->lut[lut_entry]; block; block = block->next) { + pc = kunseg(block->pc); + if (addr_in_block >= pc && + addr_in_block < pc + (block->nb_ops << 2)) + return block; + } - /* Use state->get_next_block in the code LUT, which basically - * calls back get_next_block_func(), until the compiler - * overrides this. This is required, as a NULL value in the code - * LUT means an outdated block. */ - state->code_lut[offset] = state->get_next_block; + return NULL; +} - for (op = block->opcode_list; op; op = op->next) - if (op->c.i.op == OP_META_SYNC) - state->code_lut[offset + op->offset] = NULL; +void remove_from_code_lut(struct blockcache *cache, struct block *block) +{ + struct lightrec_state *state = cache->state; + u32 offset = lut_offset(block->pc); + if (block->function) { + memset(lut_address(state, offset), 0, + block->nb_ops * lut_elm_size(state)); + } } void lightrec_register_block(struct blockcache *cache, struct block *block) @@ -94,18 +102,55 @@ void lightrec_unregister_block(struct blockcache *cache, struct block *block) pr_err("Block at PC 0x%x 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; for (i = 0; i < LUT_SIZE; i++) { for (block = cache->lut[i]; block; block = next) { next = block->next; - lightrec_free_block(block); + + if (except && block == except) + continue; + + if (!all) { + outdated = lightrec_block_is_old(state, block) || + lightrec_block_is_outdated(state, block); + } + + if (outdated) { + pr_debug("Freeing outdated block at PC 0x%08x\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); } @@ -124,18 +169,10 @@ struct blockcache * lightrec_blockcache_init(struct lightrec_state *state) u32 lightrec_calculate_block_hash(const struct block *block) { - const struct lightrec_mem_map *map = block->map; - u32 pc, hash = 0xffffffff; - const u32 *code; + const u32 *code = block->code; + u32 hash = 0xffffffff; unsigned int i; - pc = kunseg(block->pc) - map->pc; - - while (map->mirror_of) - map = map->mirror_of; - - code = map->address + pc; - /* Jenkins one-at-a-time hash algorithm */ for (i = 0; i < block->nb_ops; i++) { hash += *code++; @@ -150,12 +187,13 @@ u32 lightrec_calculate_block_hash(const struct block *block) return hash; } -bool lightrec_block_is_outdated(struct block *block) +bool lightrec_block_is_outdated(struct lightrec_state *state, struct block *block) { - void **lut_entry = &block->state->code_lut[lut_offset(block->pc)]; + u32 offset = lut_offset(block->pc); bool outdated; + void *addr; - if (*lut_entry) + if (lut_read(state, offset)) return false; outdated = block->hash != lightrec_calculate_block_hash(block); @@ -163,9 +201,11 @@ bool lightrec_block_is_outdated(struct block *block) /* The block was marked as outdated, but the content is still * the same */ if (block->function) - *lut_entry = block->function; + addr = block->function; else - *lut_entry = block->state->get_next_block; + addr = state->get_next_block; + + lut_write(state, offset, addr); } return outdated;