Skip to content

Commit 6b462c0

Browse files
committed
Introduce background compilation
Given the significant runtime compilation overhead associated with performing aggressive optimizations, we have implemented a background compilation mechanism to mitigate this issue. When the runtime profiler identifies a strong hotspot, it adds a T2C compilation request to the wait queue. A background thread, which continuously monitors this queue, triggers T2C to process the requests and notifies the main thread upon completion by updating a flag. This mechanism defers the execution of T2C-generated machine code, leading to more frequent use of T1C-generated code. Despite this, the approach effectively minimizes runtime compilation delays by eliminating the need for the main thread to wait for T2C compilation to complete, thereby improving overall performance. Close: #239
1 parent 892bc37 commit 6b462c0

File tree

5 files changed

+75
-11
lines changed

5 files changed

+75
-11
lines changed

.github/workflows/main.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ jobs:
7575
- name: undefined behavior test
7676
run: |
7777
make clean && make ENABLE_UBSAN=1 check -j$(nproc)
78-
make ENABLE_JIT=1 clean clean && make ENABLE_JIT=1 ENABLE_UBSAN=1 check -j$(nproc)
78+
make ENABLE_JIT=1 clean && make ENABLE_JIT=1 ENABLE_UBSAN=1 check -j$(nproc)
7979
8080
host-arm64:
8181
needs: [detect-code-related-file-changes]
@@ -134,6 +134,9 @@ jobs:
134134
run: |
135135
sudo apt-get update -q -y
136136
sudo apt-get install -q -y clang clang-tools libsdl2-dev libsdl2-mixer-dev
137+
wget https://apt.llvm.org/llvm.sh
138+
chmod +x ./llvm.sh
139+
sudo ./llvm.sh 17
137140
shell: bash
138141
- name: run scan-build without JIT
139142
run: make distclean && scan-build -v -o ~/scan-build --status-bugs --use-cc=clang --force-analyze-debug-code --show-description -analyzer-config stable-report-filename=true -enable-checker valist,nullability make ENABLE_EXT_F=0 ENABLE_SDL=0 ENABLE_JIT=0

src/emulate.c

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,9 @@ static block_t *block_alloc(riscv_t *rv)
308308
block->has_loops = false;
309309
block->n_invoke = 0;
310310
INIT_LIST_HEAD(&block->list);
311+
#if RV32_HAS(T2C)
312+
block->compiled = false;
313+
#endif
311314
#endif
312315
return block;
313316
}
@@ -993,13 +996,14 @@ void rv_step(void *arg)
993996
((exec_t2c_func_t) block->func)(rv);
994997
prev = NULL;
995998
continue;
996-
} /* check if the execution path is strong hotspot */
997-
if (block->n_invoke >= THRESHOLD) {
998-
t2c_compile(block,
999-
(uint64_t) ((memory_t *) PRIV(rv)->mem)->mem_base);
1000-
((exec_t2c_func_t) block->func)(rv);
1001-
prev = NULL;
1002-
continue;
999+
} /* check if invoking times of t1 generated code exceed threshold */
1000+
else if (!block->compiled && block->n_invoke >= THRESHOLD) {
1001+
block->compiled = true;
1002+
queue_entry_t *entry = malloc(sizeof(queue_entry_t));
1003+
entry->block = block;
1004+
pthread_mutex_lock(&rv->wait_queue_lock);
1005+
list_add(&entry->list, &rv->wait_queue);
1006+
pthread_mutex_unlock(&rv->wait_queue_lock);
10031007
}
10041008
#endif
10051009
/* executed through the tier-1 JIT compiler */

src/feature.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,11 @@
5050
/* Experimental just-in-time compiler */
5151
#ifndef RV32_FEATURE_JIT
5252
#define RV32_FEATURE_JIT 0
53-
#endif
54-
5553
/* Experimental tier-2 just-in-time compiler */
5654
#ifndef RV32_FEATURE_T2C
5755
#define RV32_FEATURE_T2C 0
5856
#endif
57+
#endif
5958

6059
/* Feature test macro */
6160
#define RV32_HAS(x) RV32_FEATURE_##x

src/riscv.c

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@
2828
#include "riscv_private.h"
2929
#include "utils.h"
3030
#if RV32_HAS(JIT)
31+
#if RV32_HAS(T2C)
32+
#include <pthread.h>
33+
#endif
3134
#include "cache.h"
3235
#include "jit.h"
3336
#define CODE_CACHE_SIZE (4 * 1024 * 1024)
@@ -184,6 +187,31 @@ IO_HANDLER_IMPL(byte, write_b, W)
184187
#undef R
185188
#undef W
186189

190+
#if RV32_HAS(T2C)
191+
static pthread_t t2c_thread;
192+
static void *t2c_runloop(void *arg)
193+
{
194+
riscv_t *rv = (riscv_t *) arg;
195+
while (1) {
196+
if (!list_empty(&rv->wait_queue)) {
197+
queue_entry_t *entry =
198+
list_last_entry(&rv->wait_queue, queue_entry_t, list);
199+
pthread_mutex_lock(&rv->wait_queue_lock);
200+
list_del_init(&entry->list);
201+
pthread_mutex_unlock(&rv->wait_queue_lock);
202+
t2c_compile(entry->block,
203+
(uint64_t) ((memory_t *) PRIV(rv)->mem)->mem_base);
204+
free(entry);
205+
}
206+
/* Instead of writing while(rv->quit), placing the code here prevents
207+
* rv->quit from being optimized by the compiler.*/
208+
if (rv->quit)
209+
break;
210+
}
211+
return NULL;
212+
}
213+
#endif
214+
187215
riscv_t *rv_create(riscv_user_t rv_attr)
188216
{
189217
assert(rv_attr);
@@ -269,6 +297,14 @@ riscv_t *rv_create(riscv_user_t rv_attr)
269297
rv->jit_state = jit_state_init(CODE_CACHE_SIZE);
270298
rv->block_cache = cache_create(BLOCK_MAP_CAPACITY_BITS);
271299
assert(rv->block_cache);
300+
#if RV32_HAS(T2C)
301+
rv->quit = false;
302+
/* prepare wait queue. */
303+
pthread_mutex_init(&rv->wait_queue_lock, NULL);
304+
INIT_LIST_HEAD(&rv->wait_queue);
305+
/* activate the background compilation thread. */
306+
pthread_create(&t2c_thread, NULL, t2c_runloop, rv);
307+
#endif
272308
#endif
273309

274310
return rv;
@@ -353,6 +389,11 @@ void rv_delete(riscv_t *rv)
353389
memory_delete(attr->mem);
354390
block_map_destroy(rv);
355391
#else
392+
#if RV32_HAS(T2C)
393+
rv->quit = true;
394+
pthread_join(t2c_thread, NULL);
395+
pthread_mutex_destroy(&rv->wait_queue_lock);
396+
#endif
356397
mpool_destroy(rv->chain_entry_mp);
357398
jit_state_exit(rv->jit_state);
358399
cache_free(rv->block_cache);

src/riscv_private.h

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
#include "riscv.h"
1515
#include "utils.h"
1616
#if RV32_HAS(JIT)
17+
#if RV32_HAS(T2C)
18+
#include <pthread.h>
19+
#endif
1720
#include "cache.h"
1821
#endif
1922

@@ -70,7 +73,10 @@ typedef struct block {
7073
bool
7174
translatable; /**< Determine the block has RV32AF insturctions or not */
7275
bool has_loops; /**< Determine the block has loop or not */
73-
uint32_t offset; /**< The machine code offset in T1 code cache */
76+
#if RV32_HAS(T2C)
77+
bool compiled; /**< The T2C request is enqueued or not */
78+
#endif
79+
uint32_t offset; /**< The machine code offset in T1 code cache */
7480
uint32_t n_invoke; /**< The invoking times of T1 machine code */
7581
void *func; /**< The function pointer of T2 machine code */
7682
struct list_head list;
@@ -82,6 +88,12 @@ typedef struct {
8288
block_t *block;
8389
struct list_head list;
8490
} chain_entry_t;
91+
#if RV32_HAS(T2C)
92+
typedef struct {
93+
block_t *block;
94+
struct list_head list;
95+
} queue_entry_t;
96+
#endif
8597
#endif
8698

8799
typedef struct {
@@ -134,6 +146,11 @@ struct riscv_internal {
134146
#else
135147
struct cache *block_cache;
136148
struct mpool *chain_entry_mp;
149+
#if RV32_HAS(T2C)
150+
struct list_head wait_queue;
151+
pthread_mutex_t wait_queue_lock;
152+
bool quit; /**< Determine the main thread is terminated or not */
153+
#endif
137154
#endif
138155
struct mpool *block_mp, *block_ir_mp;
139156

0 commit comments

Comments
 (0)