nit fixes
This commit is contained in:
parent
74e6500bf1
commit
85a67caaa7
2
Makefile
2
Makefile
|
@ -175,7 +175,7 @@ build/v86-debug.wasm: src/native/*.c src/native/*.h src/native/codegen/*.c src/n
|
||||||
|
|
||||||
build/codegen-test.wasm: src/native/*.c src/native/*.h src/native/codegen/*.c src/native/codegen/*.h
|
build/codegen-test.wasm: src/native/*.c src/native/*.h src/native/codegen/*.c src/native/codegen/*.h
|
||||||
mkdir -p build
|
mkdir -p build
|
||||||
emcc src/native/codegen/api.c src/native/codegen/codegen.c \
|
emcc src/native/codegen/codegen.c \
|
||||||
-Isrc/native/ \
|
-Isrc/native/ \
|
||||||
-Wall -Wpedantic -Wextra \
|
-Wall -Wpedantic -Wextra \
|
||||||
-Wno-bitwise-op-parentheses -Wno-gnu-binary-literal \
|
-Wno-bitwise-op-parentheses -Wno-gnu-binary-literal \
|
||||||
|
|
|
@ -1,339 +0,0 @@
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
#include "const.h"
|
|
||||||
#include "wasm_opcodes.h"
|
|
||||||
#include "util.h"
|
|
||||||
#include "codegen.h"
|
|
||||||
#include "wasm_util.h"
|
|
||||||
|
|
||||||
// location in memory where we store the result of the computation for testing
|
|
||||||
#define RESULT_LOC 1600
|
|
||||||
|
|
||||||
extern bool is_asize_32(void);
|
|
||||||
extern int32_t read_imm8();
|
|
||||||
extern int32_t read_imm8s();
|
|
||||||
extern int32_t read_imm16();
|
|
||||||
extern int32_t read_imm32s();
|
|
||||||
extern int32_t get_fn_index(char* fn, uint8_t fn_len, uint8_t type_index);
|
|
||||||
|
|
||||||
extern int32_t* const instruction_pointer;
|
|
||||||
extern int32_t* const previous_ip;
|
|
||||||
extern uint8_t* const reg8;
|
|
||||||
extern uint16_t* const reg16;
|
|
||||||
extern int8_t* const reg8s;
|
|
||||||
extern int16_t* const reg16s;
|
|
||||||
extern int32_t* const reg32s;
|
|
||||||
extern Writer cs;
|
|
||||||
|
|
||||||
static void jit_resolve_modrm32_(int32_t);
|
|
||||||
static void jit_resolve_modrm16_(int32_t);
|
|
||||||
|
|
||||||
void gen_increment_instruction_pointer(int32_t n)
|
|
||||||
{
|
|
||||||
push_i32(&cs, (int32_t)instruction_pointer); // store address of ip
|
|
||||||
|
|
||||||
load_i32(&cs, (int32_t)instruction_pointer); // load ip
|
|
||||||
push_i32(&cs, n); // load value to add to it
|
|
||||||
add_i32(&cs);
|
|
||||||
|
|
||||||
store_i32(&cs); // store it back in
|
|
||||||
}
|
|
||||||
|
|
||||||
void gen_set_previous_eip()
|
|
||||||
{
|
|
||||||
push_i32(&cs, (int32_t)previous_ip); // store address of previous ip
|
|
||||||
load_i32(&cs, (int32_t)instruction_pointer); // load ip
|
|
||||||
store_i32(&cs); // store it as previous ip
|
|
||||||
}
|
|
||||||
|
|
||||||
void gen_fn0(char* fn, uint8_t fn_len)
|
|
||||||
{
|
|
||||||
int32_t fn_idx = get_fn_index(fn, fn_len, FN0_TYPE_INDEX);
|
|
||||||
call_fn(&cs, fn_idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
void gen_fn1(char* fn, uint8_t fn_len, int32_t arg0)
|
|
||||||
{
|
|
||||||
int32_t fn_idx = get_fn_index(fn, fn_len, FN1_TYPE_INDEX);
|
|
||||||
push_i32(&cs, arg0);
|
|
||||||
call_fn(&cs, fn_idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
void gen_fn2(char* fn, uint8_t fn_len, int32_t arg0, int32_t arg1)
|
|
||||||
{
|
|
||||||
int32_t fn_idx = get_fn_index(fn, fn_len, FN2_TYPE_INDEX);
|
|
||||||
push_i32(&cs, arg0);
|
|
||||||
push_i32(&cs, arg1);
|
|
||||||
call_fn(&cs, fn_idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
void gen_drop()
|
|
||||||
{
|
|
||||||
write_raw_u8(&cs, OP_DROP);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define MODRM_ENTRY(n, work)\
|
|
||||||
case (n) | 0 << 3:\
|
|
||||||
case (n) | 1 << 3:\
|
|
||||||
case (n) | 2 << 3:\
|
|
||||||
case (n) | 3 << 3:\
|
|
||||||
case (n) | 4 << 3:\
|
|
||||||
case (n) | 5 << 3:\
|
|
||||||
case (n) | 6 << 3:\
|
|
||||||
case (n) | 7 << 3:\
|
|
||||||
work; break;
|
|
||||||
|
|
||||||
#define MODRM_ENTRY16_0(row, seg, reg1, reg2)\
|
|
||||||
MODRM_ENTRY(0x00 | row, gen_modrm_entry_0(seg, reg1, reg2, 0))\
|
|
||||||
MODRM_ENTRY(0x40 | row, gen_modrm_entry_0(seg, reg1, reg2, read_imm8s()))\
|
|
||||||
MODRM_ENTRY(0x80 | row, gen_modrm_entry_0(seg, reg1, reg2, read_imm16()))
|
|
||||||
|
|
||||||
#define MODRM_ENTRY16_1(row, seg, reg)\
|
|
||||||
MODRM_ENTRY(0x00 | row, gen_modrm_entry_1(seg, reg, 0))\
|
|
||||||
MODRM_ENTRY(0x40 | row, gen_modrm_entry_1(seg, reg, read_imm8s()))\
|
|
||||||
MODRM_ENTRY(0x80 | row, gen_modrm_entry_1(seg, reg, read_imm16()))
|
|
||||||
|
|
||||||
static void inline gen_modrm_entry_0(int32_t fn_idx, int32_t reg16_idx_1, int32_t reg16_idx_2, int32_t imm)
|
|
||||||
{
|
|
||||||
// generates: fn( ( reg1 + reg2 + imm ) & 0xFFFF )
|
|
||||||
load_u16(&cs, reg16_idx_1);
|
|
||||||
load_u16(&cs, reg16_idx_2);
|
|
||||||
add_i32(&cs);
|
|
||||||
|
|
||||||
push_i32(&cs, imm);
|
|
||||||
add_i32(&cs);
|
|
||||||
|
|
||||||
push_i32(&cs, 0xFFFF);
|
|
||||||
and_i32(&cs);
|
|
||||||
|
|
||||||
call_fn(&cs, fn_idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void gen_modrm_entry_1(int32_t fn_idx, int32_t reg16_idx, int32_t imm)
|
|
||||||
{
|
|
||||||
// generates: fn ( ( reg + imm ) & 0xFFFF )
|
|
||||||
load_u16(&cs, reg16_idx);
|
|
||||||
push_i32(&cs, imm);
|
|
||||||
add_i32(&cs);
|
|
||||||
|
|
||||||
push_i32(&cs, 0xFFFF);
|
|
||||||
and_i32(&cs);
|
|
||||||
|
|
||||||
call_fn(&cs, fn_idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void jit_resolve_modrm16_(int32_t modrm_byte)
|
|
||||||
{
|
|
||||||
int32_t const ds = fn_get_seg_prefix_ds_idx;
|
|
||||||
int32_t const ss = fn_get_seg_prefix_ss_idx;
|
|
||||||
|
|
||||||
switch(modrm_byte)
|
|
||||||
{
|
|
||||||
// The following casts cause some weird issue with emscripten and cause
|
|
||||||
// a performance hit. XXX: look into this later.
|
|
||||||
MODRM_ENTRY16_0(0, ds, (int32_t)(reg16 + BX), (int32_t)(reg16 + SI))
|
|
||||||
MODRM_ENTRY16_0(1, ds, (int32_t)(reg16 + BX), (int32_t)(reg16 + DI))
|
|
||||||
MODRM_ENTRY16_0(2, ss, (int32_t)(reg16 + BP), (int32_t)(reg16 + SI))
|
|
||||||
MODRM_ENTRY16_0(3, ss, (int32_t)(reg16 + BP), (int32_t)(reg16 + DI))
|
|
||||||
MODRM_ENTRY16_1(4, ds, (int32_t)(reg16 + SI))
|
|
||||||
MODRM_ENTRY16_1(5, ds, (int32_t)(reg16 + DI))
|
|
||||||
|
|
||||||
// special case
|
|
||||||
MODRM_ENTRY(0x00 | 6, call_fn_with_arg(&cs, ds, read_imm16()))
|
|
||||||
MODRM_ENTRY(0x40 | 6, gen_modrm_entry_1(ss, (int32_t)(reg16 + BP), read_imm8s()))
|
|
||||||
MODRM_ENTRY(0x80 | 6, gen_modrm_entry_1(ss, (int32_t)(reg16 + BP), read_imm16()))
|
|
||||||
|
|
||||||
MODRM_ENTRY16_1(7, ds, (int32_t)(reg16 + BX))
|
|
||||||
|
|
||||||
default:
|
|
||||||
assert(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void gen_resolve_modrm16(int32_t modrm_byte)
|
|
||||||
{
|
|
||||||
push_u32(&cs, RESULT_LOC);
|
|
||||||
jit_resolve_modrm16_(modrm_byte);
|
|
||||||
store_i32(&cs);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define MODRM_ENTRY32_0(row, seg, reg)\
|
|
||||||
MODRM_ENTRY(0x00 | row, gen_modrm32_entry(seg, reg, 0))\
|
|
||||||
MODRM_ENTRY(0x40 | row, gen_modrm32_entry(seg, reg, read_imm8s()))\
|
|
||||||
MODRM_ENTRY(0x80 | row, gen_modrm32_entry(seg, reg, read_imm32s()))
|
|
||||||
|
|
||||||
static void gen_modrm32_entry(int32_t fn_idx, int32_t reg32s_idx, int32_t imm)
|
|
||||||
{
|
|
||||||
// generates: fn ( reg + imm )
|
|
||||||
load_i32(&cs, reg32s_idx);
|
|
||||||
push_i32(&cs, imm);
|
|
||||||
add_i32(&cs);
|
|
||||||
|
|
||||||
call_fn(&cs, fn_idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void jit_resolve_sib(bool mod)
|
|
||||||
{
|
|
||||||
uint8_t sib_byte = read_imm8();
|
|
||||||
uint8_t r = sib_byte & 7;
|
|
||||||
uint8_t m = sib_byte >> 3 & 7;
|
|
||||||
|
|
||||||
int32_t base_addr;
|
|
||||||
int32_t base;
|
|
||||||
uint8_t seg;
|
|
||||||
bool base_is_mem_access = true;
|
|
||||||
|
|
||||||
if(r == 4)
|
|
||||||
{
|
|
||||||
base_addr = (int32_t)(reg32s + ESP);
|
|
||||||
seg = SS;
|
|
||||||
}
|
|
||||||
else if(r == 5)
|
|
||||||
{
|
|
||||||
if(mod)
|
|
||||||
{
|
|
||||||
base_addr = (int32_t)(reg32s + EBP);
|
|
||||||
seg = SS;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
base = read_imm32s();
|
|
||||||
seg = DS;
|
|
||||||
base_is_mem_access = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
base_addr = (int32_t)(reg32s + r);
|
|
||||||
seg = DS;
|
|
||||||
}
|
|
||||||
|
|
||||||
// generate: get_seg_prefix(seg) + base
|
|
||||||
// Where base is accessed from memory if base_is_mem_access or written as a constant otherwise
|
|
||||||
|
|
||||||
dbg_assert(seg < 16);
|
|
||||||
write_raw_u8(&cs, OP_I32CONST);
|
|
||||||
write_raw_u8(&cs, seg);
|
|
||||||
|
|
||||||
call_fn(&cs, fn_get_seg_prefix_idx);
|
|
||||||
|
|
||||||
if(base_is_mem_access)
|
|
||||||
{
|
|
||||||
load_i32(&cs, base_addr);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
push_i32(&cs, base);
|
|
||||||
}
|
|
||||||
|
|
||||||
add_i32(&cs);
|
|
||||||
|
|
||||||
// We now have to generate an offset value to add
|
|
||||||
|
|
||||||
if(m == 4)
|
|
||||||
{
|
|
||||||
// offset is 0, we don't need to add anything
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Offset is reg32s[m] << s, where s is:
|
|
||||||
|
|
||||||
uint8_t s = sib_byte >> 6 & 3;
|
|
||||||
|
|
||||||
load_i32(&cs, (int32_t)(reg32s + m));
|
|
||||||
// We don't use push_u32 here either since s will fit in 1 byte
|
|
||||||
write_raw_u8(&cs, OP_I32CONST);
|
|
||||||
write_raw_u8(&cs, s);
|
|
||||||
shl_i32(&cs);
|
|
||||||
|
|
||||||
add_i32(&cs);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void modrm32_special_case_1()
|
|
||||||
{
|
|
||||||
jit_resolve_sib(true);
|
|
||||||
push_i32(&cs, read_imm8s());
|
|
||||||
add_i32(&cs);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void modrm32_special_case_2()
|
|
||||||
{
|
|
||||||
jit_resolve_sib(true);
|
|
||||||
push_i32(&cs, read_imm32s());
|
|
||||||
add_i32(&cs);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void jit_resolve_modrm32_(int32_t modrm_byte)
|
|
||||||
{
|
|
||||||
int32_t const ds = fn_get_seg_prefix_ds_idx;
|
|
||||||
int32_t const ss = fn_get_seg_prefix_ss_idx;
|
|
||||||
|
|
||||||
switch(modrm_byte)
|
|
||||||
{
|
|
||||||
MODRM_ENTRY32_0(0, ds, (int32_t)(reg32s + EAX))
|
|
||||||
MODRM_ENTRY32_0(1, ds, (int32_t)(reg32s + ECX))
|
|
||||||
MODRM_ENTRY32_0(2, ds, (int32_t)(reg32s + EDX))
|
|
||||||
MODRM_ENTRY32_0(3, ds, (int32_t)(reg32s + EBX))
|
|
||||||
|
|
||||||
// special cases
|
|
||||||
MODRM_ENTRY(0x00 | 4, jit_resolve_sib(false))
|
|
||||||
MODRM_ENTRY(0x40 | 4, modrm32_special_case_1())
|
|
||||||
MODRM_ENTRY(0x80 | 4, modrm32_special_case_2())
|
|
||||||
MODRM_ENTRY(0x00 | 5, call_fn_with_arg(&cs, ds, read_imm32s()))
|
|
||||||
MODRM_ENTRY(0x40 | 5, gen_modrm32_entry(ss, (int32_t)(reg32s + EBP), read_imm8s()))
|
|
||||||
MODRM_ENTRY(0x80 | 5, gen_modrm32_entry(ss, (int32_t)(reg32s + EBP), read_imm32s()))
|
|
||||||
|
|
||||||
MODRM_ENTRY32_0(6, ds, (int32_t)(reg32s + ESI))
|
|
||||||
MODRM_ENTRY32_0(7, ds, (int32_t)(reg32s + EDI))
|
|
||||||
|
|
||||||
default:
|
|
||||||
assert(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void gen_resolve_modrm32(int32_t modrm_byte)
|
|
||||||
{
|
|
||||||
push_i32(&cs, RESULT_LOC);
|
|
||||||
jit_resolve_modrm32_(modrm_byte);
|
|
||||||
store_i32(&cs);
|
|
||||||
}
|
|
||||||
|
|
||||||
#undef MODRM_ENTRY
|
|
||||||
|
|
||||||
void gen_modrm_fn1(char* fn, uint8_t fn_len, int32_t modrm_byte, int32_t arg0)
|
|
||||||
{
|
|
||||||
// generates: fn( modrm_resolve( modrm_byte ), arg0 )
|
|
||||||
if(is_asize_32())
|
|
||||||
{
|
|
||||||
jit_resolve_modrm32_(modrm_byte);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
jit_resolve_modrm16_(modrm_byte);
|
|
||||||
}
|
|
||||||
|
|
||||||
push_i32(&cs, arg0);
|
|
||||||
|
|
||||||
int32_t fn_idx = get_fn_index(fn, fn_len, FN2_RET_TYPE_INDEX);
|
|
||||||
call_fn(&cs, fn_idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
void gen_modrm_fn0(char* fn, uint8_t fn_len, int32_t modrm_byte)
|
|
||||||
{
|
|
||||||
// generates: fn( modrm_resolve( modrm_byte ) )
|
|
||||||
if(is_asize_32())
|
|
||||||
{
|
|
||||||
jit_resolve_modrm32_(modrm_byte);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
jit_resolve_modrm16_(modrm_byte);
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t fn_idx = get_fn_index(fn, fn_len, FN1_RET_TYPE_INDEX);
|
|
||||||
call_fn(&cs, fn_idx);
|
|
||||||
}
|
|
||||||
|
|
|
@ -7,14 +7,35 @@
|
||||||
#include "wasm_opcodes.h"
|
#include "wasm_opcodes.h"
|
||||||
#include "codegen.h"
|
#include "codegen.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
#include "wasm_util.h"
|
||||||
#include "module_init.h"
|
#include "module_init.h"
|
||||||
|
|
||||||
static Writer op = { .start = (uint8_t* const) 2048, .ptr = (uint8_t*) 2048, .len = 1024 };
|
static Buffer op = { .start = (uint8_t* const) 2048, .ptr = (uint8_t*) 2048, .len = 1024 };
|
||||||
Writer cs = { .start = (uint8_t* const) 3072, .ptr = (uint8_t*) 3072, .len = 1024 };
|
static Buffer cs = { .start = (uint8_t* const) 3072, .ptr = (uint8_t*) 3072, .len = 1024 };
|
||||||
|
|
||||||
|
// location in memory where we store the result of the computation for testing
|
||||||
|
#define RESULT_LOC 1600
|
||||||
|
|
||||||
|
extern bool is_asize_32(void);
|
||||||
|
extern int32_t read_imm8();
|
||||||
|
extern int32_t read_imm8s();
|
||||||
|
extern int32_t read_imm16();
|
||||||
|
extern int32_t read_imm32s();
|
||||||
|
extern int32_t get_fn_index(char* fn, uint8_t fn_len, uint8_t type_index);
|
||||||
|
|
||||||
|
extern int32_t* const instruction_pointer;
|
||||||
|
extern int32_t* const previous_ip;
|
||||||
|
extern uint8_t* const reg8;
|
||||||
|
extern uint16_t* const reg16;
|
||||||
|
extern int8_t* const reg8s;
|
||||||
|
extern int16_t* const reg16s;
|
||||||
|
extern int32_t* const reg32s;
|
||||||
|
|
||||||
static uint8_t* op_ptr_reset_location;
|
static uint8_t* op_ptr_reset_location;
|
||||||
static uint32_t import_table_size_reset_value;
|
static uint32_t import_table_size_reset_value;
|
||||||
static uint32_t initial_import_count;
|
static uint32_t initial_import_count;
|
||||||
|
static void jit_resolve_modrm32_(int32_t);
|
||||||
|
static void jit_resolve_modrm16_(int32_t);
|
||||||
|
|
||||||
void gen_init()
|
void gen_init()
|
||||||
{
|
{
|
||||||
|
@ -94,3 +115,309 @@ uintptr_t gen_get_final_offset()
|
||||||
return (uintptr_t) op.ptr;
|
return (uintptr_t) op.ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void gen_increment_instruction_pointer(int32_t n)
|
||||||
|
{
|
||||||
|
push_i32(&cs, (int32_t)instruction_pointer); // store address of ip
|
||||||
|
|
||||||
|
load_i32(&cs, (int32_t)instruction_pointer); // load ip
|
||||||
|
push_i32(&cs, n); // load value to add to it
|
||||||
|
add_i32(&cs);
|
||||||
|
|
||||||
|
store_i32(&cs); // store it back in
|
||||||
|
}
|
||||||
|
|
||||||
|
void gen_set_previous_eip()
|
||||||
|
{
|
||||||
|
push_i32(&cs, (int32_t)previous_ip); // store address of previous ip
|
||||||
|
load_i32(&cs, (int32_t)instruction_pointer); // load ip
|
||||||
|
store_i32(&cs); // store it as previous ip
|
||||||
|
}
|
||||||
|
|
||||||
|
void gen_fn0(char* fn, uint8_t fn_len)
|
||||||
|
{
|
||||||
|
int32_t fn_idx = get_fn_index(fn, fn_len, FN0_TYPE_INDEX);
|
||||||
|
call_fn(&cs, fn_idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gen_fn1(char* fn, uint8_t fn_len, int32_t arg0)
|
||||||
|
{
|
||||||
|
int32_t fn_idx = get_fn_index(fn, fn_len, FN1_TYPE_INDEX);
|
||||||
|
push_i32(&cs, arg0);
|
||||||
|
call_fn(&cs, fn_idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gen_fn2(char* fn, uint8_t fn_len, int32_t arg0, int32_t arg1)
|
||||||
|
{
|
||||||
|
int32_t fn_idx = get_fn_index(fn, fn_len, FN2_TYPE_INDEX);
|
||||||
|
push_i32(&cs, arg0);
|
||||||
|
push_i32(&cs, arg1);
|
||||||
|
call_fn(&cs, fn_idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gen_drop()
|
||||||
|
{
|
||||||
|
write_raw_u8(&cs, OP_DROP);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MODRM_ENTRY(n, work)\
|
||||||
|
case (n) | 0 << 3:\
|
||||||
|
case (n) | 1 << 3:\
|
||||||
|
case (n) | 2 << 3:\
|
||||||
|
case (n) | 3 << 3:\
|
||||||
|
case (n) | 4 << 3:\
|
||||||
|
case (n) | 5 << 3:\
|
||||||
|
case (n) | 6 << 3:\
|
||||||
|
case (n) | 7 << 3:\
|
||||||
|
work; break;
|
||||||
|
|
||||||
|
#define MODRM_ENTRY16_0(row, seg, reg1, reg2)\
|
||||||
|
MODRM_ENTRY(0x00 | row, gen_modrm_entry_0(seg, reg1, reg2, 0))\
|
||||||
|
MODRM_ENTRY(0x40 | row, gen_modrm_entry_0(seg, reg1, reg2, read_imm8s()))\
|
||||||
|
MODRM_ENTRY(0x80 | row, gen_modrm_entry_0(seg, reg1, reg2, read_imm16()))
|
||||||
|
|
||||||
|
#define MODRM_ENTRY16_1(row, seg, reg)\
|
||||||
|
MODRM_ENTRY(0x00 | row, gen_modrm_entry_1(seg, reg, 0))\
|
||||||
|
MODRM_ENTRY(0x40 | row, gen_modrm_entry_1(seg, reg, read_imm8s()))\
|
||||||
|
MODRM_ENTRY(0x80 | row, gen_modrm_entry_1(seg, reg, read_imm16()))
|
||||||
|
|
||||||
|
static void inline gen_modrm_entry_0(int32_t fn_idx, int32_t reg16_idx_1, int32_t reg16_idx_2, int32_t imm)
|
||||||
|
{
|
||||||
|
// generates: fn( ( reg1 + reg2 + imm ) & 0xFFFF )
|
||||||
|
load_u16(&cs, reg16_idx_1);
|
||||||
|
load_u16(&cs, reg16_idx_2);
|
||||||
|
add_i32(&cs);
|
||||||
|
|
||||||
|
push_i32(&cs, imm);
|
||||||
|
add_i32(&cs);
|
||||||
|
|
||||||
|
push_i32(&cs, 0xFFFF);
|
||||||
|
and_i32(&cs);
|
||||||
|
|
||||||
|
call_fn(&cs, fn_idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gen_modrm_entry_1(int32_t fn_idx, int32_t reg16_idx, int32_t imm)
|
||||||
|
{
|
||||||
|
// generates: fn ( ( reg + imm ) & 0xFFFF )
|
||||||
|
load_u16(&cs, reg16_idx);
|
||||||
|
push_i32(&cs, imm);
|
||||||
|
add_i32(&cs);
|
||||||
|
|
||||||
|
push_i32(&cs, 0xFFFF);
|
||||||
|
and_i32(&cs);
|
||||||
|
|
||||||
|
call_fn(&cs, fn_idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void jit_resolve_modrm16_(int32_t modrm_byte)
|
||||||
|
{
|
||||||
|
int32_t const ds = fn_get_seg_prefix_ds_idx;
|
||||||
|
int32_t const ss = fn_get_seg_prefix_ss_idx;
|
||||||
|
|
||||||
|
switch(modrm_byte)
|
||||||
|
{
|
||||||
|
// The following casts cause some weird issue with emscripten and cause
|
||||||
|
// a performance hit. XXX: look into this later.
|
||||||
|
MODRM_ENTRY16_0(0, ds, (int32_t)(reg16 + BX), (int32_t)(reg16 + SI))
|
||||||
|
MODRM_ENTRY16_0(1, ds, (int32_t)(reg16 + BX), (int32_t)(reg16 + DI))
|
||||||
|
MODRM_ENTRY16_0(2, ss, (int32_t)(reg16 + BP), (int32_t)(reg16 + SI))
|
||||||
|
MODRM_ENTRY16_0(3, ss, (int32_t)(reg16 + BP), (int32_t)(reg16 + DI))
|
||||||
|
MODRM_ENTRY16_1(4, ds, (int32_t)(reg16 + SI))
|
||||||
|
MODRM_ENTRY16_1(5, ds, (int32_t)(reg16 + DI))
|
||||||
|
|
||||||
|
// special case
|
||||||
|
MODRM_ENTRY(0x00 | 6, call_fn_with_arg(&cs, ds, read_imm16()))
|
||||||
|
MODRM_ENTRY(0x40 | 6, gen_modrm_entry_1(ss, (int32_t)(reg16 + BP), read_imm8s()))
|
||||||
|
MODRM_ENTRY(0x80 | 6, gen_modrm_entry_1(ss, (int32_t)(reg16 + BP), read_imm16()))
|
||||||
|
|
||||||
|
MODRM_ENTRY16_1(7, ds, (int32_t)(reg16 + BX))
|
||||||
|
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void gen_resolve_modrm16(int32_t modrm_byte)
|
||||||
|
{
|
||||||
|
push_u32(&cs, RESULT_LOC);
|
||||||
|
jit_resolve_modrm16_(modrm_byte);
|
||||||
|
store_i32(&cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MODRM_ENTRY32_0(row, seg, reg)\
|
||||||
|
MODRM_ENTRY(0x00 | row, gen_modrm32_entry(seg, reg, 0))\
|
||||||
|
MODRM_ENTRY(0x40 | row, gen_modrm32_entry(seg, reg, read_imm8s()))\
|
||||||
|
MODRM_ENTRY(0x80 | row, gen_modrm32_entry(seg, reg, read_imm32s()))
|
||||||
|
|
||||||
|
static void gen_modrm32_entry(int32_t fn_idx, int32_t reg32s_idx, int32_t imm)
|
||||||
|
{
|
||||||
|
// generates: fn ( reg + imm )
|
||||||
|
load_i32(&cs, reg32s_idx);
|
||||||
|
push_i32(&cs, imm);
|
||||||
|
add_i32(&cs);
|
||||||
|
|
||||||
|
call_fn(&cs, fn_idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void jit_resolve_sib(bool mod)
|
||||||
|
{
|
||||||
|
uint8_t sib_byte = read_imm8();
|
||||||
|
uint8_t r = sib_byte & 7;
|
||||||
|
uint8_t m = sib_byte >> 3 & 7;
|
||||||
|
|
||||||
|
int32_t base_addr;
|
||||||
|
int32_t base;
|
||||||
|
uint8_t seg;
|
||||||
|
bool base_is_mem_access = true;
|
||||||
|
|
||||||
|
if(r == 4)
|
||||||
|
{
|
||||||
|
base_addr = (int32_t)(reg32s + ESP);
|
||||||
|
seg = SS;
|
||||||
|
}
|
||||||
|
else if(r == 5)
|
||||||
|
{
|
||||||
|
if(mod)
|
||||||
|
{
|
||||||
|
base_addr = (int32_t)(reg32s + EBP);
|
||||||
|
seg = SS;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
base = read_imm32s();
|
||||||
|
seg = DS;
|
||||||
|
base_is_mem_access = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
base_addr = (int32_t)(reg32s + r);
|
||||||
|
seg = DS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate: get_seg_prefix(seg) + base
|
||||||
|
// Where base is accessed from memory if base_is_mem_access or written as a constant otherwise
|
||||||
|
|
||||||
|
dbg_assert(seg < 16);
|
||||||
|
write_raw_u8(&cs, OP_I32CONST);
|
||||||
|
write_raw_u8(&cs, seg);
|
||||||
|
|
||||||
|
call_fn(&cs, fn_get_seg_prefix_idx);
|
||||||
|
|
||||||
|
if(base_is_mem_access)
|
||||||
|
{
|
||||||
|
load_i32(&cs, base_addr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
push_i32(&cs, base);
|
||||||
|
}
|
||||||
|
|
||||||
|
add_i32(&cs);
|
||||||
|
|
||||||
|
// We now have to generate an offset value to add
|
||||||
|
|
||||||
|
if(m == 4)
|
||||||
|
{
|
||||||
|
// offset is 0, we don't need to add anything
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Offset is reg32s[m] << s, where s is:
|
||||||
|
|
||||||
|
uint8_t s = sib_byte >> 6 & 3;
|
||||||
|
|
||||||
|
load_i32(&cs, (int32_t)(reg32s + m));
|
||||||
|
// We don't use push_u32 here either since s will fit in 1 byte
|
||||||
|
write_raw_u8(&cs, OP_I32CONST);
|
||||||
|
write_raw_u8(&cs, s);
|
||||||
|
shl_i32(&cs);
|
||||||
|
|
||||||
|
add_i32(&cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void modrm32_special_case_1()
|
||||||
|
{
|
||||||
|
jit_resolve_sib(true);
|
||||||
|
push_i32(&cs, read_imm8s());
|
||||||
|
add_i32(&cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void modrm32_special_case_2()
|
||||||
|
{
|
||||||
|
jit_resolve_sib(true);
|
||||||
|
push_i32(&cs, read_imm32s());
|
||||||
|
add_i32(&cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void jit_resolve_modrm32_(int32_t modrm_byte)
|
||||||
|
{
|
||||||
|
int32_t const ds = fn_get_seg_prefix_ds_idx;
|
||||||
|
int32_t const ss = fn_get_seg_prefix_ss_idx;
|
||||||
|
|
||||||
|
switch(modrm_byte)
|
||||||
|
{
|
||||||
|
MODRM_ENTRY32_0(0, ds, (int32_t)(reg32s + EAX))
|
||||||
|
MODRM_ENTRY32_0(1, ds, (int32_t)(reg32s + ECX))
|
||||||
|
MODRM_ENTRY32_0(2, ds, (int32_t)(reg32s + EDX))
|
||||||
|
MODRM_ENTRY32_0(3, ds, (int32_t)(reg32s + EBX))
|
||||||
|
|
||||||
|
// special cases
|
||||||
|
MODRM_ENTRY(0x00 | 4, jit_resolve_sib(false))
|
||||||
|
MODRM_ENTRY(0x40 | 4, modrm32_special_case_1())
|
||||||
|
MODRM_ENTRY(0x80 | 4, modrm32_special_case_2())
|
||||||
|
MODRM_ENTRY(0x00 | 5, call_fn_with_arg(&cs, ds, read_imm32s()))
|
||||||
|
MODRM_ENTRY(0x40 | 5, gen_modrm32_entry(ss, (int32_t)(reg32s + EBP), read_imm8s()))
|
||||||
|
MODRM_ENTRY(0x80 | 5, gen_modrm32_entry(ss, (int32_t)(reg32s + EBP), read_imm32s()))
|
||||||
|
|
||||||
|
MODRM_ENTRY32_0(6, ds, (int32_t)(reg32s + ESI))
|
||||||
|
MODRM_ENTRY32_0(7, ds, (int32_t)(reg32s + EDI))
|
||||||
|
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void gen_resolve_modrm32(int32_t modrm_byte)
|
||||||
|
{
|
||||||
|
push_i32(&cs, RESULT_LOC);
|
||||||
|
jit_resolve_modrm32_(modrm_byte);
|
||||||
|
store_i32(&cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef MODRM_ENTRY
|
||||||
|
|
||||||
|
void gen_modrm_fn1(char* fn, uint8_t fn_len, int32_t modrm_byte, int32_t arg0)
|
||||||
|
{
|
||||||
|
// generates: fn( modrm_resolve( modrm_byte ), arg0 )
|
||||||
|
if(is_asize_32())
|
||||||
|
{
|
||||||
|
jit_resolve_modrm32_(modrm_byte);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
jit_resolve_modrm16_(modrm_byte);
|
||||||
|
}
|
||||||
|
|
||||||
|
push_i32(&cs, arg0);
|
||||||
|
|
||||||
|
int32_t fn_idx = get_fn_index(fn, fn_len, FN2_RET_TYPE_INDEX);
|
||||||
|
call_fn(&cs, fn_idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gen_modrm_fn0(char* fn, uint8_t fn_len, int32_t modrm_byte)
|
||||||
|
{
|
||||||
|
// generates: fn( modrm_resolve( modrm_byte ) )
|
||||||
|
if(is_asize_32())
|
||||||
|
{
|
||||||
|
jit_resolve_modrm32_(modrm_byte);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
jit_resolve_modrm16_(modrm_byte);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t fn_idx = get_fn_index(fn, fn_len, FN1_RET_TYPE_INDEX);
|
||||||
|
call_fn(&cs, fn_idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "util.h"
|
|
||||||
|
|
||||||
#define FN0_TYPE_INDEX 0
|
#define FN0_TYPE_INDEX 0
|
||||||
#define FN1_TYPE_INDEX 1
|
#define FN1_TYPE_INDEX 1
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
|
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
static Writer op;
|
static Buffer op;
|
||||||
extern Writer cs;
|
static Buffer cs;
|
||||||
|
|
||||||
static void write_type_section()
|
static void write_type_section()
|
||||||
{
|
{
|
||||||
|
|
|
@ -8,13 +8,13 @@
|
||||||
#define dbg_assert(condition) { if(DEBUG) { if(!(condition)) dbg_log(#condition); assert(condition); } }
|
#define dbg_assert(condition) { if(DEBUG) { if(!(condition)) dbg_log(#condition); assert(condition); } }
|
||||||
#define dbg_assert_message(condition, message) { if(DEBUG && !(condition)) { dbg_log(message); assert(false); } }
|
#define dbg_assert_message(condition, message) { if(DEBUG && !(condition)) { dbg_log(message); assert(false); } }
|
||||||
|
|
||||||
typedef struct Writer {
|
typedef struct Buffer {
|
||||||
uint8_t* const start;
|
uint8_t* const start;
|
||||||
uint8_t* ptr;
|
uint8_t* ptr;
|
||||||
uint32_t const len;
|
uint32_t const len;
|
||||||
} Writer;
|
} Buffer;
|
||||||
|
|
||||||
static void write_leb_i32(Writer* writer, int32_t v)
|
static void write_leb_i32(Buffer* buf, int32_t v)
|
||||||
{
|
{
|
||||||
// Super complex stuff. See the following:
|
// Super complex stuff. See the following:
|
||||||
// https://en.wikipedia.org/wiki/LEB128#Encode_signed_integer
|
// https://en.wikipedia.org/wiki/LEB128#Encode_signed_integer
|
||||||
|
@ -40,11 +40,11 @@ static void write_leb_i32(Writer* writer, int32_t v)
|
||||||
{
|
{
|
||||||
byte |= 0b10000000; // turn on MSB
|
byte |= 0b10000000; // turn on MSB
|
||||||
}
|
}
|
||||||
*(writer->ptr)++ = byte;
|
*(buf->ptr)++ = byte;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void write_leb_u32(Writer* writer, uint32_t v)
|
static void write_leb_u32(Buffer* buf, uint32_t v)
|
||||||
{
|
{
|
||||||
do {
|
do {
|
||||||
uint8_t byte = v & 0b1111111; // get last 7 bits
|
uint8_t byte = v & 0b1111111; // get last 7 bits
|
||||||
|
@ -53,13 +53,13 @@ static void write_leb_u32(Writer* writer, uint32_t v)
|
||||||
{
|
{
|
||||||
byte |= 0b10000000; // turn on MSB
|
byte |= 0b10000000; // turn on MSB
|
||||||
}
|
}
|
||||||
*(writer->ptr)++ = byte;
|
*(buf->ptr)++ = byte;
|
||||||
} while (v != 0);
|
} while (v != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void inline write_raw_u8(Writer* writer, uint8_t v)
|
static void inline write_raw_u8(Buffer* buf, uint8_t v)
|
||||||
{
|
{
|
||||||
*(writer->ptr)++ = v;
|
*(buf->ptr)++ = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void inline write_fixed_leb16_to_ptr(uint8_t* ptr, uint16_t x)
|
static void inline write_fixed_leb16_to_ptr(uint8_t* ptr, uint16_t x)
|
||||||
|
@ -69,14 +69,3 @@ static void inline write_fixed_leb16_to_ptr(uint8_t* ptr, uint16_t x)
|
||||||
*(ptr + 1) = x >> 7;
|
*(ptr + 1) = x >> 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*123456789012345678901234567890123456789012345678901234567890123456789012345
|
|
||||||
0 @@@@@@@@ @@@@@@@@@ @@ @ @@@@@@@@@ @@@@@@@ @ @ 0
|
|
||||||
0 @ @ @ @ @ @ @ @ @ 0
|
|
||||||
0 @ @ @ @ @ @ @ @ @ 0
|
|
||||||
0 @@@@@@ @ @ @ @ @ @ @@@@@@@ 0
|
|
||||||
0 @ @ @ @ @ @ @ @ @ 0
|
|
||||||
0 @ @ @ @ @ @ @ @ @ 0
|
|
||||||
0 @ @ @ @ @ @ @ @ @ 0
|
|
||||||
0 @ @@@@@@@@@ @ @@ @@@@@@@@@ @@@@@@@ @ @ 0
|
|
||||||
123456789012345678901234567890123456789012345678901234567890123456789012345*/
|
|
||||||
|
|
||||||
|
|
|
@ -5,67 +5,67 @@
|
||||||
#include "wasm_opcodes.h"
|
#include "wasm_opcodes.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
static void inline push_i32(Writer* w, int32_t v)
|
static void inline push_i32(Buffer* buf, int32_t v)
|
||||||
{
|
{
|
||||||
write_raw_u8(w, OP_I32CONST);
|
write_raw_u8(buf, OP_I32CONST);
|
||||||
write_leb_i32(w, v);
|
write_leb_i32(buf, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void inline push_u32(Writer* w, uint32_t v)
|
static void inline push_u32(Buffer* buf, uint32_t v)
|
||||||
{
|
{
|
||||||
write_raw_u8(w, OP_I32CONST);
|
write_raw_u8(buf, OP_I32CONST);
|
||||||
write_leb_u32(w, v);
|
write_leb_u32(buf, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void inline load_u16(Writer* w, uint32_t addr)
|
static void inline load_u16(Buffer* buf, uint32_t addr)
|
||||||
{
|
{
|
||||||
write_raw_u8(w, OP_I32CONST);
|
write_raw_u8(buf, OP_I32CONST);
|
||||||
write_leb_u32(w, addr);
|
write_leb_u32(buf, addr);
|
||||||
write_raw_u8(w, OP_I32LOAD16U);
|
write_raw_u8(buf, OP_I32LOAD16U);
|
||||||
write_raw_u8(w, MEM_IMM_ALIGNMENT);
|
write_raw_u8(buf, MEM_IMM_ALIGNMENT);
|
||||||
write_raw_u8(w, MEM_IMM_OFFSET);
|
write_raw_u8(buf, MEM_IMM_OFFSET);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void inline load_i32(Writer* w, uint32_t addr)
|
static void inline load_i32(Buffer* buf, uint32_t addr)
|
||||||
{
|
{
|
||||||
write_raw_u8(w, OP_I32CONST);
|
write_raw_u8(buf, OP_I32CONST);
|
||||||
write_leb_u32(w, addr);
|
write_leb_u32(buf, addr);
|
||||||
write_raw_u8(w, OP_I32LOAD);
|
write_raw_u8(buf, OP_I32LOAD);
|
||||||
write_raw_u8(w, MEM_IMM_ALIGNMENT);
|
write_raw_u8(buf, MEM_IMM_ALIGNMENT);
|
||||||
write_raw_u8(w, MEM_IMM_OFFSET);
|
write_raw_u8(buf, MEM_IMM_OFFSET);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void inline store_i32(Writer* w)
|
static void inline store_i32(Buffer* buf)
|
||||||
{
|
{
|
||||||
write_raw_u8(w, OP_I32STORE);
|
write_raw_u8(buf, OP_I32STORE);
|
||||||
write_raw_u8(w, MEM_IMM_ALIGNMENT);
|
write_raw_u8(buf, MEM_IMM_ALIGNMENT);
|
||||||
write_raw_u8(w, MEM_IMM_OFFSET);
|
write_raw_u8(buf, MEM_IMM_OFFSET);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void inline add_i32(Writer* w)
|
static void inline add_i32(Buffer* buf)
|
||||||
{
|
{
|
||||||
write_raw_u8(w, OP_I32ADD);
|
write_raw_u8(buf, OP_I32ADD);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void inline and_i32(Writer* w)
|
static void inline and_i32(Buffer* buf)
|
||||||
{
|
{
|
||||||
write_raw_u8(w, OP_I32AND);
|
write_raw_u8(buf, OP_I32AND);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void inline shl_i32(Writer* w)
|
static void inline shl_i32(Buffer* buf)
|
||||||
{
|
{
|
||||||
write_raw_u8(w, OP_I32SHL);
|
write_raw_u8(buf, OP_I32SHL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void inline call_fn(Writer* w, uint8_t fn_idx)
|
static void inline call_fn(Buffer* buf, uint8_t fn_idx)
|
||||||
{
|
{
|
||||||
write_raw_u8(w, OP_CALL);
|
write_raw_u8(buf, OP_CALL);
|
||||||
write_raw_u8(w, fn_idx);
|
write_raw_u8(buf, fn_idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void inline call_fn_with_arg(Writer* w, uint8_t fn_idx, int32_t arg0)
|
static void inline call_fn_with_arg(Buffer* buf, uint8_t fn_idx, int32_t arg0)
|
||||||
{
|
{
|
||||||
push_i32(w, arg0);
|
push_i32(buf, arg0);
|
||||||
call_fn(w, fn_idx);
|
call_fn(buf, fn_idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue