@@ -287,18 +287,22 @@ enum {
287
287
#define RVOP_RUN_NEXT (!ir->tailcall)
288
288
#endif
289
289
290
- #define RVOP (inst , code ) \
291
- static bool do_##inst(riscv_t *rv UNUSED, const rv_insn_t *ir UNUSED) \
292
- { \
293
- rv->X[rv_reg_zero] = 0; \
294
- code; \
295
- rv->csr_cycle++; \
296
- nextop: \
297
- rv->PC += ir->insn_len; \
298
- if (!RVOP_RUN_NEXT) \
299
- return true; \
300
- const rv_insn_t *next = ir + 1; \
301
- MUST_TAIL return next->impl(rv, next); \
290
+ /* branch_taken record the branch is taken or not during emulation */
291
+ static bool branch_taken = false;
292
+ /* last_pc record the program counter of the previous block */
293
+ static uint32_t last_pc = 0 ;
294
+ #define RVOP (inst , code ) \
295
+ static bool do_##inst(riscv_t *rv, const rv_insn_t *ir) \
296
+ { \
297
+ rv->X[rv_reg_zero] = 0; \
298
+ rv->csr_cycle++; \
299
+ code; \
300
+ nextop: \
301
+ rv->PC += ir->insn_len; \
302
+ if (!RVOP_RUN_NEXT) \
303
+ return true; \
304
+ const rv_insn_t *next = ir + 1; \
305
+ MUST_TAIL return next->impl(rv, next); \
302
306
}
303
307
304
308
/* RV32I Base Instruction Set */
@@ -334,7 +338,7 @@ RVOP(jal, {
334
338
rv -> X [ir -> rd ] = pc + ir -> insn_len ;
335
339
/* check instruction misaligned */
336
340
RV_EXC_MISALIGN_HANDLER (pc , insn , false, 0 );
337
- return true ;
341
+ return ir -> branch_taken -> impl ( rv , ir -> branch_taken ) ;
338
342
})
339
343
340
344
/*The indirect jump instruction JALR uses the I-type encoding. The
@@ -356,107 +360,45 @@ RVOP(jalr, {
356
360
return true;
357
361
})
358
362
359
- /* BEQ: Branch if Equal */
360
- RVOP (beq , {
361
- const uint32_t pc = rv -> PC ;
362
- if (rv -> X [ir -> rs1 ] != rv -> X [ir -> rs2 ]) {
363
- if (!ir -> branch_untaken )
364
- goto nextop ;
365
- rv -> PC += ir -> insn_len ;
366
- return ir -> branch_untaken -> impl (rv , ir -> branch_untaken );
367
- }
368
- rv -> PC += ir -> imm ;
369
- /* check instruction misaligned */
370
- RV_EXC_MISALIGN_HANDLER (pc , insn , false, 0 );
371
- if (ir -> branch_taken )
372
- return ir -> branch_taken -> impl (rv , ir -> branch_taken );
363
+ /* clang-format off */
364
+ #define BRANCH_FUNC (type , cond ) \
365
+ const uint32_t pc = rv->PC; \
366
+ if ((type) rv->X[ir->rs1] cond (type) rv->X[ir->rs2]) { \
367
+ branch_taken = false; \
368
+ if (!ir->branch_untaken) \
369
+ goto nextop; \
370
+ rv->PC += ir->insn_len; \
371
+ last_pc = rv->PC; \
372
+ return ir->branch_untaken->impl(rv, ir->branch_untaken); \
373
+ } \
374
+ branch_taken = true; \
375
+ rv->PC += ir->imm; \
376
+ /* check instruction misaligned */ \
377
+ RV_EXC_MISALIGN_HANDLER (pc , insn , false, 0 ); \
378
+ if (ir -> branch_taken ) { \
379
+ last_pc = rv -> PC ; \
380
+ return ir -> branch_taken -> impl (rv , ir -> branch_taken ); \
381
+ } \
373
382
return true;
374
- })
383
+ /* clang-format on */
384
+
385
+ /* BEQ: Branch if Equal */
386
+ RVOP (beq , { BRANCH_FUNC (uint32_t , != ); })
375
387
376
388
/* BNE: Branch if Not Equal */
377
- RVOP (bne , {
378
- const uint32_t pc = rv -> PC ;
379
- if (rv -> X [ir -> rs1 ] == rv -> X [ir -> rs2 ]) {
380
- if (!ir -> branch_untaken )
381
- goto nextop ;
382
- rv -> PC += ir -> insn_len ;
383
- return ir -> branch_untaken -> impl (rv , ir -> branch_untaken );
384
- }
385
- rv -> PC += ir -> imm ;
386
- /* check instruction misaligned */
387
- RV_EXC_MISALIGN_HANDLER (pc , insn , false, 0 );
388
- if (ir -> branch_taken )
389
- return ir -> branch_taken -> impl (rv , ir -> branch_taken );
390
- return true;
391
- })
389
+ RVOP (bne , { BRANCH_FUNC (uint32_t , = = ); })
392
390
393
391
/* BLT: Branch if Less Than */
394
- RVOP (blt , {
395
- const uint32_t pc = rv -> PC ;
396
- if ((int32_t ) rv -> X [ir -> rs1 ] >= (int32_t ) rv -> X [ir -> rs2 ]) {
397
- if (!ir -> branch_untaken )
398
- goto nextop ;
399
- rv -> PC += ir -> insn_len ;
400
- return ir -> branch_untaken -> impl (rv , ir -> branch_untaken );
401
- }
402
- rv -> PC += ir -> imm ;
403
- /* check instruction misaligned */
404
- RV_EXC_MISALIGN_HANDLER (pc , insn , false, 0 );
405
- if (ir -> branch_taken )
406
- return ir -> branch_taken -> impl (rv , ir -> branch_taken );
407
- return true;
408
- })
392
+ RVOP (blt , { BRANCH_FUNC (int32_t , >=); })
409
393
410
394
/* BGE: Branch if Greater Than */
411
- RVOP (bge , {
412
- const uint32_t pc = rv -> PC ;
413
- if ((int32_t ) rv -> X [ir -> rs1 ] < (int32_t ) rv -> X [ir -> rs2 ]) {
414
- if (!ir -> branch_untaken )
415
- goto nextop ;
416
- rv -> PC += ir -> insn_len ;
417
- return ir -> branch_untaken -> impl (rv , ir -> branch_untaken );
418
- }
419
- rv -> PC += ir -> imm ;
420
- /* check instruction misaligned */
421
- RV_EXC_MISALIGN_HANDLER (pc , insn , false, 0 );
422
- if (ir -> branch_taken )
423
- return ir -> branch_taken -> impl (rv , ir -> branch_taken );
424
- return true;
425
- })
395
+ RVOP (bge , { BRANCH_FUNC (int32_t , < ); })
426
396
427
397
/* BLTU: Branch if Less Than Unsigned */
428
- RVOP (bltu , {
429
- const uint32_t pc = rv -> PC ;
430
- if (rv -> X [ir -> rs1 ] >= rv -> X [ir -> rs2 ]) {
431
- if (!ir -> branch_untaken )
432
- goto nextop ;
433
- rv -> PC += ir -> insn_len ;
434
- return ir -> branch_untaken -> impl (rv , ir -> branch_untaken );
435
- }
436
- rv -> PC += ir -> imm ;
437
- /* check instruction misaligned */
438
- RV_EXC_MISALIGN_HANDLER (pc , insn , false, 0 );
439
- if (ir -> branch_taken )
440
- return ir -> branch_taken -> impl (rv , ir -> branch_taken );
441
- return true;
442
- })
398
+ RVOP (bltu , { BRANCH_FUNC (uint32_t , >=); })
443
399
444
400
/* BGEU: Branch if Greater Than Unsigned */
445
- RVOP (bgeu , {
446
- const uint32_t pc = rv -> PC ;
447
- if (rv -> X [ir -> rs1 ] < rv -> X [ir -> rs2 ]) {
448
- if (!ir -> branch_untaken )
449
- goto nextop ;
450
- rv -> PC += ir -> insn_len ;
451
- return ir -> branch_untaken -> impl (rv , ir -> branch_untaken );
452
- }
453
- rv -> PC += ir -> imm ;
454
- /* check instruction misaligned */
455
- RV_EXC_MISALIGN_HANDLER (pc , insn , false, 0 );
456
- if (ir -> branch_taken )
457
- return ir -> branch_taken -> impl (rv , ir -> branch_taken );
458
- return true;
459
- })
401
+ RVOP (bgeu , { BRANCH_FUNC (uint32_t , < ); })
460
402
461
403
/* LB: Load Byte */
462
404
RVOP (lb , {
@@ -1116,7 +1058,7 @@ RVOP(cjal, {
1116
1058
rv -> X [1 ] = rv -> PC + ir -> insn_len ;
1117
1059
rv -> PC += ir -> imm ;
1118
1060
RV_EXC_MISALIGN_HANDLER (rv -> PC , insn , true, 0 );
1119
- return true ;
1061
+ return ir -> branch_taken -> impl ( rv , ir -> branch_taken ) ;
1120
1062
})
1121
1063
1122
1064
/* C.LI loads the sign-extended 6-bit immediate, imm, into register rd.
@@ -1184,7 +1126,7 @@ RVOP(cand, { rv->X[ir->rd] = rv->X[ir->rs1] & rv->X[ir->rs2]; })
1184
1126
RVOP (cj , {
1185
1127
rv -> PC += ir -> imm ;
1186
1128
RV_EXC_MISALIGN_HANDLER (rv -> PC , insn , true, 0 );
1187
- return true ;
1129
+ return ir -> branch_taken -> impl ( rv , ir -> branch_taken ) ;
1188
1130
})
1189
1131
1190
1132
/* C.BEQZ performs conditional control transfers. The offset is
@@ -1195,11 +1137,13 @@ RVOP(cj, {
1195
1137
*/
1196
1138
RVOP (cbeqz , {
1197
1139
if (rv -> X [ir -> rs1 ]) {
1140
+ branch_taken = false;
1198
1141
if (!ir -> branch_untaken )
1199
1142
goto nextop ;
1200
1143
rv -> PC += ir -> insn_len ;
1201
1144
return ir -> branch_untaken -> impl (rv , ir -> branch_untaken );
1202
1145
}
1146
+ branch_taken = true;
1203
1147
rv -> PC += (uint32_t ) ir -> imm ;
1204
1148
if (ir -> branch_taken )
1205
1149
return ir -> branch_taken -> impl (rv , ir -> branch_taken );
@@ -1209,11 +1153,13 @@ RVOP(cbeqz, {
1209
1153
/* C.BEQZ */
1210
1154
RVOP (cbnez , {
1211
1155
if (!rv -> X [ir -> rs1 ]) {
1156
+ branch_taken = false;
1212
1157
if (!ir -> branch_untaken )
1213
1158
goto nextop ;
1214
1159
rv -> PC += ir -> insn_len ;
1215
1160
return ir -> branch_untaken -> impl (rv , ir -> branch_untaken );
1216
1161
}
1162
+ branch_taken = true;
1217
1163
rv -> PC += (uint32_t ) ir -> imm ;
1218
1164
if (ir -> branch_taken )
1219
1165
return ir -> branch_taken -> impl (rv , ir -> branch_taken );
@@ -1294,6 +1240,26 @@ static bool insn_is_branch(uint8_t opcode)
1294
1240
return false;
1295
1241
}
1296
1242
1243
+ static bool insn_is_unconditional_branch (uint8_t opcode )
1244
+ {
1245
+ switch (opcode ) {
1246
+ case rv_insn_ecall :
1247
+ case rv_insn_ebreak :
1248
+ case rv_insn_jal :
1249
+ case rv_insn_jalr :
1250
+ case rv_insn_mret :
1251
+ #if RV32_HAS (EXT_C )
1252
+ case rv_insn_cj :
1253
+ case rv_insn_cjalr :
1254
+ case rv_insn_cjal :
1255
+ case rv_insn_cjr :
1256
+ case rv_insn_cebreak :
1257
+ #endif
1258
+ return true;
1259
+ }
1260
+ return false;
1261
+ }
1262
+
1297
1263
/* hash function is used when mapping address into the block map */
1298
1264
static uint32_t hash (size_t k )
1299
1265
{
@@ -1377,37 +1343,25 @@ static void block_translate(riscv_t *rv, block_t *block)
1377
1343
block -> n_insn ++ ;
1378
1344
1379
1345
/* stop on branch */
1380
- if (insn_is_branch (ir -> opcode ))
1346
+ if (insn_is_branch (ir -> opcode )) {
1347
+ /* recursive jump translation */
1348
+ if (ir -> opcode == rv_insn_jal
1349
+ #if RV32_HAS (EXT_C )
1350
+ || ir -> opcode == rv_insn_cj || ir -> opcode == rv_insn_cjal
1351
+ #endif
1352
+ ) {
1353
+ block -> pc_end = block -> pc_end - ir -> insn_len + ir -> imm ;
1354
+ ir -> branch_taken = ir + 1 ;
1355
+ continue ;
1356
+ }
1381
1357
break ;
1358
+ }
1382
1359
}
1383
1360
block -> ir [block -> n_insn - 1 ].tailcall = true;
1384
1361
}
1385
1362
1386
- static void extend_block (riscv_t * rv , block_t * block )
1387
- {
1388
- rv_insn_t * last_ir = block -> ir + block -> n_insn - 1 ;
1389
- if (last_ir -> branch_taken && last_ir -> branch_untaken )
1390
- return ;
1391
- /* calculate the PC of taken and untaken branches to find block */
1392
- uint32_t taken_pc = block -> pc_end - last_ir -> insn_len + last_ir -> imm ,
1393
- not_taken_pc = block -> pc_end ;
1394
-
1395
- block_map_t * map = & rv -> block_map ;
1396
- block_t * next ;
1397
-
1398
- /* check the branch_taken/branch_untaken pointer has been assigned and the
1399
- * first basic block in the path of the taken/untaken branches exists or
1400
- * not. If either of these conditions is not met, it will not be possible to
1401
- * extend the path of the taken/untaken branches for basic block.
1402
- */
1403
- if (!last_ir -> branch_taken && (next = block_find (map , taken_pc )))
1404
- last_ir -> branch_taken = next -> ir ;
1405
-
1406
- if (!last_ir -> branch_untaken && (next = block_find (map , not_taken_pc )))
1407
- last_ir -> branch_untaken = next -> ir ;
1408
- }
1409
-
1410
- static block_t * block_find_or_translate (riscv_t * rv , block_t * prev )
1363
+ static block_t * prev = NULL ;
1364
+ static block_t * block_find_or_translate (riscv_t * rv )
1411
1365
{
1412
1366
block_map_t * map = & rv -> block_map ;
1413
1367
/* lookup the next block in the block map */
@@ -1435,9 +1389,7 @@ static block_t *block_find_or_translate(riscv_t *rv, block_t *prev)
1435
1389
*/
1436
1390
if (prev )
1437
1391
prev -> predict = next ;
1438
- } else
1439
- extend_block (rv , next );
1440
-
1392
+ }
1441
1393
1442
1394
return next ;
1443
1395
}
@@ -1447,27 +1399,45 @@ void rv_step(riscv_t *rv, int32_t cycles)
1447
1399
assert (rv );
1448
1400
1449
1401
/* find or translate a block for starting PC */
1450
- block_t * prev = NULL ;
1451
-
1452
1402
const uint64_t cycles_target = rv -> csr_cycle + cycles ;
1453
1403
1454
1404
/* loop until we hit out cycle target */
1455
1405
while (rv -> csr_cycle < cycles_target && !rv -> halt ) {
1456
1406
block_t * block ;
1457
-
1458
1407
/* try to predict the next block */
1459
1408
if (prev && prev -> predict && prev -> predict -> pc_start == rv -> PC ) {
1460
1409
block = prev -> predict ;
1461
1410
} else {
1462
1411
/* lookup the next block in block map or translate a new block,
1463
1412
* and move onto the next block.
1464
1413
*/
1465
- block = block_find_or_translate (rv , prev );
1414
+ block = block_find_or_translate (rv );
1466
1415
}
1467
1416
1468
1417
/* we should have a block by now */
1469
1418
assert (block );
1470
1419
1420
+ /* After emulating the previous block, we determine whether the branch
1421
+ * is taken or not. Consequently, we assign the IR array of the current
1422
+ * block to either the branch_taken or branch_untaken pointer of the
1423
+ * previous block.
1424
+ */
1425
+ if (prev ) {
1426
+ /* updtae previous block */
1427
+ if (prev -> pc_start != last_pc )
1428
+ prev = block_find (& rv -> block_map , last_pc );
1429
+
1430
+ rv_insn_t * last_ir = prev -> ir + prev -> n_insn - 1 ;
1431
+ /* chain block */
1432
+ if (!insn_is_unconditional_branch (last_ir -> opcode )) {
1433
+ if (branch_taken && !last_ir -> branch_taken )
1434
+ last_ir -> branch_taken = block -> ir ;
1435
+ else if (!last_ir -> branch_untaken )
1436
+ last_ir -> branch_untaken = block -> ir ;
1437
+ }
1438
+ }
1439
+ last_pc = rv -> PC ;
1440
+
1471
1441
/* execute the block */
1472
1442
const rv_insn_t * ir = block -> ir ;
1473
1443
if (unlikely (!ir -> impl (rv , ir )))
0 commit comments