4
4
*/
5
5
6
6
#include <assert.h>
7
+ #include <sched.h>
8
+ #include <stdatomic.h>
7
9
#include <stdbool.h>
8
10
#include <stdint.h>
9
11
#include <stdio.h>
@@ -278,12 +280,13 @@ static block_t *block_alloc(riscv_t *rv)
278
280
#if RV32_HAS (JIT )
279
281
block -> translatable = true;
280
282
block -> hot = false;
281
- block -> hot2 = false;
282
283
block -> has_loops = false;
283
284
block -> n_invoke = 0 ;
284
285
INIT_LIST_HEAD (& block -> list );
285
286
#if RV32_HAS (T2C )
286
- block -> compiled = false;
287
+ /* Initialize T2C compilation flag */
288
+ atomic_store_explicit (& block -> compiled , false, memory_order_relaxed );
289
+ atomic_store_explicit (& block -> func , NULL , memory_order_relaxed );
287
290
#endif
288
291
#endif
289
292
return block ;
@@ -918,6 +921,29 @@ static block_t *block_find_or_translate(riscv_t *rv)
918
921
return next_blk ;
919
922
}
920
923
924
+ #if RV32_HAS (T2C )
925
+ /* Avoid evicting blocks being compiled */
926
+ if (atomic_load_explicit (& replaced_blk -> compiled , memory_order_acquire )) {
927
+ /* This block is being compiled - do not evict it.
928
+ * Return NULL to signal cache is full.
929
+ */
930
+ pthread_mutex_unlock (& rv -> cache_lock );
931
+
932
+ /* Free the newly translated block */
933
+ for (rv_insn_t * ir = next_blk -> ir_head , * next_ir ; ir ; ir = next_ir ) {
934
+ next_ir = ir -> next ;
935
+ free (ir -> fuse );
936
+ mpool_free (rv -> block_ir_mp , ir );
937
+ }
938
+ list_del_init (& next_blk -> list );
939
+ atomic_store_explicit (& next_blk -> func , NULL , memory_order_relaxed );
940
+ mpool_free (rv -> block_mp , next_blk );
941
+
942
+ return NULL ;
943
+ }
944
+ /* Allow evicting blocks that are not yet compiled */
945
+ #endif
946
+
921
947
if (prev == replaced_blk )
922
948
prev = NULL ;
923
949
@@ -930,43 +956,60 @@ static block_t *block_find_or_translate(riscv_t *rv)
930
956
rv_insn_t * taken = entry -> ir_tail -> branch_taken ,
931
957
* untaken = entry -> ir_tail -> branch_untaken ;
932
958
933
- if (taken == replaced_blk_entry ) {
959
+ if (taken == replaced_blk_entry )
934
960
entry -> ir_tail -> branch_taken = NULL ;
935
- }
936
- if (untaken == replaced_blk_entry ) {
961
+ if (untaken == replaced_blk_entry )
937
962
entry -> ir_tail -> branch_untaken = NULL ;
938
- }
939
963
940
964
/* upadte JALR LUT */
941
- if (!entry -> ir_tail -> branch_table ) {
965
+ if (!entry -> ir_tail -> branch_table )
942
966
continue ;
943
- }
944
967
945
- /**
946
- * TODO: upadate all JALR instructions which references to this
947
- * basic block as the destination.
968
+ /* TODO: upadate all JALR instructions which references to this basic
969
+ * block as the destination.
948
970
*/
949
971
}
950
972
951
- /* free IRs in replaced block */
952
- for (rv_insn_t * ir = replaced_blk -> ir_head , * next_ir ; ir != NULL ;
953
- ir = next_ir ) {
954
- next_ir = ir -> next ;
973
+ #if RV32_HAS (T2C )
974
+ /* Double-check - do not evict if being compiled */
975
+ if (atomic_load_explicit (& replaced_blk -> compiled , memory_order_acquire )) {
976
+ /* Block is being compiled - cannot evict */
977
+ pthread_mutex_unlock (& rv -> cache_lock );
955
978
956
- if (ir -> fuse )
979
+ /* Free the newly translated block */
980
+ for (rv_insn_t * ir = next_blk -> ir_head , * next_ir ; ir ; ir = next_ir ) {
981
+ next_ir = ir -> next ;
957
982
free (ir -> fuse );
983
+ mpool_free (rv -> block_ir_mp , ir );
984
+ }
985
+ list_del_init (& next_blk -> list );
986
+ atomic_store_explicit (& next_blk -> func , NULL , memory_order_relaxed );
987
+ mpool_free (rv -> block_mp , next_blk );
958
988
989
+ return NULL ;
990
+ }
991
+ #endif
992
+ /* At this point, block is not compiled - safe to evict */
993
+ /* Free the replaced block
994
+ */
995
+ for (rv_insn_t * ir = replaced_blk -> ir_head , * next_ir ; ir ; ir = next_ir ) {
996
+ next_ir = ir -> next ;
997
+ free (ir -> fuse );
959
998
mpool_free (rv -> block_ir_mp , ir );
960
999
}
961
1000
962
1001
list_del_init (& replaced_blk -> list );
1002
+ #if RV32_HAS (T2C )
1003
+ /* Clear atomic fields before returning to pool */
1004
+ atomic_store_explicit (& replaced_blk -> func , NULL , memory_order_relaxed );
1005
+ atomic_store_explicit (& replaced_blk -> compiled , false, memory_order_relaxed );
1006
+ #endif
963
1007
mpool_free (rv -> block_mp , replaced_blk );
964
1008
#if RV32_HAS (T2C )
965
1009
pthread_mutex_unlock (& rv -> cache_lock );
966
1010
#endif
967
1011
#endif
968
1012
969
- assert (next_blk );
970
1013
return next_blk ;
971
1014
}
972
1015
@@ -1077,6 +1120,15 @@ void rv_step(void *arg)
1077
1120
* and move onto the next block.
1078
1121
*/
1079
1122
block_t * block = block_find_or_translate (rv );
1123
+ #if RV32_HAS (T2C )
1124
+ if (!block ) {
1125
+ /* Cache is full of compiled blocks.
1126
+ * Try again after yielding to allow T2C thread to complete.
1127
+ */
1128
+ sched_yield ();
1129
+ continue ;
1130
+ }
1131
+ #endif
1080
1132
/* by now, a block should be available */
1081
1133
assert (block );
1082
1134
@@ -1120,20 +1172,88 @@ void rv_step(void *arg)
1120
1172
last_pc = rv -> PC ;
1121
1173
#if RV32_HAS (JIT )
1122
1174
#if RV32_HAS (T2C )
1123
- /* executed through the tier-2 JIT compiler */
1124
- if (block -> hot2 ) {
1125
- ((exec_t2c_func_t ) block -> func )(rv );
1175
+ /* Check if T2C compiled code exists for this block */
1176
+ t2c_entry_t * t2c_entry =
1177
+ cache_get (rv -> t2c_cache , block -> pc_start , false);
1178
+ if (t2c_entry && t2c_entry -> func ) {
1179
+ /* Clear compiled flag now that T2C code is available
1180
+ * This allows the block to be evicted if needed */
1181
+ atomic_store_explicit (& block -> compiled , false,
1182
+ memory_order_relaxed );
1183
+
1184
+ /* Execute the compiled function - no synchronization needed
1185
+ * as T2C entries are immutable
1186
+ */
1187
+ exec_t2c_func_t func = (exec_t2c_func_t ) t2c_entry -> func ;
1188
+ func (rv );
1126
1189
prev = NULL ;
1127
1190
continue ;
1128
- } /* check if invoking times of t1 generated code exceed threshold */
1129
- else if (!block -> compiled && block -> n_invoke >= THRESHOLD ) {
1130
- block -> compiled = true;
1191
+ }
1192
+ /* check if invoking times of t1 generated code exceed threshold */
1193
+ if (!atomic_load_explicit (& block -> compiled , memory_order_acquire ) &&
1194
+ block -> n_invoke >= THRESHOLD ) {
1195
+ /* Mark block as queued for compilation to avoid re-queueing */
1196
+ atomic_store_explicit (& block -> compiled , true, memory_order_release );
1197
+
1131
1198
queue_entry_t * entry = malloc (sizeof (queue_entry_t ));
1132
- entry -> block = block ;
1133
- pthread_mutex_lock (& rv -> wait_queue_lock );
1134
- list_add (& entry -> list , & rv -> wait_queue );
1135
- pthread_mutex_unlock (& rv -> wait_queue_lock );
1199
+ if (entry ) {
1200
+ /* Copy block metadata */
1201
+ entry -> pc_start = block -> pc_start ;
1202
+ entry -> pc_end = block -> pc_end ;
1203
+ entry -> n_insn = block -> n_insn ;
1204
+ #if RV32_HAS (SYSTEM )
1205
+ entry -> satp = block -> satp ;
1206
+ #endif
1207
+ /* Deep copy the IR chain */
1208
+ entry -> ir_head_copy = NULL ;
1209
+ rv_insn_t * * copy_ptr = & entry -> ir_head_copy ;
1210
+ for (rv_insn_t * ir = block -> ir_head ; ir ; ir = ir -> next ) {
1211
+ rv_insn_t * ir_copy = malloc (sizeof (rv_insn_t ));
1212
+ if (!ir_copy ) {
1213
+ /* Clean up on failure */
1214
+ for (rv_insn_t * tmp = entry -> ir_head_copy , * next ; tmp ;
1215
+ tmp = next ) {
1216
+ next = tmp -> next ;
1217
+ free (tmp -> fuse );
1218
+ free (tmp );
1219
+ }
1220
+ free (entry );
1221
+ atomic_store_explicit (& block -> compiled , false,
1222
+ memory_order_release );
1223
+ goto skip_t2c ;
1224
+ }
1225
+ memcpy (ir_copy , ir , sizeof (rv_insn_t ));
1226
+ /* Copy fuse data if present */
1227
+ if (ir -> fuse ) {
1228
+ size_t fuse_size = ir -> imm2 * sizeof (opcode_fuse_t );
1229
+ ir_copy -> fuse = malloc (fuse_size );
1230
+ if (ir_copy -> fuse )
1231
+ memcpy (ir_copy -> fuse , ir -> fuse , fuse_size );
1232
+ }
1233
+ /* Clear branch pointers as they are not needed for
1234
+ * compilation.
1235
+ */
1236
+ ir_copy -> branch_taken = NULL ;
1237
+ ir_copy -> branch_untaken = NULL ;
1238
+ ir_copy -> branch_table = NULL ;
1239
+ /* Link the copy */
1240
+ ir_copy -> next = NULL ;
1241
+ * copy_ptr = ir_copy ;
1242
+ copy_ptr = & ir_copy -> next ;
1243
+ }
1244
+
1245
+ pthread_mutex_lock (& rv -> wait_queue_lock );
1246
+ list_add (& entry -> list , & rv -> wait_queue );
1247
+ pthread_cond_signal (& rv -> wait_queue_cond );
1248
+ pthread_mutex_unlock (& rv -> wait_queue_lock );
1249
+ /* Keep compiled flag set to prevent re-queueing */
1250
+ } else {
1251
+ /* Failed to allocate - clear compiled flag */
1252
+ atomic_store_explicit (& block -> compiled , false,
1253
+ memory_order_release );
1254
+ }
1136
1255
}
1256
+ skip_t2c :; /* Empty statement for label */
1137
1257
#endif
1138
1258
/* executed through the tier-1 JIT compiler */
1139
1259
struct jit_state * state = rv -> jit_state ;
@@ -1165,6 +1285,12 @@ void rv_step(void *arg)
1165
1285
#endif
1166
1286
/* execute the block by interpreter */
1167
1287
const rv_insn_t * ir = block -> ir_head ;
1288
+ /* should never happen */
1289
+ if (unlikely (!ir || !ir -> impl )) {
1290
+ /* Fatal error: block is corrupted. Break to avoid infinite loop */
1291
+ prev = NULL ;
1292
+ break ;
1293
+ }
1168
1294
if (unlikely (!ir -> impl (rv , ir , rv -> csr_cycle , rv -> PC ))) {
1169
1295
/* block should not be extended if execption handler invoked */
1170
1296
prev = NULL ;
0 commit comments