refactor codegen to use writer struct
This commit is contained in:
parent
9dc4ff6973
commit
74e6500bf1
|
@ -26,52 +26,53 @@ 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((int32_t)instruction_pointer); // store address of ip
|
||||
push_i32(&cs, (int32_t)instruction_pointer); // store address of ip
|
||||
|
||||
load_i32((int32_t)instruction_pointer); // load ip
|
||||
push_i32(n); // load value to add to it
|
||||
add_i32();
|
||||
load_i32(&cs, (int32_t)instruction_pointer); // load ip
|
||||
push_i32(&cs, n); // load value to add to it
|
||||
add_i32(&cs);
|
||||
|
||||
store_i32(); // store it back in
|
||||
store_i32(&cs); // store it back in
|
||||
}
|
||||
|
||||
void gen_set_previous_eip()
|
||||
{
|
||||
push_i32((int32_t)previous_ip); // store address of previous ip
|
||||
load_i32((int32_t)instruction_pointer); // load ip
|
||||
store_i32(); // store it as previous ip
|
||||
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(fn_idx);
|
||||
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(arg0);
|
||||
call_fn(fn_idx);
|
||||
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(arg0);
|
||||
push_i32(arg1);
|
||||
call_fn(fn_idx);
|
||||
push_i32(&cs, arg0);
|
||||
push_i32(&cs, arg1);
|
||||
call_fn(&cs, fn_idx);
|
||||
}
|
||||
|
||||
void gen_drop()
|
||||
{
|
||||
cs_write_u8(OP_DROP);
|
||||
write_raw_u8(&cs, OP_DROP);
|
||||
}
|
||||
|
||||
#define MODRM_ENTRY(n, work)\
|
||||
|
@ -98,30 +99,30 @@ void gen_drop()
|
|||
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(reg16_idx_1);
|
||||
load_u16(reg16_idx_2);
|
||||
add_i32();
|
||||
load_u16(&cs, reg16_idx_1);
|
||||
load_u16(&cs, reg16_idx_2);
|
||||
add_i32(&cs);
|
||||
|
||||
push_i32(imm);
|
||||
add_i32();
|
||||
push_i32(&cs, imm);
|
||||
add_i32(&cs);
|
||||
|
||||
push_i32(0xFFFF);
|
||||
and_i32();
|
||||
push_i32(&cs, 0xFFFF);
|
||||
and_i32(&cs);
|
||||
|
||||
call_fn(fn_idx);
|
||||
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(reg16_idx);
|
||||
push_i32(imm);
|
||||
add_i32();
|
||||
load_u16(&cs, reg16_idx);
|
||||
push_i32(&cs, imm);
|
||||
add_i32(&cs);
|
||||
|
||||
push_i32(0xFFFF);
|
||||
and_i32();
|
||||
push_i32(&cs, 0xFFFF);
|
||||
and_i32(&cs);
|
||||
|
||||
call_fn(fn_idx);
|
||||
call_fn(&cs, fn_idx);
|
||||
}
|
||||
|
||||
static void jit_resolve_modrm16_(int32_t modrm_byte)
|
||||
|
@ -141,7 +142,7 @@ static void jit_resolve_modrm16_(int32_t modrm_byte)
|
|||
MODRM_ENTRY16_1(5, ds, (int32_t)(reg16 + DI))
|
||||
|
||||
// special case
|
||||
MODRM_ENTRY(0x00 | 6, call_fn_with_arg(ds, read_imm16()))
|
||||
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()))
|
||||
|
||||
|
@ -154,9 +155,9 @@ static void jit_resolve_modrm16_(int32_t modrm_byte)
|
|||
|
||||
void gen_resolve_modrm16(int32_t modrm_byte)
|
||||
{
|
||||
push_u32(RESULT_LOC);
|
||||
push_u32(&cs, RESULT_LOC);
|
||||
jit_resolve_modrm16_(modrm_byte);
|
||||
store_i32();
|
||||
store_i32(&cs);
|
||||
}
|
||||
|
||||
#define MODRM_ENTRY32_0(row, seg, reg)\
|
||||
|
@ -167,11 +168,11 @@ void gen_resolve_modrm16(int32_t modrm_byte)
|
|||
static void gen_modrm32_entry(int32_t fn_idx, int32_t reg32s_idx, int32_t imm)
|
||||
{
|
||||
// generates: fn ( reg + imm )
|
||||
load_i32(reg32s_idx);
|
||||
push_i32(imm);
|
||||
add_i32();
|
||||
load_i32(&cs, reg32s_idx);
|
||||
push_i32(&cs, imm);
|
||||
add_i32(&cs);
|
||||
|
||||
call_fn(fn_idx);
|
||||
call_fn(&cs, fn_idx);
|
||||
}
|
||||
|
||||
static void jit_resolve_sib(bool mod)
|
||||
|
@ -214,21 +215,21 @@ static void jit_resolve_sib(bool mod)
|
|||
// Where base is accessed from memory if base_is_mem_access or written as a constant otherwise
|
||||
|
||||
dbg_assert(seg < 16);
|
||||
cs_write_u8(OP_I32CONST);
|
||||
cs_write_u8(seg);
|
||||
write_raw_u8(&cs, OP_I32CONST);
|
||||
write_raw_u8(&cs, seg);
|
||||
|
||||
call_fn(fn_get_seg_prefix_idx);
|
||||
call_fn(&cs, fn_get_seg_prefix_idx);
|
||||
|
||||
if(base_is_mem_access)
|
||||
{
|
||||
load_i32(base_addr);
|
||||
load_i32(&cs, base_addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
push_i32(base);
|
||||
push_i32(&cs, base);
|
||||
}
|
||||
|
||||
add_i32();
|
||||
add_i32(&cs);
|
||||
|
||||
// We now have to generate an offset value to add
|
||||
|
||||
|
@ -242,27 +243,27 @@ static void jit_resolve_sib(bool mod)
|
|||
|
||||
uint8_t s = sib_byte >> 6 & 3;
|
||||
|
||||
load_i32((int32_t)(reg32s + m));
|
||||
load_i32(&cs, (int32_t)(reg32s + m));
|
||||
// We don't use push_u32 here either since s will fit in 1 byte
|
||||
cs_write_u8(OP_I32CONST);
|
||||
cs_write_u8(s);
|
||||
shl_i32();
|
||||
write_raw_u8(&cs, OP_I32CONST);
|
||||
write_raw_u8(&cs, s);
|
||||
shl_i32(&cs);
|
||||
|
||||
add_i32();
|
||||
add_i32(&cs);
|
||||
}
|
||||
|
||||
static void modrm32_special_case_1()
|
||||
{
|
||||
jit_resolve_sib(true);
|
||||
push_i32(read_imm8s());
|
||||
add_i32();
|
||||
push_i32(&cs, read_imm8s());
|
||||
add_i32(&cs);
|
||||
}
|
||||
|
||||
static void modrm32_special_case_2()
|
||||
{
|
||||
jit_resolve_sib(true);
|
||||
push_i32(read_imm32s());
|
||||
add_i32();
|
||||
push_i32(&cs, read_imm32s());
|
||||
add_i32(&cs);
|
||||
}
|
||||
|
||||
static void jit_resolve_modrm32_(int32_t modrm_byte)
|
||||
|
@ -281,7 +282,7 @@ static void jit_resolve_modrm32_(int32_t modrm_byte)
|
|||
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(ds, read_imm32s()))
|
||||
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()))
|
||||
|
||||
|
@ -295,9 +296,9 @@ static void jit_resolve_modrm32_(int32_t modrm_byte)
|
|||
|
||||
void gen_resolve_modrm32(int32_t modrm_byte)
|
||||
{
|
||||
push_i32(RESULT_LOC);
|
||||
push_i32(&cs, RESULT_LOC);
|
||||
jit_resolve_modrm32_(modrm_byte);
|
||||
store_i32();
|
||||
store_i32(&cs);
|
||||
}
|
||||
|
||||
#undef MODRM_ENTRY
|
||||
|
@ -314,10 +315,10 @@ void gen_modrm_fn1(char* fn, uint8_t fn_len, int32_t modrm_byte, int32_t arg0)
|
|||
jit_resolve_modrm16_(modrm_byte);
|
||||
}
|
||||
|
||||
push_i32(arg0);
|
||||
push_i32(&cs, arg0);
|
||||
|
||||
int32_t fn_idx = get_fn_index(fn, fn_len, FN2_RET_TYPE_INDEX);
|
||||
call_fn(fn_idx);
|
||||
call_fn(&cs, fn_idx);
|
||||
}
|
||||
|
||||
void gen_modrm_fn0(char* fn, uint8_t fn_len, int32_t modrm_byte)
|
||||
|
@ -333,6 +334,6 @@ void gen_modrm_fn0(char* fn, uint8_t fn_len, int32_t modrm_byte)
|
|||
}
|
||||
|
||||
int32_t fn_idx = get_fn_index(fn, fn_len, FN1_RET_TYPE_INDEX);
|
||||
call_fn(fn_idx);
|
||||
call_fn(&cs, fn_idx);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void gen_fn0(char* fn, uint8_t fn_len);
|
||||
void gen_fn1(char* fn, uint8_t fn_len, int32_t arg0);
|
||||
void gen_fn2(char* fn, uint8_t fn_len, int32_t arg0, int32_t arg1);
|
||||
void gen_modrm_fn0(char* fn, uint8_t fn_len, int32_t modrm_byte);
|
||||
void gen_modrm_fn1(char* fn, uint8_t fn_len, int32_t modrm_byte, int32_t arg0);
|
||||
void gen_resolve_modrm16(int32_t modrm_byte);
|
||||
void gen_resolve_modrm32(int32_t modrm_byte);
|
||||
void gen_increment_instruction_pointer(int32_t n);
|
||||
void gen_set_previous_eip();
|
||||
void gen_drop();
|
||||
|
|
@ -5,184 +5,12 @@
|
|||
#include "cstring.h"
|
||||
#include "const.h"
|
||||
#include "wasm_opcodes.h"
|
||||
#include "codegen_util.h"
|
||||
#include "codegen.h"
|
||||
#include "util.h"
|
||||
#include "module_init.h"
|
||||
|
||||
uint8_t* cs_ptr = code_section;
|
||||
|
||||
static void write_type_section()
|
||||
{
|
||||
write_u8(SC_TYPE);
|
||||
|
||||
uint8_t* ptr_section_size = op_ptr;
|
||||
write_u8(0);
|
||||
|
||||
write_u8(6); // number of type descriptors
|
||||
|
||||
// FN0
|
||||
write_u8(TYPE_FUNC);
|
||||
write_u8(0); // no args
|
||||
write_u8(0); // no return val
|
||||
|
||||
// FN1
|
||||
write_u8(TYPE_FUNC);
|
||||
write_u8(1);
|
||||
write_u8(TYPE_I32);
|
||||
write_u8(0);
|
||||
|
||||
// FN2
|
||||
write_u8(TYPE_FUNC);
|
||||
write_u8(2);
|
||||
write_u8(TYPE_I32);
|
||||
write_u8(TYPE_I32);
|
||||
write_u8(0);
|
||||
|
||||
// FN0_RET
|
||||
write_u8(TYPE_FUNC);
|
||||
write_u8(0);
|
||||
write_u8(1);
|
||||
write_u8(TYPE_I32);
|
||||
|
||||
// FN1_RET
|
||||
write_u8(TYPE_FUNC);
|
||||
write_u8(1);
|
||||
write_u8(TYPE_I32);
|
||||
write_u8(1);
|
||||
write_u8(TYPE_I32);
|
||||
|
||||
// FN2_RET
|
||||
write_u8(TYPE_FUNC);
|
||||
write_u8(2);
|
||||
write_u8(TYPE_I32);
|
||||
write_u8(TYPE_I32);
|
||||
write_u8(1);
|
||||
write_u8(TYPE_I32);
|
||||
|
||||
*ptr_section_size = (op_ptr - 1) - ptr_section_size;
|
||||
}
|
||||
|
||||
// Import table
|
||||
|
||||
static uint8_t* ptr_import_count = (uint8_t*) 0;
|
||||
static uint8_t* ptr_import_entries = (uint8_t*) 0;
|
||||
static uint8_t* ptr_import_table_size = (uint8_t*) 0;
|
||||
|
||||
// The import table size is written in leb encoding, which we can't read by simple dereferencing so
|
||||
// we store the actual value separately. This is needed since we reserve two bytes for the import
|
||||
// table size as it can exceed 127
|
||||
// Default value is one as the section starts with containing one byte for the import count
|
||||
static uint32_t import_table_size = 1;
|
||||
|
||||
// Goes over the import block to find index of an import entry by function name
|
||||
// Returns -1 if not found
|
||||
static int32_t get_import_index(char* fn, uint8_t fn_len)
|
||||
{
|
||||
uint8_t* offset = ptr_import_entries;
|
||||
for(int32_t i = 0; i < *ptr_import_count; i++)
|
||||
{
|
||||
offset += 1; // skip length of module name
|
||||
offset += 1; // skip module name itself
|
||||
uint8_t len = *offset++;
|
||||
char* name = (char*) offset;
|
||||
if (len == fn_len && strncmp(name, fn, fn_len) == 0)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
offset += len; // skip the string
|
||||
offset += 1; // skip import kind
|
||||
offset += 1; // skip type index
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void set_import_table_size(uint16_t size)
|
||||
{
|
||||
import_table_size = size;
|
||||
write_fixed_leb16_to_ptr(ptr_import_table_size, size);
|
||||
}
|
||||
|
||||
static void write_import_section_preamble()
|
||||
{
|
||||
write_u8(SC_IMPORT);
|
||||
|
||||
ptr_import_table_size = op_ptr; // store current pointer location to write into later on
|
||||
write_u8(1 | 0b10000000); write_u8(0); // 1 in 2 byte leb
|
||||
|
||||
// same as above but for count of entries
|
||||
ptr_import_count = op_ptr;
|
||||
write_u8(0);
|
||||
|
||||
// here after starts the actual list of imports
|
||||
ptr_import_entries = op_ptr;
|
||||
}
|
||||
|
||||
static void write_memory_import()
|
||||
{
|
||||
write_u8(1);
|
||||
write_u8('e');
|
||||
write_u8(1);
|
||||
write_u8('m');
|
||||
|
||||
write_u8(EXT_MEMORY);
|
||||
|
||||
write_u8(0); // memory flag, 0 for no maximum memory limit present
|
||||
write_u32(256); // initial memory length of 256 pages, takes 2 bytes in leb128
|
||||
|
||||
*ptr_import_count += 1;
|
||||
set_import_table_size(import_table_size + 1 + 1 + 1 + 1 + 1 + 1 + 2);
|
||||
}
|
||||
|
||||
static uint8_t write_import_entry(char* fn_name, uint8_t fn_name_len, uint8_t type_index)
|
||||
{
|
||||
write_u8(1); // length of module name
|
||||
write_u8('e'); // module name
|
||||
write_u8(fn_name_len);
|
||||
for (uint8_t i = 0; i < fn_name_len; i++)
|
||||
{
|
||||
write_u8(fn_name[i]);
|
||||
}
|
||||
write_u8(EXT_FUNCTION);
|
||||
write_u8(type_index);
|
||||
*ptr_import_count += 1;
|
||||
|
||||
set_import_table_size(import_table_size + 1 + 1 + 1 + fn_name_len + 1 + 1);
|
||||
|
||||
return *ptr_import_count - 1;
|
||||
}
|
||||
|
||||
static void write_function_section()
|
||||
{
|
||||
write_u8(SC_FUNCTION);
|
||||
write_u8(2); // length of this section
|
||||
write_u8(1); // count of signature indices
|
||||
write_u8(FN0_TYPE_INDEX); // we export one function which is nullary
|
||||
}
|
||||
|
||||
static void write_export_section()
|
||||
{
|
||||
write_u8(SC_EXPORT);
|
||||
write_u8(1 + 1 + 1 + 1 + 1); // size of this section
|
||||
write_u8(1); // count of table: just one function exported
|
||||
|
||||
write_u8(1); // length of exported function name
|
||||
write_u8('f'); // function name
|
||||
write_u8(EXT_FUNCTION);
|
||||
|
||||
// index of the exported function
|
||||
// function space starts with imports. index of last import is import count - 1
|
||||
// the last import however is a memory, so we subtract one from that
|
||||
write_u8(*ptr_import_count - 1);
|
||||
}
|
||||
|
||||
int32_t get_fn_index(char* fn, uint8_t fn_len, uint8_t type_index)
|
||||
{
|
||||
int32_t fn_idx = get_import_index(fn, fn_len);
|
||||
if (fn_idx == -1)
|
||||
{
|
||||
return write_import_entry(fn, fn_len, type_index);
|
||||
}
|
||||
return fn_idx;
|
||||
}
|
||||
static Writer 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 uint8_t* op_ptr_reset_location;
|
||||
static uint32_t import_table_size_reset_value;
|
||||
|
@ -191,10 +19,10 @@ static uint32_t initial_import_count;
|
|||
void gen_init()
|
||||
{
|
||||
// wasm magic header
|
||||
write_u8(0); write_u8('a'); write_u8('s'); write_u8('m');
|
||||
write_raw_u8(&op, 0); write_raw_u8(&op, 'a'); write_raw_u8(&op, 's'); write_raw_u8(&op, 'm');
|
||||
|
||||
// wasm version in leb128, 4 bytes
|
||||
write_u8(WASM_VERSION); write_u8(0); write_u8(0); write_u8(0);
|
||||
write_raw_u8(&op, WASM_VERSION); write_raw_u8(&op, 0); write_raw_u8(&op, 0); write_raw_u8(&op, 0);
|
||||
|
||||
write_type_section();
|
||||
write_import_section_preamble();
|
||||
|
@ -213,28 +41,19 @@ void gen_init()
|
|||
assert(_fn_get_seg_prefix_idx == fn_get_seg_prefix_idx);
|
||||
|
||||
// store state of current pointers etc. so we can reset them later
|
||||
op_ptr_reset_location = op_ptr;
|
||||
op_ptr_reset_location = op.ptr;
|
||||
initial_import_count = *ptr_import_count;
|
||||
import_table_size_reset_value = import_table_size;
|
||||
}
|
||||
|
||||
void gen_reset()
|
||||
{
|
||||
op_ptr = op_ptr_reset_location;
|
||||
cs_ptr = code_section;
|
||||
op.ptr = op_ptr_reset_location;
|
||||
cs.ptr = cs.start;
|
||||
*ptr_import_count = initial_import_count;
|
||||
import_table_size = import_table_size_reset_value;
|
||||
}
|
||||
|
||||
static void copy_code_section()
|
||||
{
|
||||
uint8_t* offset = code_section;
|
||||
while (offset < cs_ptr)
|
||||
{
|
||||
write_u8(*offset++);
|
||||
}
|
||||
}
|
||||
|
||||
uintptr_t gen_finish()
|
||||
{
|
||||
write_memory_import();
|
||||
|
@ -245,33 +64,33 @@ uintptr_t gen_finish()
|
|||
uint8_t* ptr_fn_body_size = (uint8_t*) 0; // this as well
|
||||
|
||||
// write code section preamble
|
||||
write_u8(SC_CODE);
|
||||
ptr_code_section_size = op_ptr; // we will write to this location later
|
||||
write_u8(0); write_u8(0); // write temp val for now using 2 bytes
|
||||
write_raw_u8(&op, SC_CODE);
|
||||
ptr_code_section_size = op.ptr; // we will write to this location later
|
||||
write_raw_u8(&op, 0); write_raw_u8(&op, 0); // write temp val for now using 2 bytes
|
||||
|
||||
write_u8(1); // number of function bodies: just 1
|
||||
write_raw_u8(&op, 1); // number of function bodies: just 1
|
||||
|
||||
// same as above but for body size of the function
|
||||
ptr_fn_body_size = op_ptr;
|
||||
write_u8(0); write_u8(0);
|
||||
ptr_fn_body_size = op.ptr;
|
||||
write_raw_u8(&op, 0); write_raw_u8(&op, 0);
|
||||
|
||||
write_u8(0); // count of locals, none
|
||||
write_raw_u8(&op, 0); // count of locals, none
|
||||
|
||||
copy_code_section();
|
||||
|
||||
// write code section epilogue
|
||||
write_u8(OP_END);
|
||||
write_raw_u8(&op, OP_END);
|
||||
|
||||
// write the actual sizes to the pointer locations stored above. We subtract 1 from the actual
|
||||
// value because the ptr itself points to two bytes
|
||||
write_fixed_leb16_to_ptr(ptr_fn_body_size, ((op_ptr - 1) - ptr_fn_body_size) - 1);
|
||||
write_fixed_leb16_to_ptr(ptr_code_section_size, ((op_ptr - 1) - ptr_code_section_size) - 1);
|
||||
write_fixed_leb16_to_ptr(ptr_fn_body_size, ((op.ptr - 1) - ptr_fn_body_size) - 1);
|
||||
write_fixed_leb16_to_ptr(ptr_code_section_size, ((op.ptr - 1) - ptr_code_section_size) - 1);
|
||||
|
||||
return (uintptr_t) op_ptr;
|
||||
return (uintptr_t) op.ptr;
|
||||
}
|
||||
|
||||
uintptr_t gen_get_final_offset()
|
||||
{
|
||||
return (uintptr_t) op_ptr;
|
||||
return (uintptr_t) op.ptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "util.h"
|
||||
|
||||
#define FN0_TYPE_INDEX 0
|
||||
#define FN1_TYPE_INDEX 1
|
||||
|
@ -13,10 +14,21 @@ static uint8_t const fn_get_seg_prefix_ds_idx = 0;
|
|||
static uint8_t const fn_get_seg_prefix_ss_idx = 1;
|
||||
static uint8_t const fn_get_seg_prefix_idx = 2;
|
||||
|
||||
int32_t get_fn_index(char* fn, uint8_t fn_len, uint8_t type_index);
|
||||
|
||||
void gen_init(void);
|
||||
void gen_reset(void);
|
||||
uintptr_t gen_finish(void);
|
||||
uintptr_t gen_get_final_offset(void);
|
||||
|
||||
int32_t get_fn_index(char* fn, uint8_t fn_len, uint8_t type_index);
|
||||
|
||||
void gen_fn0(char* fn, uint8_t fn_len);
|
||||
void gen_fn1(char* fn, uint8_t fn_len, int32_t arg0);
|
||||
void gen_fn2(char* fn, uint8_t fn_len, int32_t arg0, int32_t arg1);
|
||||
void gen_modrm_fn0(char* fn, uint8_t fn_len, int32_t modrm_byte);
|
||||
void gen_modrm_fn1(char* fn, uint8_t fn_len, int32_t modrm_byte, int32_t arg0);
|
||||
void gen_resolve_modrm16(int32_t modrm_byte);
|
||||
void gen_resolve_modrm32(int32_t modrm_byte);
|
||||
void gen_increment_instruction_pointer(int32_t n);
|
||||
void gen_set_previous_eip();
|
||||
void gen_drop();
|
||||
|
||||
|
|
|
@ -1,48 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
static uint8_t* const output = (uint8_t* const) 2048;
|
||||
|
||||
// pointer to next free byte slot in output buffer, incremented as we write along in the buffer
|
||||
static uint8_t* op_ptr = output;
|
||||
|
||||
static uint8_t* const code_section = output + 1024;
|
||||
extern uint8_t* cs_ptr;
|
||||
|
||||
// JS can keep strings at this location for passing them to wasm
|
||||
//XXX: figure out a better location for this
|
||||
static uint8_t* const str_input = code_section - 32;
|
||||
|
||||
static void inline write_u8(uint8_t x)
|
||||
{
|
||||
*op_ptr++ = x;
|
||||
}
|
||||
|
||||
static void inline cs_write_u8(uint8_t x)
|
||||
{
|
||||
*cs_ptr++ = x;
|
||||
}
|
||||
|
||||
static void inline write_i32(int32_t x)
|
||||
{
|
||||
op_ptr = _write_leb_i32(op_ptr, x);
|
||||
}
|
||||
|
||||
static void inline cs_write_i32(int32_t x)
|
||||
{
|
||||
cs_ptr = _write_leb_i32(cs_ptr, x);
|
||||
}
|
||||
|
||||
static void inline write_u32(uint32_t x)
|
||||
{
|
||||
op_ptr = _write_leb_u32(op_ptr, x);
|
||||
}
|
||||
|
||||
static void inline cs_write_u32(uint32_t x)
|
||||
{
|
||||
cs_ptr = _write_leb_u32(cs_ptr, x);
|
||||
}
|
||||
|
192
src/native/codegen/module_init.h
Normal file
192
src/native/codegen/module_init.h
Normal file
|
@ -0,0 +1,192 @@
|
|||
#pragma once
|
||||
|
||||
#include<stdint.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
static Writer op;
|
||||
extern Writer cs;
|
||||
|
||||
static void write_type_section()
|
||||
{
|
||||
write_raw_u8(&op, SC_TYPE);
|
||||
|
||||
uint8_t* ptr_section_size = op.ptr;
|
||||
write_raw_u8(&op, 0);
|
||||
|
||||
write_raw_u8(&op, 6); // number of type descriptors
|
||||
|
||||
// FN0
|
||||
write_raw_u8(&op, TYPE_FUNC);
|
||||
write_raw_u8(&op, 0); // no args
|
||||
write_raw_u8(&op, 0); // no return val
|
||||
|
||||
// FN1
|
||||
write_raw_u8(&op, TYPE_FUNC);
|
||||
write_raw_u8(&op, 1);
|
||||
write_raw_u8(&op, TYPE_I32);
|
||||
write_raw_u8(&op, 0);
|
||||
|
||||
// FN2
|
||||
write_raw_u8(&op, TYPE_FUNC);
|
||||
write_raw_u8(&op, 2);
|
||||
write_raw_u8(&op, TYPE_I32);
|
||||
write_raw_u8(&op, TYPE_I32);
|
||||
write_raw_u8(&op, 0);
|
||||
|
||||
// FN0_RET
|
||||
write_raw_u8(&op, TYPE_FUNC);
|
||||
write_raw_u8(&op, 0);
|
||||
write_raw_u8(&op, 1);
|
||||
write_raw_u8(&op, TYPE_I32);
|
||||
|
||||
// FN1_RET
|
||||
write_raw_u8(&op, TYPE_FUNC);
|
||||
write_raw_u8(&op, 1);
|
||||
write_raw_u8(&op, TYPE_I32);
|
||||
write_raw_u8(&op, 1);
|
||||
write_raw_u8(&op, TYPE_I32);
|
||||
|
||||
// FN2_RET
|
||||
write_raw_u8(&op, TYPE_FUNC);
|
||||
write_raw_u8(&op, 2);
|
||||
write_raw_u8(&op, TYPE_I32);
|
||||
write_raw_u8(&op, TYPE_I32);
|
||||
write_raw_u8(&op, 1);
|
||||
write_raw_u8(&op, TYPE_I32);
|
||||
|
||||
*ptr_section_size = (op.ptr - 1) - ptr_section_size;
|
||||
}
|
||||
|
||||
// Import table
|
||||
|
||||
static uint8_t* ptr_import_count = (uint8_t*) 0;
|
||||
static uint8_t* ptr_import_entries = (uint8_t*) 0;
|
||||
static uint8_t* ptr_import_table_size = (uint8_t*) 0;
|
||||
|
||||
// The import table size is written in leb encoding, which we can't read by simple dereferencing so
|
||||
// we store the actual value separately. This is needed since we reserve two bytes for the import
|
||||
// table size as it can exceed 127
|
||||
// Default value is one as the section starts with containing one byte for the import count
|
||||
static uint32_t import_table_size = 1;
|
||||
|
||||
// Goes over the import block to find index of an import entry by function name
|
||||
// Returns -1 if not found
|
||||
static int32_t get_import_index(char* fn, uint8_t fn_len)
|
||||
{
|
||||
uint8_t* offset = ptr_import_entries;
|
||||
for(int32_t i = 0; i < *ptr_import_count; i++)
|
||||
{
|
||||
offset += 1; // skip length of module name
|
||||
offset += 1; // skip module name itself
|
||||
uint8_t len = *offset++;
|
||||
char* name = (char*) offset;
|
||||
if (len == fn_len && strncmp(name, fn, fn_len) == 0)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
offset += len; // skip the string
|
||||
offset += 1; // skip import kind
|
||||
offset += 1; // skip type index
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void set_import_table_size(uint16_t size)
|
||||
{
|
||||
import_table_size = size;
|
||||
write_fixed_leb16_to_ptr(ptr_import_table_size, size);
|
||||
}
|
||||
|
||||
static void write_import_section_preamble()
|
||||
{
|
||||
write_raw_u8(&op, SC_IMPORT);
|
||||
|
||||
ptr_import_table_size = op.ptr; // store current pointer location to write into later on
|
||||
write_raw_u8(&op, 1 | 0b10000000); write_raw_u8(&op, 0); // 1 in 2 byte leb
|
||||
|
||||
// same as above but for count of entries
|
||||
ptr_import_count = op.ptr;
|
||||
write_raw_u8(&op, 0);
|
||||
|
||||
// here after starts the actual list of imports
|
||||
ptr_import_entries = op.ptr;
|
||||
}
|
||||
|
||||
static void write_memory_import()
|
||||
{
|
||||
write_raw_u8(&op, 1);
|
||||
write_raw_u8(&op, 'e');
|
||||
write_raw_u8(&op, 1);
|
||||
write_raw_u8(&op, 'm');
|
||||
|
||||
write_raw_u8(&op, EXT_MEMORY);
|
||||
|
||||
write_raw_u8(&op, 0); // memory flag, 0 for no maximum memory limit present
|
||||
write_leb_u32(&op, 256); // initial memory length of 256 pages, takes 2 bytes in leb128
|
||||
|
||||
*ptr_import_count += 1;
|
||||
set_import_table_size(import_table_size + 1 + 1 + 1 + 1 + 1 + 1 + 2);
|
||||
}
|
||||
|
||||
static uint8_t write_import_entry(char* fn_name, uint8_t fn_name_len, uint8_t type_index)
|
||||
{
|
||||
write_raw_u8(&op, 1); // length of module name
|
||||
write_raw_u8(&op, 'e'); // module name
|
||||
write_raw_u8(&op, fn_name_len);
|
||||
for (uint8_t i = 0; i < fn_name_len; i++)
|
||||
{
|
||||
write_raw_u8(&op, fn_name[i]);
|
||||
}
|
||||
write_raw_u8(&op, EXT_FUNCTION);
|
||||
write_raw_u8(&op, type_index);
|
||||
*ptr_import_count += 1;
|
||||
|
||||
set_import_table_size(import_table_size + 1 + 1 + 1 + fn_name_len + 1 + 1);
|
||||
|
||||
return *ptr_import_count - 1;
|
||||
}
|
||||
|
||||
static void write_function_section()
|
||||
{
|
||||
write_raw_u8(&op, SC_FUNCTION);
|
||||
write_raw_u8(&op, 2); // length of this section
|
||||
write_raw_u8(&op, 1); // count of signature indices
|
||||
write_raw_u8(&op, FN0_TYPE_INDEX); // we export one function which is nullary
|
||||
}
|
||||
|
||||
static void write_export_section()
|
||||
{
|
||||
write_raw_u8(&op, SC_EXPORT);
|
||||
write_raw_u8(&op, 1 + 1 + 1 + 1 + 1); // size of this section
|
||||
write_raw_u8(&op, 1); // count of table: just one function exported
|
||||
|
||||
write_raw_u8(&op, 1); // length of exported function name
|
||||
write_raw_u8(&op, 'f'); // function name
|
||||
write_raw_u8(&op, EXT_FUNCTION);
|
||||
|
||||
// index of the exported function
|
||||
// function space starts with imports. index of last import is import count - 1
|
||||
// the last import however is a memory, so we subtract one from that
|
||||
write_raw_u8(&op, *ptr_import_count - 1);
|
||||
}
|
||||
|
||||
int32_t get_fn_index(char* fn, uint8_t fn_len, uint8_t type_index)
|
||||
{
|
||||
int32_t fn_idx = get_import_index(fn, fn_len);
|
||||
if (fn_idx == -1)
|
||||
{
|
||||
return write_import_entry(fn, fn_len, type_index);
|
||||
}
|
||||
return fn_idx;
|
||||
}
|
||||
|
||||
static void copy_code_section()
|
||||
{
|
||||
uint8_t* offset = cs.start;
|
||||
while (offset < cs.ptr)
|
||||
{
|
||||
write_raw_u8(&op, *offset++);
|
||||
}
|
||||
}
|
||||
|
|
@ -8,21 +8,13 @@
|
|||
#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); } }
|
||||
|
||||
static uint8_t* _write_leb_u32(uint8_t* ptr, uint32_t v)
|
||||
{
|
||||
do {
|
||||
uint8_t byte = v & 0b1111111; // get last 7 bits
|
||||
v >>= 7; // shift them away from the value
|
||||
if (v != 0)
|
||||
{
|
||||
byte |= 0b10000000; // turn on MSB
|
||||
}
|
||||
*ptr++ = byte;
|
||||
} while (v != 0);
|
||||
return ptr;
|
||||
}
|
||||
typedef struct Writer {
|
||||
uint8_t* const start;
|
||||
uint8_t* ptr;
|
||||
uint32_t const len;
|
||||
} Writer;
|
||||
|
||||
static uint8_t* _write_leb_i32(uint8_t* ptr, int32_t v)
|
||||
static void write_leb_i32(Writer* writer, int32_t v)
|
||||
{
|
||||
// Super complex stuff. See the following:
|
||||
// https://en.wikipedia.org/wiki/LEB128#Encode_signed_integer
|
||||
|
@ -48,9 +40,26 @@ static uint8_t* _write_leb_i32(uint8_t* ptr, int32_t v)
|
|||
{
|
||||
byte |= 0b10000000; // turn on MSB
|
||||
}
|
||||
*ptr++ = byte;
|
||||
*(writer->ptr)++ = byte;
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static void write_leb_u32(Writer* writer, uint32_t v)
|
||||
{
|
||||
do {
|
||||
uint8_t byte = v & 0b1111111; // get last 7 bits
|
||||
v >>= 7; // shift them away from the value
|
||||
if (v != 0)
|
||||
{
|
||||
byte |= 0b10000000; // turn on MSB
|
||||
}
|
||||
*(writer->ptr)++ = byte;
|
||||
} while (v != 0);
|
||||
}
|
||||
|
||||
static void inline write_raw_u8(Writer* writer, uint8_t v)
|
||||
{
|
||||
*(writer->ptr)++ = v;
|
||||
}
|
||||
|
||||
static void inline write_fixed_leb16_to_ptr(uint8_t* ptr, uint16_t x)
|
||||
|
@ -60,3 +69,14 @@ static void inline write_fixed_leb16_to_ptr(uint8_t* ptr, uint16_t x)
|
|||
*(ptr + 1) = x >> 7;
|
||||
}
|
||||
|
||||
/*123456789012345678901234567890123456789012345678901234567890123456789012345
|
||||
0 @@@@@@@@ @@@@@@@@@ @@ @ @@@@@@@@@ @@@@@@@ @ @ 0
|
||||
0 @ @ @ @ @ @ @ @ @ 0
|
||||
0 @ @ @ @ @ @ @ @ @ 0
|
||||
0 @@@@@@ @ @ @ @ @ @ @@@@@@@ 0
|
||||
0 @ @ @ @ @ @ @ @ @ 0
|
||||
0 @ @ @ @ @ @ @ @ @ 0
|
||||
0 @ @ @ @ @ @ @ @ @ 0
|
||||
0 @ @@@@@@@@@ @ @@ @@@@@@@@@ @@@@@@@ @ @ 0
|
||||
123456789012345678901234567890123456789012345678901234567890123456789012345*/
|
||||
|
||||
|
|
|
@ -3,69 +3,69 @@
|
|||
#include <stdint.h>
|
||||
|
||||
#include "wasm_opcodes.h"
|
||||
#include "codegen_util.h"
|
||||
#include "util.h"
|
||||
|
||||
static void inline push_i32(int32_t v)
|
||||
static void inline push_i32(Writer* w, int32_t v)
|
||||
{
|
||||
cs_write_u8(OP_I32CONST);
|
||||
cs_write_i32(v);
|
||||
write_raw_u8(w, OP_I32CONST);
|
||||
write_leb_i32(w, v);
|
||||
}
|
||||
|
||||
static void inline push_u32(uint32_t v)
|
||||
static void inline push_u32(Writer* w, uint32_t v)
|
||||
{
|
||||
cs_write_u8(OP_I32CONST);
|
||||
cs_write_u32(v);
|
||||
write_raw_u8(w, OP_I32CONST);
|
||||
write_leb_u32(w, v);
|
||||
}
|
||||
|
||||
static void inline load_u16(uint32_t addr)
|
||||
static void inline load_u16(Writer* w, uint32_t addr)
|
||||
{
|
||||
cs_write_u8(OP_I32CONST);
|
||||
cs_write_u32(addr);
|
||||
cs_write_u8(OP_I32LOAD16U);
|
||||
cs_write_u8(MEM_IMM_ALIGNMENT);
|
||||
cs_write_u8(MEM_IMM_OFFSET);
|
||||
write_raw_u8(w, OP_I32CONST);
|
||||
write_leb_u32(w, addr);
|
||||
write_raw_u8(w, OP_I32LOAD16U);
|
||||
write_raw_u8(w, MEM_IMM_ALIGNMENT);
|
||||
write_raw_u8(w, MEM_IMM_OFFSET);
|
||||
}
|
||||
|
||||
static void inline load_i32(uint32_t addr)
|
||||
static void inline load_i32(Writer* w, uint32_t addr)
|
||||
{
|
||||
cs_write_u8(OP_I32CONST);
|
||||
cs_write_u32(addr);
|
||||
cs_write_u8(OP_I32LOAD);
|
||||
cs_write_u8(MEM_IMM_ALIGNMENT);
|
||||
cs_write_u8(MEM_IMM_OFFSET);
|
||||
write_raw_u8(w, OP_I32CONST);
|
||||
write_leb_u32(w, addr);
|
||||
write_raw_u8(w, OP_I32LOAD);
|
||||
write_raw_u8(w, MEM_IMM_ALIGNMENT);
|
||||
write_raw_u8(w, MEM_IMM_OFFSET);
|
||||
}
|
||||
|
||||
static void inline store_i32()
|
||||
static void inline store_i32(Writer* w)
|
||||
{
|
||||
cs_write_u8(OP_I32STORE);
|
||||
cs_write_u8(MEM_IMM_ALIGNMENT);
|
||||
cs_write_u8(MEM_IMM_OFFSET);
|
||||
write_raw_u8(w, OP_I32STORE);
|
||||
write_raw_u8(w, MEM_IMM_ALIGNMENT);
|
||||
write_raw_u8(w, MEM_IMM_OFFSET);
|
||||
}
|
||||
|
||||
static void inline add_i32()
|
||||
static void inline add_i32(Writer* w)
|
||||
{
|
||||
cs_write_u8(OP_I32ADD);
|
||||
write_raw_u8(w, OP_I32ADD);
|
||||
}
|
||||
|
||||
static void inline and_i32()
|
||||
static void inline and_i32(Writer* w)
|
||||
{
|
||||
cs_write_u8(OP_I32AND);
|
||||
write_raw_u8(w, OP_I32AND);
|
||||
}
|
||||
|
||||
static void inline shl_i32()
|
||||
static void inline shl_i32(Writer* w)
|
||||
{
|
||||
cs_write_u8(OP_I32SHL);
|
||||
write_raw_u8(w, OP_I32SHL);
|
||||
}
|
||||
|
||||
static void inline call_fn(uint8_t fn_idx)
|
||||
static void inline call_fn(Writer* w, uint8_t fn_idx)
|
||||
{
|
||||
cs_write_u8(OP_CALL);
|
||||
cs_write_u8(fn_idx);
|
||||
write_raw_u8(w, OP_CALL);
|
||||
write_raw_u8(w, fn_idx);
|
||||
}
|
||||
|
||||
static void inline call_fn_with_arg(uint8_t fn_idx, int32_t arg0)
|
||||
static void inline call_fn_with_arg(Writer* w, uint8_t fn_idx, int32_t arg0)
|
||||
{
|
||||
push_i32(arg0);
|
||||
call_fn(fn_idx);
|
||||
push_i32(w, arg0);
|
||||
call_fn(w, fn_idx);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue