Skip to content

Commit

Permalink
Introduce baseline JIT compiler
Browse files Browse the repository at this point in the history
The baseline JIT compiler design involves tracing an EBB when its usage
frequency exceeds a predetermined threshold. We then utilize a code
generator to convert the instruction sequence into C code. The C code is
subsequently compiled using mir, and the resulting target machine code
is stored in the code cache for future utilization.

The primary objective of introducing the baseline JIT compiler is to
enhance the execution speed of RISC-V instructions with minimal
modification. This implementation requires three additional components:
a code generator, mir compiler, and code cache, while maintaining the
original design of the interpreter. Furthermore, this baseline JIT
compiler serves as the foundational target for future improvements.

In addition, we have developed a Python script that effectively traces
code templates and automatically generates JIT code templates. This
approach eliminates the need for manually writing duplicated codey.
  • Loading branch information
qwe661234 committed Oct 18, 2023
1 parent a36f35e commit 59622a8
Show file tree
Hide file tree
Showing 14 changed files with 1,388 additions and 253 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ build/map/
tests/**/*.elf
tests/arch-test-target/config.ini
__pycache__/
src/jit_template.c
23 changes: 22 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,27 @@ gdbstub-test: $(BIN)
$(Q).ci/gdbstub-test.sh && $(call notice, [OK])
endif

ENABLE_JIT ?= 0
$(call set-feature, JIT)
ifeq ($(call has, JIT), 1)
CFLAGS += -I./src/mir
LDFLAGS += src/mir/libmir.a -lpthread
OBJS_EXT += compile.o

src/jit_template.c:
$(Q)tools/gen-jit-template.py $(CFLAGS) > $@

src/mir/GNUmakefile:
git submodule update --init $(dir $@)

src/mir/libmir.a: src/mir/GNUmakefile
$(MAKE) --quiet -C src/mir

$(OUT)/compile.o: src/compile.c src/mir/libmir.a src/jit_template.c
$(VECHO) " CC\t$@\n"
$(Q)$(CC) -o $@ $(CFLAGS) -c -MMD -MF $@.d $<
endif

# For tail-call elimination, we need a specific set of build flags applied.
# FIXME: On macOS + Apple Silicon, -fno-stack-protector might have a negative impact.
$(OUT)/emulate.o: CFLAGS += $(CFLAGS_NO_CET) -foptimize-sibling-calls -fomit-frame-pointer -fno-stack-check -fno-stack-protector
Expand Down Expand Up @@ -182,7 +203,7 @@ endif
endif

clean:
$(RM) $(BIN) $(OBJS) $(HIST_BIN) $(HIST_OBJS) $(deps) $(CACHE_OUT)
$(RM) $(BIN) $(OBJS) $(HIST_BIN) $(HIST_OBJS) $(deps) $(CACHE_OUT) src/jit_template.c
distclean: clean
-$(RM) $(DOOM_DATA) $(QUAKE_DATA)
$(RM) -r $(OUT)/id1
Expand Down
1 change: 1 addition & 0 deletions mk/tools.mk
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ HIST_BIN := $(OUT)/rv_histogram
# FIXME: riscv.o and map.o are dependencies of 'elf.o', not 'rv_histogram'.
HIST_OBJS := \
riscv.o \
utils.o \
map.o \
elf.o \
decode.o \
Expand Down
43 changes: 42 additions & 1 deletion src/cache.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@
/* THRESHOLD is set to identify hot spots. Once the frequency of use for a block
* exceeds the THRESHOLD, the JIT compiler flow is triggered.
*/
#define THRESHOLD 1000
#define THRESHOLD 32768
#if RV32_HAS(JIT)
#define sys_icache_invalidate(addr, size) \
__builtin___clear_cache((char *) (addr), (char *) (addr) + (size));
#endif

static uint32_t cache_size, cache_size_bits;
static struct mpool *cache_mp;
Expand Down Expand Up @@ -545,3 +549,40 @@ void cache_free(cache_t *cache, void (*callback)(void *))
free(cache->map);
free(cache);
}

#if RV32_HAS(JIT)
bool cache_hot(struct cache *cache, uint32_t key)
{
if (!cache->capacity ||
hlist_empty(&cache->map->ht_list_head[cache_hash(key)]))
return false;
#if RV32_HAS(ARC)
arc_entry_t *entry = NULL;
#ifdef __HAVE_TYPEOF
hlist_for_each_entry (entry, &cache->map->ht_list_head[cache_hash(key)],
ht_list)
#else
hlist_for_each_entry (entry, &cache->map->ht_list_head[cache_hash(key)],
ht_list, arc_entry_t)
#endif
{
if (entry->key == key && entry->frequency == THRESHOLD)
return true;
}
#else
lfu_entry_t *entry = NULL;
#ifdef __HAVE_TYPEOF
hlist_for_each_entry (entry, &cache->map->ht_list_head[cache_hash(key)],
ht_list)
#else
hlist_for_each_entry (entry, &cache->map->ht_list_head[cache_hash(key)],
ht_list, lfu_entry_t)
#endif
{
if (entry->key == key && entry->frequency == THRESHOLD)
return true;
}
#endif
return false;
}
#endif
11 changes: 11 additions & 0 deletions src/cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#pragma once

#include <stdbool.h>
#include <stdint.h>

struct cache;
Expand Down Expand Up @@ -38,3 +39,13 @@ void *cache_put(struct cache *cache, uint32_t key, void *value);
* @callback: a function for freeing cache entry completely
*/
void cache_free(struct cache *cache, void (*callback)(void *));

#if RV32_HAS(JIT)
/**
* cache_hot - check whether the frequency of the cache entry exceeds the
* threshold or not
* @cache: a pointer points to target cache
* @key: the key of the specified entry
*/
bool cache_hot(struct cache *cache, uint32_t key);
#endif
Loading

0 comments on commit 59622a8

Please sign in to comment.