+// SPDX-License-Identifier: LGPL-2.1-or-later
/*
- * Copyright (C) 2015-2020 Paul Cercueil <paul@crapouillou.net>
- *
- * 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 <paul@crapouillou.net>
*/
#include "blockcache.h"
#include <stdbool.h>
#include <stdlib.h>
+#include <string.h>
/* Must be power of two */
#define LUT_SIZE 0x4000
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;
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)
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);
}
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++;
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);
/* 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;