Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Enforce doubly-linked chained block #331

Merged
merged 2 commits into from
Jan 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 0 additions & 62 deletions src/cache.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,6 @@ static struct mpool *cache_mp;
/* hash function for the cache */
HASH_FUNC_IMPL(cache_hash, cache_size_bits, cache_size)

struct list_head {
struct list_head *prev, *next;
};

struct hlist_head {
struct hlist_node *first;
};
Expand Down Expand Up @@ -58,64 +54,6 @@ typedef struct cache {
uint32_t capacity;
} cache_t;

static inline void INIT_LIST_HEAD(struct list_head *head)
{
head->next = head;
head->prev = head;
}

static inline int list_empty(const struct list_head *head)
{
return (head->next == head);
}

static inline void list_add(struct list_head *node, struct list_head *head)
{
struct list_head *next = head->next;

next->prev = node;
node->next = next;
node->prev = head;
head->next = node;
}

static inline void list_del(struct list_head *node)
{
struct list_head *next = node->next;
struct list_head *prev = node->prev;

next->prev = prev;
prev->next = next;
}

static inline void list_del_init(struct list_head *node)
{
list_del(node);
INIT_LIST_HEAD(node);
}

#define list_entry(node, type, member) container_of(node, type, member)

#define list_first_entry(head, type, member) \
list_entry((head)->next, type, member)

#define list_last_entry(head, type, member) \
list_entry((head)->prev, type, member)

#ifdef __HAVE_TYPEOF
#define list_for_each_entry_safe(entry, safe, head, member) \
for (entry = list_entry((head)->next, __typeof__(*entry), member), \
safe = list_entry(entry->member.next, __typeof__(*entry), member); \
&entry->member != (head); entry = safe, \
safe = list_entry(safe->member.next, __typeof__(*entry), member))
#else
#define list_for_each_entry_safe(entry, safe, head, member, type) \
for (entry = list_entry((head)->next, type, member), \
safe = list_entry(entry->member.next, type, member); \
&entry->member != (head); \
entry = safe, safe = list_entry(safe->member.next, type, member))
#endif

#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)

static inline void INIT_HLIST_NODE(struct hlist_node *h)
Expand Down
117 changes: 82 additions & 35 deletions src/emulate.c
Original file line number Diff line number Diff line change
Expand Up @@ -301,11 +301,11 @@ static block_t *block_alloc(riscv_t *rv)
block_t *block = mpool_alloc(rv->block_mp);
assert(block);
block->n_insn = 0;
block->predict = NULL;
#if RV32_HAS(JIT)
block->translatable = true;
block->hot = false;
block->backward = false;
INIT_LIST_HEAD(&block->list);
#endif
return block;
}
Expand Down Expand Up @@ -979,6 +979,49 @@ static block_t *block_find_or_translate(riscv_t *rv)
/* insert the block into block cache */
block_t *delete_target = cache_put(rv->block_cache, rv->PC, &(*next));
if (delete_target) {
if (prev == delete_target)
prev = NULL;
chain_entry_t *entry, *safe;
/* correctly remove deleted block from its chained block */
rv_insn_t *taken = delete_target->ir_tail->branch_taken,
*untaken = delete_target->ir_tail->branch_untaken;
if (taken && taken->pc != delete_target->pc_start) {
block_t *target = cache_get(rv->block_cache, taken->pc);
bool flag = false;
list_for_each_entry_safe (entry, safe, &target->list, list) {
if (entry->block == delete_target) {
list_del_init(&entry->list);
mpool_free(rv->chain_entry_mp, entry);
flag = true;
}
}
assert(flag);
}
if (untaken && untaken->pc != delete_target->pc_start) {
block_t *target = cache_get(rv->block_cache, untaken->pc);
assert(target);
bool flag = false;
list_for_each_entry_safe (entry, safe, &target->list, list) {
if (entry->block == delete_target) {
list_del_init(&entry->list);
mpool_free(rv->chain_entry_mp, entry);
flag = true;
}
}
assert(flag);
}
/* correctly remove deleted block from the block chained to it */
list_for_each_entry_safe (entry, safe, &delete_target->list, list) {
if (entry->block == delete_target)
continue;
rv_insn_t *target = entry->block->ir_tail;
if (target->branch_taken == delete_target->ir_head)
target->branch_taken = NULL;
else if (target->branch_untaken == delete_target->ir_head)
target->branch_untaken = NULL;
mpool_free(rv->chain_entry_mp, entry);
}
/* free deleted block */
uint32_t idx;
rv_insn_t *ir, *next;
for (idx = 0, ir = delete_target->ir_head;
Expand All @@ -990,13 +1033,6 @@ static block_t *block_find_or_translate(riscv_t *rv)
mpool_free(rv->block_mp, delete_target);
}
#endif
/* update the block prediction.
* When translating a new block, the block predictor may benefit,
* but updating it after finding a particular block may penalize
* significantly.
*/
if (prev)
prev->predict = next;
}

return next;
Expand All @@ -1015,17 +1051,18 @@ void rv_step(riscv_t *rv, int32_t cycles)

/* loop until hitting the cycle target */
while (rv->csr_cycle < cycles_target && !rv->halt) {
block_t *block;
/* try to predict the next block */
if (prev && prev->predict && prev->predict->pc_start == rv->PC) {
block = prev->predict;
} else {
/* lookup the next block in block map or translate a new block,
* and move onto the next block.
*/
block = block_find_or_translate(rv);
if (prev && prev->pc_start != last_pc) {
/* update previous block */
#if !RV32_HAS(JIT)
prev = block_find(&rv->block_map, last_pc);
#else
prev = cache_get(rv->block_cache, last_pc);
#endif
}

/* lookup the next block in block map or translate a new block,
* and move onto the next block.
*/
block_t *block = block_find_or_translate(rv);
/* by now, a block should be available */
assert(block);

Expand All @@ -1034,28 +1071,38 @@ void rv_step(riscv_t *rv, int32_t cycles)
* assigned to either the branch_taken or branch_untaken pointer of
* the previous block.
*/

if (prev) {
/* update previous block */
if (prev->pc_start != last_pc)
#if !RV32_HAS(JIT)
prev = block_find(&rv->block_map, last_pc);
#else
prev = cache_get(rv->block_cache, last_pc);
rv_insn_t *last_ir = prev->ir_tail;
/* chain block */
if (!insn_is_unconditional_branch(last_ir->opcode)) {
if (is_branch_taken && !last_ir->branch_taken) {
last_ir->branch_taken = block->ir_head;
#if RV32_HAS(JIT)
chain_entry_t *new_entry = mpool_alloc(rv->chain_entry_mp);
new_entry->block = prev;
list_add(&new_entry->list, &block->list);
#endif
if (prev) {
rv_insn_t *last_ir = prev->ir_tail;
/* chain block */
if (!insn_is_unconditional_branch(last_ir->opcode)) {
if (is_branch_taken)
last_ir->branch_taken = block->ir_head;
else if (!is_branch_taken)
last_ir->branch_untaken = block->ir_head;
} else if (IF_insn(last_ir, jal)
} else if (!is_branch_taken && !last_ir->branch_untaken) {
last_ir->branch_untaken = block->ir_head;
#if RV32_HAS(JIT)
chain_entry_t *new_entry = mpool_alloc(rv->chain_entry_mp);
new_entry->block = prev;
list_add(&new_entry->list, &block->list);
#endif
}
} else if (IF_insn(last_ir, jal)
#if RV32_HAS(EXT_C)
|| IF_insn(last_ir, cj) || IF_insn(last_ir, cjal)
|| IF_insn(last_ir, cj) || IF_insn(last_ir, cjal)
#endif
) {
) {
if (!last_ir->branch_taken) {
last_ir->branch_taken = block->ir_head;
#if RV32_HAS(JIT)
chain_entry_t *new_entry = mpool_alloc(rv->chain_entry_mp);
new_entry->block = prev;
list_add(&new_entry->list, &block->list);
#endif
}
}
}
Expand Down
12 changes: 6 additions & 6 deletions src/jit.c
Original file line number Diff line number Diff line change
Expand Up @@ -1496,14 +1496,14 @@ static void translate_chained_block(struct jit_state *state,
offset_map_insert(state, block->pc_start);
translate(state, rv, block);
rv_insn_t *ir = block->ir_tail;
if (ir->branch_untaken && !set_has(set, ir->pc + 4)) {
block_t *block1 = cache_get(rv->block_cache, ir->pc + 4);
if (block1 && block1->translatable)
if (ir->branch_untaken && !set_has(set, ir->branch_untaken->pc)) {
block_t *block1 = cache_get(rv->block_cache, ir->branch_untaken->pc);
if (block1->translatable)
translate_chained_block(state, rv, block1, set);
}
if (ir->branch_taken && !set_has(set, ir->pc + ir->imm)) {
block_t *block1 = cache_get(rv->block_cache, ir->pc + ir->imm);
if (block1 && block1->translatable)
if (ir->branch_taken && !set_has(set, ir->branch_taken->pc)) {
block_t *block1 = cache_get(rv->block_cache, ir->branch_taken->pc);
if (block1->translatable)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shall we check block1 before dereferencing?

Copy link
Collaborator Author

@qwe661234 qwe661234 Jan 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The block1 must be existed because we correctly handle deleted block.

translate_chained_block(state, rv, block1, set);
}
}
Expand Down
4 changes: 4 additions & 0 deletions src/riscv.c
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,9 @@ riscv_t *rv_create(const riscv_io_t *io,
/* initialize the block map */
block_map_init(&rv->block_map, BLOCK_MAP_CAPACITY_BITS);
#else
rv->chain_entry_mp =
mpool_create(sizeof(chain_entry_t) << BLOCK_IR_MAP_CAPACITY_BITS,
sizeof(chain_entry_t));
rv->jit_state = jit_state_init(CODE_CACHE_SIZE);
rv->block_cache = cache_create(BLOCK_MAP_CAPACITY_BITS);
assert(rv->block_cache);
Expand Down Expand Up @@ -165,6 +168,7 @@ void rv_delete(riscv_t *rv)
#if !RV32_HAS(JIT)
block_map_destroy(rv);
#else
mpool_destroy(rv->chain_entry_mp);
jit_state_exit(rv->jit_state);
cache_free(rv->block_cache);
#endif
Expand Down
11 changes: 10 additions & 1 deletion src/riscv_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#endif
#include "decode.h"
#include "riscv.h"
#include "utils.h"
#if RV32_HAS(JIT)
#include "cache.h"
#endif
Expand Down Expand Up @@ -59,7 +60,6 @@ enum {
typedef struct block {
uint32_t n_insn; /**< number of instructions encompased */
uint32_t pc_start, pc_end; /**< address range of the basic block */
struct block *predict; /**< block prediction */

rv_insn_t *ir_head, *ir_tail; /**< the first and last ir for this block */
bool backward;
Expand All @@ -68,9 +68,17 @@ typedef struct block {
uint32_t offset;
bool
translatable; /**< Determine the block has RV32AF insturctions or not */
struct list_head list;
#endif
} block_t;

#if RV32_HAS(JIT)
typedef struct {
block_t *block;
struct list_head list;
} chain_entry_t;
#endif

typedef struct {
uint32_t block_capacity; /**< max number of entries in the block map */
uint32_t size; /**< number of entries currently in the map */
Expand Down Expand Up @@ -117,6 +125,7 @@ struct riscv_internal {
block_map_t block_map; /**< basic block map */
#else
struct cache *block_cache;
struct mpool *chain_entry_mp;
#endif
struct mpool *block_mp, *block_ir_mp;
/* print exit code on syscall_exit */
Expand Down
Loading