2 * Copyright (C) 2019-2020 Paul Cercueil <paul@crapouillou.net>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
16 #include "interpreter.h"
17 #include "lightrec-private.h"
18 #include "memmanager.h"
21 #include <stdatomic.h>
28 struct block_rec *next;
32 struct lightrec_state *state;
35 pthread_mutex_t mutex;
37 struct block *current_block;
38 struct block_rec *list;
41 static void slist_remove(struct recompiler *rec, struct block_rec *elm)
43 struct block_rec *prev;
45 if (rec->list == elm) {
46 rec->list = elm->next;
48 for (prev = rec->list; prev && prev->next != elm; )
51 prev->next = elm->next;
55 static void lightrec_compile_list(struct recompiler *rec)
57 struct block_rec *next;
61 while (!!(next = rec->list)) {
63 rec->current_block = block;
65 pthread_mutex_unlock(&rec->mutex);
67 ret = lightrec_compile_block(block);
69 pr_err("Unable to compile block at PC 0x%x: %d\n",
73 pthread_mutex_lock(&rec->mutex);
75 slist_remove(rec, next);
76 lightrec_free(rec->state, MEM_FOR_LIGHTREC,
78 pthread_cond_signal(&rec->cond);
81 rec->current_block = NULL;
84 static void * lightrec_recompiler_thd(void *d)
86 struct recompiler *rec = d;
88 pthread_mutex_lock(&rec->mutex);
92 pthread_cond_wait(&rec->cond, &rec->mutex);
95 pthread_mutex_unlock(&rec->mutex);
101 lightrec_compile_list(rec);
105 struct recompiler *lightrec_recompiler_init(struct lightrec_state *state)
107 struct recompiler *rec;
110 rec = lightrec_malloc(state, MEM_FOR_LIGHTREC, sizeof(*rec));
112 pr_err("Cannot create recompiler: Out of memory\n");
118 rec->current_block = NULL;
121 ret = pthread_cond_init(&rec->cond, NULL);
123 pr_err("Cannot init cond variable: %d\n", ret);
127 ret = pthread_mutex_init(&rec->mutex, NULL);
129 pr_err("Cannot init mutex variable: %d\n", ret);
130 goto err_cnd_destroy;
133 ret = pthread_create(&rec->thd, NULL, lightrec_recompiler_thd, rec);
135 pr_err("Cannot create recompiler thread: %d\n", ret);
136 goto err_mtx_destroy;
142 pthread_mutex_destroy(&rec->mutex);
144 pthread_cond_destroy(&rec->cond);
146 lightrec_free(state, MEM_FOR_LIGHTREC, sizeof(*rec), rec);
150 void lightrec_free_recompiler(struct recompiler *rec)
154 /* Stop the thread */
155 pthread_mutex_lock(&rec->mutex);
156 pthread_cond_signal(&rec->cond);
157 pthread_mutex_unlock(&rec->mutex);
158 pthread_join(rec->thd, NULL);
160 pthread_mutex_destroy(&rec->mutex);
161 pthread_cond_destroy(&rec->cond);
162 lightrec_free(rec->state, MEM_FOR_LIGHTREC, sizeof(*rec), rec);
165 int lightrec_recompiler_add(struct recompiler *rec, struct block *block)
167 struct block_rec *block_rec, *prev;
169 pthread_mutex_lock(&rec->mutex);
171 for (block_rec = rec->list, prev = NULL; block_rec;
172 prev = block_rec, block_rec = block_rec->next) {
173 if (block_rec->block == block) {
174 /* The block to compile is already in the queue - bump
175 * it to the top of the list */
177 prev->next = block_rec->next;
178 block_rec->next = rec->list;
179 rec->list = block_rec;
182 pthread_mutex_unlock(&rec->mutex);
187 /* By the time this function was called, the block has been recompiled
188 * and ins't in the wait list anymore. Just return here. */
189 if (block->function) {
190 pthread_mutex_unlock(&rec->mutex);
194 block_rec = lightrec_malloc(rec->state, MEM_FOR_LIGHTREC,
197 pthread_mutex_unlock(&rec->mutex);
201 pr_debug("Adding block PC 0x%x to recompiler\n", block->pc);
203 block_rec->block = block;
204 block_rec->next = rec->list;
205 rec->list = block_rec;
207 /* Signal the thread */
208 pthread_cond_signal(&rec->cond);
209 pthread_mutex_unlock(&rec->mutex);
214 void lightrec_recompiler_remove(struct recompiler *rec, struct block *block)
216 struct block_rec *block_rec;
218 pthread_mutex_lock(&rec->mutex);
220 for (block_rec = rec->list; block_rec; block_rec = block_rec->next) {
221 if (block_rec->block == block) {
222 if (block == rec->current_block) {
223 /* Block is being recompiled - wait for
226 pthread_cond_wait(&rec->cond,
228 } while (block == rec->current_block);
230 /* Block is not yet being processed - remove it
232 slist_remove(rec, block_rec);
233 lightrec_free(rec->state, MEM_FOR_LIGHTREC,
234 sizeof(*block_rec), block_rec);
241 pthread_mutex_unlock(&rec->mutex);
244 void * lightrec_recompiler_run_first_pass(struct block *block, u32 *pc)
248 if (likely(block->function)) {
249 if (block->flags & BLOCK_FULLY_TAGGED) {
250 freed = atomic_flag_test_and_set(&block->op_list_freed);
253 pr_debug("Block PC 0x%08x is fully tagged"
254 " - free opcode list\n", block->pc);
256 /* The block was already compiled but the opcode list
257 * didn't get freed yet - do it now */
258 lightrec_free_opcode_list(block->state,
260 block->opcode_list = NULL;
264 return block->function;
267 /* Mark the opcode list as freed, so that the threaded compiler won't
268 * free it while we're using it in the interpreter. */
269 freed = atomic_flag_test_and_set(&block->op_list_freed);
271 /* Block wasn't compiled yet - run the interpreter */
272 *pc = lightrec_emulate_block(block, *pc);
275 atomic_flag_clear(&block->op_list_freed);
277 /* The block got compiled while the interpreter was running.
278 * We can free the opcode list now. */
279 if (block->function && (block->flags & BLOCK_FULLY_TAGGED) &&
280 !atomic_flag_test_and_set(&block->op_list_freed)) {
281 pr_debug("Block PC 0x%08x is fully tagged"
282 " - free opcode list\n", block->pc);
284 lightrec_free_opcode_list(block->state, block->opcode_list);
285 block->opcode_list = NULL;