git subrepo pull --force deps/lightrec
[pcsx_rearmed.git] / deps / lightrec / blockcache.c
CommitLineData
98fa08a5 1// SPDX-License-Identifier: LGPL-2.1-or-later
d16005f8 2/*
98fa08a5 3 * Copyright (C) 2015-2021 Paul Cercueil <paul@crapouillou.net>
d16005f8
PC
4 */
5
6#include "blockcache.h"
7#include "debug.h"
8#include "lightrec-private.h"
9#include "memmanager.h"
10
11#include <stdbool.h>
12#include <stdlib.h>
98fa08a5 13#include <string.h>
d16005f8
PC
14
15/* Must be power of two */
16#define LUT_SIZE 0x4000
17
18struct blockcache {
19 struct lightrec_state *state;
20 struct block * lut[LUT_SIZE];
21};
22
98fa08a5
PC
23u16 lightrec_get_lut_entry(const struct block *block)
24{
25 return (kunseg(block->pc) >> 2) & (LUT_SIZE - 1);
26}
27
d16005f8
PC
28struct block * lightrec_find_block(struct blockcache *cache, u32 pc)
29{
30 struct block *block;
31
32 pc = kunseg(pc);
33
34 for (block = cache->lut[(pc >> 2) & (LUT_SIZE - 1)];
35 block; block = block->next)
36 if (kunseg(block->pc) == pc)
37 return block;
38
39 return NULL;
40}
41
98fa08a5
PC
42struct block * lightrec_find_block_from_lut(struct blockcache *cache,
43 u16 lut_entry, u32 addr_in_block)
d16005f8 44{
98fa08a5
PC
45 struct block *block;
46 u32 pc;
d16005f8 47
98fa08a5 48 addr_in_block = kunseg(addr_in_block);
d16005f8 49
98fa08a5
PC
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))
54 return block;
55 }
d16005f8 56
98fa08a5
PC
57 return NULL;
58}
59
60void remove_from_code_lut(struct blockcache *cache, struct block *block)
61{
62 struct lightrec_state *state = cache->state;
63 u32 offset = lut_offset(block->pc);
64
65 if (block->function) {
02487de7
PC
66 memset(lut_address(state, offset), 0,
67 block->nb_ops * lut_elm_size(state));
98fa08a5 68 }
d16005f8
PC
69}
70
d16005f8
PC
71void lightrec_register_block(struct blockcache *cache, struct block *block)
72{
73 u32 pc = kunseg(block->pc);
74 struct block *old;
75
76 old = cache->lut[(pc >> 2) & (LUT_SIZE - 1)];
77 if (old)
78 block->next = old;
79
80 cache->lut[(pc >> 2) & (LUT_SIZE - 1)] = block;
81
82 remove_from_code_lut(cache, block);
83}
84
85void lightrec_unregister_block(struct blockcache *cache, struct block *block)
86{
87 u32 pc = kunseg(block->pc);
88 struct block *old = cache->lut[(pc >> 2) & (LUT_SIZE - 1)];
89
d16005f8
PC
90 if (old == block) {
91 cache->lut[(pc >> 2) & (LUT_SIZE - 1)] = old->next;
92 return;
93 }
94
95 for (; old; old = old->next) {
96 if (old->next == block) {
97 old->next = block->next;
98 return;
99 }
100 }
101
102 pr_err("Block at PC 0x%x is not in cache\n", block->pc);
103}
104
105void lightrec_free_block_cache(struct blockcache *cache)
106{
107 struct block *block, *next;
108 unsigned int i;
109
110 for (i = 0; i < LUT_SIZE; i++) {
111 for (block = cache->lut[i]; block; block = next) {
112 next = block->next;
98fa08a5 113 lightrec_free_block(cache->state, block);
d16005f8
PC
114 }
115 }
116
117 lightrec_free(cache->state, MEM_FOR_LIGHTREC, sizeof(*cache), cache);
118}
119
120struct blockcache * lightrec_blockcache_init(struct lightrec_state *state)
121{
122 struct blockcache *cache;
123
124 cache = lightrec_calloc(state, MEM_FOR_LIGHTREC, sizeof(*cache));
125 if (!cache)
126 return NULL;
127
128 cache->state = state;
129
130 return cache;
131}
132
133u32 lightrec_calculate_block_hash(const struct block *block)
134{
98fa08a5
PC
135 const u32 *code = block->code;
136 u32 hash = 0xffffffff;
d16005f8
PC
137 unsigned int i;
138
d16005f8
PC
139 /* Jenkins one-at-a-time hash algorithm */
140 for (i = 0; i < block->nb_ops; i++) {
141 hash += *code++;
142 hash += (hash << 10);
143 hash ^= (hash >> 6);
144 }
145
146 hash += (hash << 3);
147 hash ^= (hash >> 11);
148 hash += (hash << 15);
149
150 return hash;
151}
152
98fa08a5 153bool lightrec_block_is_outdated(struct lightrec_state *state, struct block *block)
d16005f8 154{
02487de7 155 u32 offset = lut_offset(block->pc);
d16005f8 156 bool outdated;
02487de7 157 void *addr;
d16005f8 158
02487de7 159 if (lut_read(state, offset))
d16005f8
PC
160 return false;
161
162 outdated = block->hash != lightrec_calculate_block_hash(block);
163 if (likely(!outdated)) {
164 /* The block was marked as outdated, but the content is still
165 * the same */
166 if (block->function)
02487de7 167 addr = block->function;
d16005f8 168 else
02487de7
PC
169 addr = state->get_next_block;
170
171 lut_write(state, offset, addr);
d16005f8
PC
172 }
173
174 return outdated;
175}