1 // SPDX-License-Identifier: LGPL-2.1-or-later
3 * Copyright (C) 2015-2021 Paul Cercueil <paul@crapouillou.net>
6 #include "blockcache.h"
8 #include "lightrec-private.h"
9 #include "memmanager.h"
15 /* Must be power of two */
16 #define LUT_SIZE 0x4000
19 struct lightrec_state *state;
20 struct block * lut[LUT_SIZE];
23 u16 lightrec_get_lut_entry(const struct block *block)
25 return (kunseg(block->pc) >> 2) & (LUT_SIZE - 1);
28 struct block * lightrec_find_block(struct blockcache *cache, u32 pc)
34 for (block = cache->lut[(pc >> 2) & (LUT_SIZE - 1)];
35 block; block = block->next)
36 if (kunseg(block->pc) == pc)
42 struct block * lightrec_find_block_from_lut(struct blockcache *cache,
43 u16 lut_entry, u32 addr_in_block)
48 addr_in_block = kunseg(addr_in_block);
50 for (block = cache->lut[lut_entry]; block; block = block->next) {
51 pc = kunseg(block->pc);
52 if (addr_in_block >= pc &&
53 addr_in_block < pc + (block->nb_ops << 2))
60 void remove_from_code_lut(struct blockcache *cache, struct block *block)
62 struct lightrec_state *state = cache->state;
63 u32 offset = lut_offset(block->pc);
65 if (block->function) {
66 memset(&state->code_lut[offset], 0,
67 block->nb_ops * sizeof(*state->code_lut));
71 void lightrec_register_block(struct blockcache *cache, struct block *block)
73 u32 pc = kunseg(block->pc);
76 old = cache->lut[(pc >> 2) & (LUT_SIZE - 1)];
80 cache->lut[(pc >> 2) & (LUT_SIZE - 1)] = block;
82 remove_from_code_lut(cache, block);
85 void lightrec_unregister_block(struct blockcache *cache, struct block *block)
87 u32 pc = kunseg(block->pc);
88 struct block *old = cache->lut[(pc >> 2) & (LUT_SIZE - 1)];
91 cache->lut[(pc >> 2) & (LUT_SIZE - 1)] = old->next;
95 for (; old; old = old->next) {
96 if (old->next == block) {
97 old->next = block->next;
102 pr_err("Block at PC 0x%x is not in cache\n", block->pc);
105 void lightrec_free_block_cache(struct blockcache *cache)
107 struct block *block, *next;
110 for (i = 0; i < LUT_SIZE; i++) {
111 for (block = cache->lut[i]; block; block = next) {
113 lightrec_free_block(cache->state, block);
117 lightrec_free(cache->state, MEM_FOR_LIGHTREC, sizeof(*cache), cache);
120 struct blockcache * lightrec_blockcache_init(struct lightrec_state *state)
122 struct blockcache *cache;
124 cache = lightrec_calloc(state, MEM_FOR_LIGHTREC, sizeof(*cache));
128 cache->state = state;
133 u32 lightrec_calculate_block_hash(const struct block *block)
135 const u32 *code = block->code;
136 u32 hash = 0xffffffff;
139 /* Jenkins one-at-a-time hash algorithm */
140 for (i = 0; i < block->nb_ops; i++) {
142 hash += (hash << 10);
147 hash ^= (hash >> 11);
148 hash += (hash << 15);
153 bool lightrec_block_is_outdated(struct lightrec_state *state, struct block *block)
155 void **lut_entry = &state->code_lut[lut_offset(block->pc)];
161 outdated = block->hash != lightrec_calculate_block_hash(block);
162 if (likely(!outdated)) {
163 /* The block was marked as outdated, but the content is still
166 *lut_entry = block->function;
168 *lut_entry = state->get_next_block;