This commit is contained in:
Fabian 2018-01-03 13:53:21 -06:00
parent 30f7442d4d
commit 5e21d68d83
5 changed files with 45 additions and 51 deletions

View file

@ -245,7 +245,6 @@ function V86Starter(options)
"_get_time": () => Date.now(),
"_codegen_finalize": (cache_index, virt_start, start, end) => cpu.codegen_finalize(cache_index, virt_start, start, end),
"_codegen_call_cache": (start) => cpu.codegen_call_cache(start),
};
let wasm_file = DEBUG ? "v86-debug.wasm" : "v86.wasm";

View file

@ -20,10 +20,6 @@ function CPU(bus, wm, codegen)
this.memory_size = new Uint32Array(wm.memory.buffer, 812, 1);
// XXX: Replace with wasm table
// XXX: Not garbage collected currently
this.instr_cache = Object.create(null);
// Note: Currently unused (degrades performance and not required by any OS
// that we support)
this.a20_enabled = new Int32Array(wm.memory.buffer, 552, 1);
@ -1375,22 +1371,11 @@ CPU.prototype.run_instruction_0f = function()
var seen_code = {};
CPU.prototype.codegen_call_cache = function(start)
{
//const before = this.instruction_pointer[0];
//dbg_log("calling cached generated code at " + h(before));
this.instr_cache[start]();
//const after = this.instruction_pointer[0];
//dbg_log("cached code block from " + h(before) + " to " + h(after));
};
CPU.prototype.codegen_finalize = function(cache_index, virtual_start, start, end)
{
dbg_log("finalize");
//dbg_log("finalize");
const code = this.codegen.get_module_code();
//this.debug.dump_wasm(code);
let module;
if(DEBUG)
@ -1401,9 +1386,17 @@ CPU.prototype.codegen_finalize = function(cache_index, virtual_start, start, end
seen_code[start] = true;
const buffer = new Uint8Array(end - start + 1);
if((start ^ end) & ~0xFFF)
{
dbg_log("truncated disassembly");
end = (start | 0xFFF) + 1; // until the end of the page
}
for(let i = start; i < end + 1; i++)
dbg_assert(end >= start);
const buffer = new Uint8Array(end - start);
for(let i = start; i < end; i++)
{
buffer[i - start] = this.read8(i);
}
@ -1433,19 +1426,17 @@ CPU.prototype.codegen_finalize = function(cache_index, virtual_start, start, end
const instance = new WebAssembly.Instance(module, this.jit_imports);
const f = instance.exports["f"];
this.instr_cache[start] = f;
// The following will throw if o.exports.f isn't an exported function
this.wm.imports["env"].table.set(cache_index, f);
this.instruction_pointer[0] = virtual_start;
const before = this.instruction_pointer[0];
dbg_log("calling generated code at " + h(before >>> 0));
//const before = this.instruction_pointer[0];
//dbg_log("calling generated code at " + h(before >>> 0));
//debugger;
f();
const after = this.instruction_pointer[0];
dbg_log("code block from " + h(before >>> 0) + " to " + h(after >>> 0));
//const after = this.instruction_pointer[0];
//dbg_log("code block from " + h(before >>> 0) + " to " + h(after >>> 0));
};
CPU.prototype.dbg_log = function()

View file

@ -162,14 +162,15 @@
#define MXCSR_MASK (0xFFFF & ~(1 << 6))
// Mask used to map physical address to index in cache array
#define JIT_PHYS_MASK 0xFFFF
#define WASM_TABLE_SIZE 0x10000
// Mask used to map physical address to index in cache array
#define JIT_PHYS_MASK (WASM_TABLE_SIZE - 1)
#define HASH_PRIME 6151
#define JIT_THRESHOLD 10000
// XXX: Consider making this the same as page size (12) during perf testing
#define DIRTY_ARR_SHIFT 16
#define DIRTY_ARR_SHIFT 12
#define MAX_INSTR_LEN 15
#define MAX_BLOCK_LENGTH ((1 << DIRTY_ARR_SHIFT) - MAX_INSTR_LEN)

View file

@ -267,7 +267,7 @@ void generate_instruction(int32_t opcode)
int32_t instruction_length = end_eip - start_eip;
assert(instruction_length >= 0 && instruction_length < 16);
dbg_log("instruction_length=%d", instruction_length);
//dbg_log("instruction_length=%d", instruction_length);
gen_patch_increment_instruction_pointer(instruction_length);
}
@ -296,6 +296,7 @@ void cycle_internal()
const bool JIT_ALWAYS = false;
const bool JIT_DONT_USE_CACHE = false;
const bool JIT_COMPILE_ONLY_AFTER_JUMP = true;
if(cached && !clean)
{
@ -323,6 +324,7 @@ void cycle_internal()
run_instruction(entry->opcode[i] | !!*is_32 << 8);
(*timestamp_counter)++;
}*/
assert(entry->opcode[0] == read8(phys_addr));
//codegen_call_cache(phys_addr);
call_indirect(addr_index);
@ -333,9 +335,7 @@ void cycle_internal()
profiler_end(P_RUN_FROM_CACHE);
}
// A jump just occured indicating the start of a basic block + the
// address is hot; let's JIT compile it
else if(JIT_ALWAYS || jit_jump == 1 && ++hot_code_addresses[jit_hot_hash(phys_addr)] > JIT_THRESHOLD)
else if(JIT_ALWAYS || (!JIT_COMPILE_ONLY_AFTER_JUMP || jit_jump == 1) && ++hot_code_addresses[jit_hot_hash(phys_addr)] > JIT_THRESHOLD)
{
if(clean && entry->start_addr != 0 && entry->start_addr != phys_addr)
{
@ -353,10 +353,14 @@ void cycle_internal()
// Minimize collision based thrashing
hot_code_addresses[jit_hot_hash(phys_addr)] = 0;
// invalidate now, in case generate_instruction raises
entry->group_status = group_dirtiness[phys_addr >> DIRTY_ARR_SHIFT] + 1;
int32_t len = 0;
jit_jump = 0;
entry->len = 0;
entry->start_addr = phys_addr;
entry->end_addr = phys_addr + 1;
int32_t end_addr = phys_addr + 1;
entry->is_32 = *is_32;
//jit_cache_arr[addr_index] = *entry;
@ -367,37 +371,37 @@ void cycle_internal()
// XXX: Artificial limit allows jit_dirty_cache to be
// simplified by only dirtying 2 entries based on a mask
// (instead of all possible entries)
while(jit_jump == 0 && entry->len < 100 &&
(entry->end_addr - entry->start_addr) < MAX_BLOCK_LENGTH)
while(jit_jump == 0 && len < 50 &&
(end_addr - entry->start_addr) < MAX_BLOCK_LENGTH)
{
*previous_ip = *instruction_pointer;
int32_t opcode = read_imm8();
entry->opcode[entry->len] = opcode;
entry->len++;
if(len == 0)
{
entry->opcode[0] = opcode;
}
len++;
generate_instruction(opcode | !!*is_32 << 8);
entry->end_addr = *eip_phys ^ *instruction_pointer;
end_addr = *eip_phys ^ *instruction_pointer;
}
gen_increment_timestamp_counter(entry->len);
gen_increment_timestamp_counter(len);
entry->len = len;
jit_jump = 0;
gen_finish();
jit_in_progress = false;
codegen_finalize(addr_index, start_addr, entry->start_addr, entry->end_addr);
codegen_finalize(addr_index, start_addr, entry->start_addr, end_addr);
assert(*prefixes == 0);
// When the hot instruction is a jmp (backwards),
// leave its group_status unupdated, thereby invalidating it
//if (entry->end_addr > entry->start_addr)
//{
entry->group_status = group_dirtiness[phys_addr >> DIRTY_ARR_SHIFT];
//}
profiler_stat_increment(S_COMPILE_SUCCESS);
profiler_end(P_GEN_INSTR);
@ -438,7 +442,7 @@ static void run_prefix_instruction()
static void jit_prefix_instruction()
{
dbg_log("jit_prefix_instruction is32=%d", is_osize_32());
//dbg_log("jit_prefix_instruction is32=%d", is_osize_32());
jit_instruction(read_imm8() | is_osize_32() << 8);
}

View file

@ -33,15 +33,14 @@ struct code_cache {
// Address of the start of the basic block
uint32_t start_addr;
// Address of the instruction immediately after the basic block ends
uint32_t end_addr;
int32_t opcode[100];
int32_t opcode[1]; // TODO: Remove in debug mode
int32_t len;
int32_t is_32;
// Cleanliness status of the entry's "group" (based on
// DIRTY_ARR_SHIFT). Value only has meaning in relation with the
// group_dirtiness value.
uint32_t group_status;
} jit_cache_arr[WASM_TABLE_SIZE] = {{0, 0, {0}, 0, 0, 0}};
} jit_cache_arr[WASM_TABLE_SIZE] = {{0, {0}, 0, 0, 0}};
// Flag indicating whether the instruction that just ran was a jump of some sort
uint32_t jit_jump = 0;