mirror of
https://github.com/ish-app/ish.git
synced 2026-02-01 14:32:26 +00:00
First draft of block caching
This commit is contained in:
parent
bff119e216
commit
75e8d35381
31
emu/gen.c
31
emu/gen.c
@ -4,6 +4,37 @@
|
||||
#include "emu/cpuid.h"
|
||||
#include "emu/interrupt.h"
|
||||
|
||||
static void gen(struct gen_state *state, unsigned long thing) {
|
||||
assert(state->size <= state->capacity);
|
||||
if (state->size >= state->capacity) {
|
||||
state->capacity *= 2;
|
||||
struct jit_block *bigger_block = realloc(state->block,
|
||||
sizeof(struct jit_block) + state->capacity * sizeof(unsigned long));
|
||||
if (bigger_block == NULL) {
|
||||
println("out of memory while jitting");
|
||||
abort();
|
||||
}
|
||||
state->block = bigger_block;
|
||||
}
|
||||
assert(state->size < state->capacity);
|
||||
state->block->code[state->size++] = thing;
|
||||
}
|
||||
|
||||
void gen_start(addr_t addr, struct gen_state *state) {
|
||||
state->capacity = JIT_BLOCK_INITIAL_CAPACITY;
|
||||
state->size = 0;
|
||||
state->block = malloc(sizeof(struct jit_block) + state->capacity * sizeof(unsigned long));
|
||||
state->block->addr = addr;
|
||||
state->ip = addr;
|
||||
}
|
||||
|
||||
void gen_exit(struct gen_state *state) {
|
||||
extern void gadget_exit();
|
||||
// in case the last instruction didn't end the block
|
||||
gen(state, (unsigned long) gadget_exit);
|
||||
gen(state, state->ip);
|
||||
}
|
||||
|
||||
#define DECLARE_LOCALS \
|
||||
dword_t saved_ip = state->ip; \
|
||||
dword_t addr_offset = 0; \
|
||||
|
||||
@ -11,7 +11,9 @@ struct gen_state {
|
||||
unsigned capacity;
|
||||
};
|
||||
|
||||
void gen(struct gen_state *state, unsigned long thing);
|
||||
void gen_start(addr_t addr, struct gen_state *state);
|
||||
void gen_exit(struct gen_state *state);
|
||||
|
||||
int gen_step32(struct gen_state *state, struct tlb *tlb);
|
||||
int gen_step16(struct gen_state *state, struct tlb *tlb);
|
||||
|
||||
|
||||
133
emu/jit.c
133
emu/jit.c
@ -6,78 +6,73 @@
|
||||
#include "emu/gen.h"
|
||||
#include "kernel/calls.h"
|
||||
|
||||
struct jit *jit_new(struct mem *mem) {
|
||||
struct jit *jit = malloc(sizeof(struct jit));
|
||||
jit->mem = mem;
|
||||
for (int i = 0; i < JIT_HASH_SIZE; i++)
|
||||
list_init(&jit->hash[i]);
|
||||
return jit;
|
||||
}
|
||||
|
||||
void jit_free(struct jit *jit) {
|
||||
for (int i = 0; i < JIT_HASH_SIZE; i++) {
|
||||
struct jit_block *block, *tmp;
|
||||
list_for_each_entry_safe(&jit->hash[i], block, tmp, chain) {
|
||||
list_remove(&block->chain);
|
||||
free(block);
|
||||
}
|
||||
}
|
||||
free(jit);
|
||||
}
|
||||
|
||||
void jit_insert(struct jit *jit, struct jit_block *block) {
|
||||
list_add(&jit->hash[block->addr % JIT_HASH_SIZE], &block->chain);
|
||||
}
|
||||
|
||||
struct jit_block *jit_lookup(struct jit *jit, addr_t addr) {
|
||||
struct jit_block *block;
|
||||
list_for_each_entry(&jit->hash[addr % JIT_HASH_SIZE], block, chain) {
|
||||
if (block->addr == addr)
|
||||
return block;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void jit_block_free(struct jit_block *block) {
|
||||
free(block);
|
||||
}
|
||||
|
||||
void gen_start(addr_t addr, struct gen_state *state) {
|
||||
state->capacity = JIT_BLOCK_INITIAL_CAPACITY;
|
||||
state->size = 0;
|
||||
state->block = malloc(sizeof(struct jit_block) + state->capacity * sizeof(unsigned long));
|
||||
state->block->addr = addr;
|
||||
state->ip = addr;
|
||||
}
|
||||
|
||||
void gen_end(struct gen_state *state) {
|
||||
}
|
||||
|
||||
void gen_exit(struct gen_state *state) {
|
||||
extern void gadget_exit();
|
||||
// in case the last instruction didn't end the block
|
||||
gen(state, (unsigned long) gadget_exit);
|
||||
gen(state, state->ip);
|
||||
}
|
||||
|
||||
void gen(struct gen_state *state, unsigned long thing) {
|
||||
assert(state->size <= state->capacity);
|
||||
if (state->size >= state->capacity) {
|
||||
state->capacity *= 2;
|
||||
struct jit_block *bigger_block = realloc(state->block,
|
||||
sizeof(struct jit_block) + state->capacity * sizeof(unsigned long));
|
||||
if (bigger_block == NULL) {
|
||||
println("out of memory while jitting");
|
||||
abort();
|
||||
}
|
||||
state->block = bigger_block;
|
||||
}
|
||||
assert(state->size < state->capacity);
|
||||
state->block->code[state->size++] = thing;
|
||||
struct jit_block *jit_block_compile(addr_t ip, struct tlb *tlb) {
|
||||
struct gen_state state;
|
||||
gen_start(ip, &state);
|
||||
bool in_block = true;
|
||||
while (in_block)
|
||||
in_block = gen_step32(&state, tlb);
|
||||
return state.block;
|
||||
}
|
||||
|
||||
// assembler function
|
||||
int jit_enter(struct jit_block *block, struct cpu_state *cpu, struct tlb *tlb);
|
||||
|
||||
int cpu_step32(struct cpu_state *cpu, struct tlb *tlb) {
|
||||
struct gen_state state;
|
||||
gen_start(cpu->eip, &state);
|
||||
gen_step32(&state, tlb);
|
||||
gen_exit(&state);
|
||||
gen_end(&state);
|
||||
|
||||
struct jit_block *block = state.block;
|
||||
int interrupt = jit_enter(block, cpu, tlb);
|
||||
jit_block_free(block);
|
||||
return interrupt;
|
||||
}
|
||||
|
||||
void cpu_run(struct cpu_state *cpu) {
|
||||
struct tlb *tlb = tlb_new(cpu->mem);
|
||||
struct jit *jit = jit_new(cpu->mem);
|
||||
|
||||
int i = 0;
|
||||
struct tlb tlb = {.mem = cpu->mem};
|
||||
tlb_flush(&tlb);
|
||||
read_wrlock(&cpu->mem->lock);
|
||||
int changes = cpu->mem->changes;
|
||||
|
||||
while (true) {
|
||||
struct gen_state state;
|
||||
gen_start(cpu->eip, &state);
|
||||
bool in_block = true;
|
||||
while (in_block)
|
||||
in_block = gen_step32(&state, &tlb);
|
||||
gen_end(&state);
|
||||
struct jit_block *block = state.block;
|
||||
int interrupt = jit_enter(block, cpu, &tlb);
|
||||
jit_block_free(block);
|
||||
// look to see if we have a matching block
|
||||
// if so, (link it into the previous block and) run it
|
||||
// otherwise, make a new block and stick it in the hash
|
||||
|
||||
while (true) {
|
||||
struct jit_block *block = jit_lookup(jit, cpu->eip);
|
||||
if (block == NULL) {
|
||||
block = jit_block_compile(cpu->eip, tlb);
|
||||
jit_insert(jit, block);
|
||||
}
|
||||
int interrupt = jit_enter(block, cpu, tlb);
|
||||
if (interrupt == INT_NONE && i++ >= 100000) {
|
||||
i = 0;
|
||||
interrupt = INT_TIMER;
|
||||
@ -87,21 +82,25 @@ void cpu_run(struct cpu_state *cpu) {
|
||||
read_wrunlock(&cpu->mem->lock);
|
||||
handle_interrupt(interrupt);
|
||||
read_wrlock(&cpu->mem->lock);
|
||||
if (tlb.mem != cpu->mem)
|
||||
tlb.mem = cpu->mem;
|
||||
if (tlb->mem != cpu->mem)
|
||||
tlb->mem = cpu->mem;
|
||||
if (cpu->mem->changes != changes) {
|
||||
tlb_flush(&tlb);
|
||||
tlb_flush(tlb);
|
||||
changes = cpu->mem->changes;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// these functions are never used, but will be someday
|
||||
struct jit *jit_new(struct mem *mem) {
|
||||
struct jit *jit = malloc(sizeof(struct jit));
|
||||
return jit;
|
||||
}
|
||||
void jit_free(struct jit *jit) {
|
||||
free(jit);
|
||||
// really only here for ptraceomatic
|
||||
int cpu_step32(struct cpu_state *cpu, struct tlb *tlb) {
|
||||
struct gen_state state;
|
||||
gen_start(cpu->eip, &state);
|
||||
gen_step32(&state, tlb);
|
||||
gen_exit(&state);
|
||||
|
||||
struct jit_block *block = state.block;
|
||||
int interrupt = jit_enter(block, cpu, tlb);
|
||||
jit_block_free(block);
|
||||
return interrupt;
|
||||
}
|
||||
|
||||
@ -8,12 +8,15 @@
|
||||
struct jit {
|
||||
// there is one jit per address space
|
||||
struct mem *mem;
|
||||
struct list hash[JIT_HASH_SIZE];
|
||||
};
|
||||
|
||||
// this is roughly the average number of instructions in a basic block according to anonymous sources
|
||||
#define JIT_BLOCK_INITIAL_CAPACITY 8
|
||||
// times 4, roughly the average number of gadgets/parameters in an instruction
|
||||
#define JIT_BLOCK_INITIAL_CAPACITY 32
|
||||
|
||||
struct jit_block {
|
||||
struct list chain;
|
||||
addr_t addr;
|
||||
unsigned long code[];
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user