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"
22 #include <stdatomic.h>
29 struct slist_elm slist;
33 struct lightrec_state *state;
36 pthread_mutex_t mutex;
38 struct block *current_block;
39 struct slist_elm slist;
42 static void lightrec_compile_list(struct recompiler *rec)
44 struct block_rec *block_rec;
45 struct slist_elm *next;
49 while (!!(next = slist_first(&rec->slist))) {
50 block_rec = container_of(next, struct block_rec, slist);
51 block = block_rec->block;
52 rec->current_block = block;
54 pthread_mutex_unlock(&rec->mutex);
56 ret = lightrec_compile_block(block);
58 pr_err("Unable to compile block at PC 0x%x: %d\n",
62 pthread_mutex_lock(&rec->mutex);
64 slist_remove(&rec->slist, next);
65 lightrec_free(rec->state, MEM_FOR_LIGHTREC,
66 sizeof(*block_rec), block_rec);
67 pthread_cond_signal(&rec->cond);
70 rec->current_block = NULL;
73 static void * lightrec_recompiler_thd(void *d)
75 struct recompiler *rec = d;
77 pthread_mutex_lock(&rec->mutex);
81 pthread_cond_wait(&rec->cond, &rec->mutex);
86 } while (slist_empty(&rec->slist));
88 lightrec_compile_list(rec);
92 pthread_mutex_unlock(&rec->mutex);
96 struct recompiler *lightrec_recompiler_init(struct lightrec_state *state)
98 struct recompiler *rec;
101 rec = lightrec_malloc(state, MEM_FOR_LIGHTREC, sizeof(*rec));
103 pr_err("Cannot create recompiler: Out of memory\n");
109 rec->current_block = NULL;
110 slist_init(&rec->slist);
112 ret = pthread_cond_init(&rec->cond, NULL);
114 pr_err("Cannot init cond variable: %d\n", ret);
118 ret = pthread_mutex_init(&rec->mutex, NULL);
120 pr_err("Cannot init mutex variable: %d\n", ret);
121 goto err_cnd_destroy;
124 ret = pthread_create(&rec->thd, NULL, lightrec_recompiler_thd, rec);
126 pr_err("Cannot create recompiler thread: %d\n", ret);
127 goto err_mtx_destroy;
133 pthread_mutex_destroy(&rec->mutex);
135 pthread_cond_destroy(&rec->cond);
137 lightrec_free(state, MEM_FOR_LIGHTREC, sizeof(*rec), rec);
141 void lightrec_free_recompiler(struct recompiler *rec)
145 /* Stop the thread */
146 pthread_mutex_lock(&rec->mutex);
147 pthread_cond_signal(&rec->cond);
148 pthread_mutex_unlock(&rec->mutex);
149 pthread_join(rec->thd, NULL);
151 pthread_mutex_destroy(&rec->mutex);
152 pthread_cond_destroy(&rec->cond);
153 lightrec_free(rec->state, MEM_FOR_LIGHTREC, sizeof(*rec), rec);
156 int lightrec_recompiler_add(struct recompiler *rec, struct block *block)
158 struct slist_elm *elm, *prev;
159 struct block_rec *block_rec;
162 pthread_mutex_lock(&rec->mutex);
164 /* If the block is marked as dead, don't compile it, it will be removed
165 * as soon as it's safe. */
166 if (block->flags & BLOCK_IS_DEAD)
169 for (elm = slist_first(&rec->slist), prev = NULL; elm;
170 prev = elm, elm = elm->next) {
171 block_rec = container_of(elm, struct block_rec, slist);
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, unless the block is being
177 if (prev && !(block->flags & BLOCK_SHOULD_RECOMPILE)) {
178 slist_remove_next(prev);
179 slist_append(&rec->slist, elm);
186 /* By the time this function was called, the block has been recompiled
187 * and ins't in the wait list anymore. Just return here. */
188 if (block->function && !(block->flags & BLOCK_SHOULD_RECOMPILE))
191 block_rec = lightrec_malloc(rec->state, MEM_FOR_LIGHTREC,
198 pr_debug("Adding block PC 0x%x to recompiler\n", block->pc);
200 block_rec->block = block;
204 /* If the block is being recompiled, push it to the end of the queue;
205 * otherwise push it to the front of the queue. */
206 if (block->flags & BLOCK_SHOULD_RECOMPILE)
207 for (; elm->next; elm = elm->next);
209 slist_append(elm, &block_rec->slist);
211 /* Signal the thread */
212 pthread_cond_signal(&rec->cond);
215 pthread_mutex_unlock(&rec->mutex);
219 void lightrec_recompiler_remove(struct recompiler *rec, struct block *block)
221 struct block_rec *block_rec;
222 struct slist_elm *elm;
224 pthread_mutex_lock(&rec->mutex);
226 for (elm = slist_first(&rec->slist); elm; elm = elm->next) {
227 block_rec = container_of(elm, struct block_rec, slist);
229 if (block_rec->block == block) {
230 if (block == rec->current_block) {
231 /* Block is being recompiled - wait for
234 pthread_cond_wait(&rec->cond,
236 } while (block == rec->current_block);
238 /* Block is not yet being processed - remove it
240 slist_remove(&rec->slist, elm);
241 lightrec_free(rec->state, MEM_FOR_LIGHTREC,
242 sizeof(*block_rec), block_rec);
249 pthread_mutex_unlock(&rec->mutex);
252 void * lightrec_recompiler_run_first_pass(struct block *block, u32 *pc)
256 if (likely(block->function)) {
257 if (block->flags & BLOCK_FULLY_TAGGED) {
258 freed = atomic_flag_test_and_set(&block->op_list_freed);
261 pr_debug("Block PC 0x%08x is fully tagged"
262 " - free opcode list\n", block->pc);
264 /* The block was already compiled but the opcode list
265 * didn't get freed yet - do it now */
266 lightrec_free_opcode_list(block->state,
268 block->opcode_list = NULL;
272 return block->function;
275 /* Mark the opcode list as freed, so that the threaded compiler won't
276 * free it while we're using it in the interpreter. */
277 freed = atomic_flag_test_and_set(&block->op_list_freed);
279 /* Block wasn't compiled yet - run the interpreter */
280 *pc = lightrec_emulate_block(block, *pc);
283 atomic_flag_clear(&block->op_list_freed);
285 /* The block got compiled while the interpreter was running.
286 * We can free the opcode list now. */
287 if (block->function && (block->flags & BLOCK_FULLY_TAGGED) &&
288 !atomic_flag_test_and_set(&block->op_list_freed)) {
289 pr_debug("Block PC 0x%08x is fully tagged"
290 " - free opcode list\n", block->pc);
292 lightrec_free_opcode_list(block->state, block->opcode_list);
293 block->opcode_list = NULL;