Last active
May 6, 2020 14:26
-
-
Save gingerBill/2b6627c58bfb59122e49c3ca54b3702e to your computer and use it in GitHub Desktop.
Basic WASM Assembler in Odin
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package wasm_assembler | |
import "core:fmt" | |
PAGE_SIZE :: 1<<16; | |
Val_Type :: enum u8 { | |
void = 0x00, | |
i32 = 0x7f, | |
i64 = 0x7e, | |
f32 = 0x7d, | |
f64 = 0x7c, | |
} | |
Op :: enum u16le { | |
Unreachable = 0x00, | |
Nop = 0x01, | |
Block = 0x02, | |
Loop = 0x03, | |
If = 0x04, | |
End = 0x0b, | |
Br = 0x0c, | |
Br_If = 0x0d, | |
Br_Table = 0x0e, | |
Return = 0x0f, | |
Call = 0x10, | |
Call_Indirect = 0x11, | |
Drop = 0x1a, | |
Select = 0x1b, | |
Local_Get = 0x20, | |
Local_Set = 0x21, | |
Local_Tee = 0x22, // Sets and then returns value | |
Global_Get = 0x23, | |
Global_Set = 0x24, | |
Load_i32 = 0x28, | |
Load_i64 = 0x29, | |
Load_f32 = 0x2a, | |
Load_f64 = 0x2b, | |
Load_i32_as_s8 = 0x2c, | |
Load_i32_as_u8 = 0x2d, | |
Load_i32_as_s16 = 0x2e, | |
Load_i32_as_u16 = 0x2f, | |
Load_i64_as_s8 = 0x30, | |
Load_i64_as_u8 = 0x31, | |
Load_i64_as_s16 = 0x32, | |
Load_i64_as_u16 = 0x33, | |
Load_i64_as_s32 = 0x34, | |
Load_i64_as_u32 = 0x35, | |
Store_i32 = 0x36, | |
Store_i64 = 0x37, | |
Store_f32 = 0x38, | |
Store_f64 = 0x39, | |
Store_i32_i8 = 0x3a, | |
Store_i32_i16 = 0x3b, | |
Store_i64_i8 = 0x3c, | |
Store_i64_i16 = 0x3d, | |
Store_i64_i32 = 0x3e, | |
Memory_Size = 0x3f, | |
Memory_Grow = 0x40, | |
Const_i32 = 0x41, | |
Const_i64 = 0x42, | |
Const_f32 = 0x43, | |
Const_f64 = 0x44, | |
Eqz_i32 = 0x45, | |
Eq_i32 = 0x46, | |
Ne_i32 = 0x47, | |
Lt_s32 = 0x48, | |
Lt_u32 = 0x49, | |
Gt_s32 = 0x4a, | |
Gt_u32 = 0x4b, | |
Le_s32 = 0x4c, | |
Le_u32 = 0x4d, | |
Ge_s32 = 0x4e, | |
Ge_u32 = 0x4f, | |
Eqz_i64 = 0x50, | |
Eq_i64 = 0x51, | |
Ne_i64 = 0x52, | |
Lt_s64 = 0x53, | |
Lt_u64 = 0x54, | |
Gt_s64 = 0x55, | |
Gt_u64 = 0x56, | |
Le_s64 = 0x57, | |
Le_u64 = 0x58, | |
Ge_s64 = 0x59, | |
Ge_u64 = 0x5a, | |
Eq_f32 = 0x5b, | |
Ne_f32 = 0x5c, | |
Lt_f32 = 0x5d, | |
Gt_f32 = 0x5e, | |
Le_f32 = 0x5f, | |
Ge_f32 = 0x60, | |
Eq_f64 = 0x61, | |
Ne_f64 = 0x62, | |
Lt_f64 = 0x63, | |
Gt_f64 = 0x64, | |
Le_f64 = 0x65, | |
Ge_f64 = 0x66, | |
Clz_i32 = 0x67, | |
Ctz_i32, | |
Popcnt_i32, | |
Add_i32, | |
Sub_i32, | |
Mul_i32, | |
Div_s32, | |
Div_u32, | |
Rem_s32, | |
Rem_u32, | |
And_i32, | |
Or_i32, | |
Xor_i32, | |
Shl_i32, | |
Shr_s32, | |
Shr_u32, | |
Rotl_i32, | |
Rotr_i32 = 0x78, | |
Clz_i64 = 0x79, | |
Ctz_i64, | |
Popcnt_i64, | |
Add_i64, | |
Sub_i64, | |
Mul_i64, | |
Div_s64, | |
Div_u64, | |
Rem_s64, | |
Rem_u64, | |
And_i64, | |
Or_i64, | |
Xor_i64, | |
Shl_i64, | |
Shr_s64, | |
Shr_u64, | |
Rotl_i64, | |
Rotr_i64 = 0x8a, | |
Abs_f32 = 0x8b, | |
Neg_f32, | |
Ceil_f32, | |
Floor_f32, | |
Trunc_f32, | |
Nearest_f32, | |
Sqrt_f32, | |
Add_f32, | |
Sub_f32, | |
Mul_f32, | |
Div_f32, | |
Min_f32, | |
Max_f32, | |
Copysign_f32 = 0x98, | |
Abs_f64 = 0x99, | |
Neg_f64, | |
Ceil_f64, | |
Floor_f64, | |
Trunc_f64, | |
Nearest_f64, | |
Sqrt_f64, | |
Add_f64, | |
Sub_f64, | |
Mul_f64, | |
Div_f64, | |
Min_f64, | |
Max_f64, | |
Copysign_f64 = 0xa6, | |
Wrap_i64_to_i32 = 0xa7, | |
Trunc_f32_s32, | |
Trunc_f32_u32, | |
Trunc_f64_s32, | |
Trunc_f64_u32, | |
Extend_i32_s64, | |
Extend_i32_u64, | |
Trunc_f32_s64, | |
Trunc_f32_u64, | |
Trunc_f64_s64, | |
Trunc_f64_u64, | |
Convert_s32_f32, | |
Convert_u32_f32, | |
Convert_s64_f32, | |
Convert_u64_f32, | |
Demote_f64_to_f32, | |
Convert_s32_to_f64, | |
Convert_u32_to_f64, | |
Convert_s64_to_f64, | |
Convert_u64_to_f64, | |
Promote_f32_to_f64, | |
Reinterpret_f32_to_i32, | |
Reinterpret_f64_to_i64, | |
Reinterpret_i32_to_f32, | |
Reinterpret_i64_to_f64 = 0xbf, | |
Extend_s8_to_i32 = 0xc0, | |
Extend_s16_to_i32 = 0xc1, | |
Extend_s8_to_i64 = 0xc2, | |
Extend_s16_to_i64 = 0xc3, | |
Extend_s32_to_i64 = 0xc4, | |
GAP = 0xff, | |
trunc_sat_f32_to_s32 = 0x00_fc, | |
trunc_sat_f32_to_u32 = 0x01_fc, | |
trunc_sat_f64_to_s32 = 0x02_fc, | |
trunc_sat_f64_to_u32 = 0x03_fc, | |
trunc_sat_f32_to_s64 = 0x04_fc, | |
trunc_sat_f32_to_u64 = 0x05_fc, | |
trunc_sat_f64_to_s64 = 0x06_fc, | |
trunc_sat_f64_to_u64 = 0x07_fc, | |
} | |
Section :: enum u8 { | |
Custom = 0x00, | |
Type = 0x01, | |
Import = 0x02, | |
Function = 0x03, | |
Table = 0x04, | |
Memory = 0x05, | |
Global = 0x06, | |
Export = 0x07, | |
Start = 0x08, | |
Element = 0x09, | |
Code = 0x0a, | |
Data = 0x0b, | |
} | |
Emitter :: struct { | |
binary_data: [dynamic]byte, | |
} | |
Assembler :: struct { | |
emitter: Emitter, | |
signatures: [dynamic]Signature, | |
funcs: [dynamic]Function, | |
imports: [dynamic]Import, | |
globals: [dynamic]Global, | |
memories: [1]Memory, | |
memories_len: u32, | |
func_indices: map[string]u32, | |
global_indices: map[string]u32, | |
data_section_size: u32, | |
datums: [dynamic]Datum, | |
} | |
MAGIC := [4]byte{0x00, 0x61, 0x73, 0x6d}; | |
VERSION := [4]byte{0x01, 0x00, 0x00, 0x00}; | |
MAX_PARAM_LEN :: 16; | |
Signature :: struct { | |
index: u32, | |
params: [MAX_PARAM_LEN]Val_Type, | |
param_len: u8, | |
results: [1]Val_Type, | |
result_len: u8, | |
} | |
Location_Kind :: enum { | |
Internal = 0, | |
Export, | |
Import, | |
} | |
Function :: struct { | |
name: string, | |
sig: ^Signature, | |
kind: Location_Kind, | |
code: Emitter, | |
} | |
Import_Kind :: enum u8 { | |
Func = 0x00, | |
Table = 0x01, | |
Mem = 0x02, | |
Global = 0x03, | |
} | |
Import :: struct { | |
kind: Import_Kind, | |
module: string, | |
name: string, | |
type: union{Val_Type, ^Signature}, | |
} | |
Global_Expr :: union { | |
i32, i64, | |
f32, f64, | |
[]byte, | |
} | |
Global :: struct { | |
type: Val_Type, | |
is_const: bool, | |
expr: Global_Expr, | |
name: string, | |
kind: Location_Kind, | |
} | |
Memory :: struct { | |
limit_min: u32, | |
limit_max: u32, | |
name: string, | |
} | |
Datum :: struct { | |
offset: i32, | |
memory: []byte, | |
} | |
EXPORT_DESC_FUNC :: 0x00; | |
EXPORT_DESC_TABLE :: 0x01; | |
EXPORT_DESC_MEM :: 0x02; | |
EXPORT_DESC_GLOBAL :: 0x03; | |
emit_header :: proc(using e: ^Emitter) { | |
append(&binary_data, ..MAGIC[:]); | |
append(&binary_data, ..VERSION[:]); | |
} | |
emit_byte :: proc(using e: ^Emitter, b: byte) { | |
append(&binary_data, b); | |
} | |
emit_bytes :: proc(using e: ^Emitter, bytes: ..byte) { | |
append(&binary_data, ..bytes); | |
} | |
emit_type :: proc(using e: ^Emitter, t: Val_Type) { | |
append(&binary_data, byte(t)); | |
} | |
emit_op :: proc(using e: ^Emitter, op: Op) { | |
if op <= .GAP { | |
append(&binary_data, byte(op)); | |
} else { | |
data := transmute([2]u8)u16le(op); | |
append(&binary_data, data[0], data[1]); | |
} | |
} | |
u32_byte_length :: proc(u: int) -> u32 { | |
length := u32(0); | |
value := u32le(u); | |
for { | |
b := byte(value & 0x7f); | |
value >>= 7; | |
if value != 0 { | |
b |= 0x80; | |
} | |
length += 1; | |
if value == 0 { | |
break; | |
} | |
} | |
return length; | |
} | |
u32_byte_length32 :: proc(u: u32) -> u32 { | |
length := u32(0); | |
value := u32le(u); | |
for { | |
b := byte(value & 0x7f); | |
value >>= 7; | |
if value != 0 { | |
b |= 0x80; | |
} | |
length += 1; | |
if value == 0 { | |
break; | |
} | |
} | |
return length; | |
} | |
i32_byte_length32 :: proc(u: i32) -> u32 { | |
length := u32(0); | |
value := i32le(u); | |
negative := value < 0; | |
size := size_of(value)*8; | |
more := true; | |
for more { | |
b := byte(value & 0x7f); | |
value >>= 7; | |
if negative { | |
value |= ~i32le(0) << u32le(size - 7); | |
} | |
if value == 0 && (b & 0x40) != 0x40 || | |
value == -1 && (b & 0x40) == 0x40 { | |
more = false; | |
} else { | |
b |= 0x80; | |
} | |
length += 1; | |
} | |
return length; | |
} | |
u64_byte_length32 :: proc(u: u64) -> u32 { | |
length := u32(0); | |
value := u64le(u); | |
for { | |
b := byte(value & 0x7f); | |
value >>= 7; | |
if value != 0 { | |
b |= 0x80; | |
} | |
length += 1; | |
if value == 0 { | |
break; | |
} | |
} | |
return length; | |
} | |
i64_byte_length32 :: proc(u: i64) -> u32 { | |
length := u32(0); | |
value := i64le(u); | |
negative := value < 0; | |
size := size_of(value)*8; | |
more := true; | |
for more { | |
b := byte(value & 0x7f); | |
value >>= 7; | |
if negative { | |
value |= ~i64le(0) << u64le(size - 7); | |
} | |
if value == 0 && (b & 0x40) != 0x40 || | |
value == -1 && (b & 0x40) == 0x40 { | |
more = false; | |
} else { | |
b |= 0x80; | |
} | |
length += 1; | |
} | |
return length; | |
} | |
emit_u32 :: proc(using e: ^Emitter, u: u32) { | |
value := u32le(u); | |
for { | |
b := byte(value & 0x7f); | |
value >>= 7; | |
if value != 0 { | |
b |= 0x80; | |
} | |
append(&binary_data, b); | |
if value == 0 { | |
break; | |
} | |
} | |
} | |
emit_u64 :: proc(using e: ^Emitter, u: u64) { | |
value := u64le(u); | |
for { | |
b := byte(value & 0x7f); | |
value >>= 7; | |
if value != 0 { | |
b |= 0x80; | |
} | |
append(&binary_data, b); | |
if value == 0 { | |
break; | |
} | |
} | |
} | |
emit_i32 :: proc(using e: ^Emitter, u: i32) { | |
value := i32le(u); | |
negative := value < 0; | |
size := size_of(value)*8; | |
more := true; | |
for more { | |
b := byte(value & 0x7f); | |
value >>= 7; | |
if negative { | |
value |= ~i32le(0) << u32le(size - 7); | |
} | |
if value == 0 && (b & 0x40) != 0x40 || | |
value == -1 && (b & 0x40) == 0x40 { | |
more = false; | |
} else { | |
b |= 0x80; | |
} | |
append(&binary_data, b); | |
} | |
} | |
emit_i64 :: proc(using e: ^Emitter, u: i64) { | |
value := i64le(u); | |
negative := value < 0; | |
size := size_of(value)*8; | |
more := true; | |
for more { | |
b := byte(value & 0x7f); | |
value >>= 7; | |
if negative { | |
value |= ~i64le(0) << u64le(size - 7); | |
} | |
if value == 0 && (b & 0x40) != 0x40 || | |
value == -1 && (b & 0x40) == 0x40 { | |
more = false; | |
} else { | |
b |= 0x80; | |
} | |
append(&binary_data, b); | |
} | |
} | |
emit_f32 :: proc(using e: ^Emitter, f: f32) { | |
value := transmute([4]byte)f32le(f); | |
append(&binary_data, ..value[:]); | |
} | |
emit_f64 :: proc(using e: ^Emitter, f: f64) { | |
value := transmute([8]byte)f64le(f); | |
append(&binary_data, ..value[:]); | |
} | |
emit_name :: proc(using e: ^Emitter, name: string) { | |
n := u32(len(name)); | |
emit_u32(e, n); | |
append(&binary_data, name); | |
} | |
emit_name_byte_length :: proc(name: string) -> u32 { | |
return u32_byte_length(len(name)) + u32(len(name)); | |
} | |
emit_section :: proc(using e: ^Emitter, section: Section, size: u32) { | |
append(&binary_data, byte(section)); | |
emit_u32(e, size); | |
} | |
emit_call :: proc(e: ^Emitter, func_index: u32) { | |
emit_op(e, .Call); | |
emit_u32(e, func_index); | |
} | |
emit_limits_min :: proc(e: ^Emitter, min: u32) { | |
emit_byte(e, 0x00); | |
emit_u32(e, min); | |
} | |
emit_limits_min_max :: proc(e: ^Emitter, min, max: u32) { | |
emit_byte(e, 0x01); | |
emit_u32(e, min); | |
emit_u32(e, max); | |
} | |
emit_table_type_min :: proc(e: ^Emitter, min: u32) { | |
emit_byte(e, 0x70); | |
emit_limits_min(e, min); | |
} | |
emit_table_type_min_max :: proc(e: ^Emitter, min, max: u32) { | |
emit_byte(e, 0x70); | |
emit_limits_min_max(e, min, max); | |
} | |
emit_global_type :: proc(e: ^Emitter, t: Val_Type, is_const: bool) { | |
emit_type(e, t); | |
emit_byte(e, 0x01 if is_const else 0x00); | |
} | |
emit_const_i32 :: proc(e: ^Emitter, val: i32) { | |
emit_op(e, .Const_i32); emit_i32(e, val); | |
} | |
emit_const_i64 :: proc(e: ^Emitter, val: i64) { | |
emit_op(e, .Const_i64); emit_i64(e, val); | |
} | |
emit_const_f32 :: proc(e: ^Emitter, val: f32) { | |
emit_op(e, .Const_f32); emit_f32(e, val); | |
} | |
emit_const_f64 :: proc(e: ^Emitter, val: f64) { | |
emit_op(e, .Const_f64); emit_f64(e, val); | |
} | |
emit_mem_arg :: proc(e: ^Emitter, offset, align: u32) { | |
emit_u32(e, offset); | |
emit_u32(e, align); | |
} | |
emit_load_i32 :: proc(e: ^Emitter, offset: u32, align: u32 = align_of(i32le)) { | |
emit_op(e, .Load_i32); emit_mem_arg(e, offset, align); | |
} | |
emit_load_i64 :: proc(e: ^Emitter, offset: u32, align: u32 = align_of(i64le)) { | |
emit_op(e, .Load_i64); emit_mem_arg(e, offset, align); | |
} | |
emit_load_f32 :: proc(e: ^Emitter, offset: u32, align: u32 = align_of(f32le)) { | |
emit_op(e, .Load_f32); emit_mem_arg(e, offset, align); | |
} | |
emit_load_f64 :: proc(e: ^Emitter, offset: u32, align: u32 = align_of(f64le)) { | |
emit_op(e, .Load_f64); emit_mem_arg(e, offset, align); | |
} | |
emit_load_i32_as_s8 :: proc(e: ^Emitter, offset: u32, align: u32 = align_of(i32le)) { | |
emit_op(e, .Load_i32_as_s8); emit_mem_arg(e, offset, align); | |
} | |
emit_load_i32_as_u8 :: proc(e: ^Emitter, offset: u32, align: u32 = align_of(i32le)) { | |
emit_op(e, .Load_i32_as_u8); emit_mem_arg(e, offset, align); | |
} | |
emit_load_i32_as_s16 :: proc(e: ^Emitter, offset: u32, align: u32 = align_of(i32le)) { | |
emit_op(e, .Load_i32_as_s16); emit_mem_arg(e, offset, align); | |
} | |
emit_load_i32_as_u16 :: proc(e: ^Emitter, offset: u32, align: u32 = align_of(i32le)) { | |
emit_op(e, .Load_i32_as_u16); emit_mem_arg(e, offset, align); | |
} | |
emit_load_i64_as_s8 :: proc(e: ^Emitter, offset: u32, align: u32 = align_of(i64le)) { | |
emit_op(e, .Load_i64_as_s8); emit_mem_arg(e, offset, align); | |
} | |
emit_load_i64_as_u8 :: proc(e: ^Emitter, offset: u32, align: u32 = align_of(i64le)) { | |
emit_op(e, .Load_i64_as_u8); emit_mem_arg(e, offset, align); | |
} | |
emit_load_i64_as_s16 :: proc(e: ^Emitter, offset: u32, align: u32 = align_of(i64le)) { | |
emit_op(e, .Load_i64_as_s16); emit_mem_arg(e, offset, align); | |
} | |
emit_load_i64_as_u16 :: proc(e: ^Emitter, offset: u32, align: u32 = align_of(i64le)) { | |
emit_op(e, .Load_i64_as_u16); emit_mem_arg(e, offset, align); | |
} | |
emit_load_i64_as_s32 :: proc(e: ^Emitter, offset: u32, align: u32 = align_of(i64le)) { | |
emit_op(e, .Load_i64_as_s32); emit_mem_arg(e, offset, align); | |
} | |
emit_load_i64_as_u32 :: proc(e: ^Emitter, offset: u32, align: u32 = align_of(i64le)) { | |
emit_op(e, .Load_i64_as_u32); emit_mem_arg(e, offset, align); | |
} | |
emit_store_i32 :: proc(e: ^Emitter, offset: u32, align: u32 = align_of(i32le)) { | |
emit_op(e, .Store_i32); emit_mem_arg(e, offset, align); | |
} | |
emit_store_i64 :: proc(e: ^Emitter, offset: u32, align: u32 = align_of(i32le)) { | |
emit_op(e, .Store_i64); emit_mem_arg(e, offset, align); | |
} | |
emit_store_f32 :: proc(e: ^Emitter, offset: u32, align: u32 = align_of(i32le)) { | |
emit_op(e, .Store_f32); emit_mem_arg(e, offset, align); | |
} | |
emit_store_f64 :: proc(e: ^Emitter, offset: u32, align: u32 = align_of(i32le)) { | |
emit_op(e, .Store_f64); emit_mem_arg(e, offset, align); | |
} | |
emit_store_i32_i8 :: proc(e: ^Emitter, offset: u32, align: u32 = align_of(i32le)) { | |
emit_op(e, .Store_i32_i8); emit_mem_arg(e, offset, align); | |
} | |
emit_store_i32_i16 :: proc(e: ^Emitter, offset: u32, align: u32 = align_of(i32le)) { | |
emit_op(e, .Store_i32_i16); emit_mem_arg(e, offset, align); | |
} | |
emit_store_i64_i8 :: proc(e: ^Emitter, offset: u32, align: u32 = align_of(i32le)) { | |
emit_op(e, .Store_i64_i8); emit_mem_arg(e, offset, align); | |
} | |
emit_store_i64_i16 :: proc(e: ^Emitter, offset: u32, align: u32 = align_of(i32le)) { | |
emit_op(e, .Store_i64_i16); emit_mem_arg(e, offset, align); | |
} | |
emit_store_i64_i32 :: proc(e: ^Emitter, offset: u32, align: u32 = align_of(i32le)) { | |
emit_op(e, .Store_i64_i32); emit_mem_arg(e, offset, align); | |
} | |
signature_equal :: proc(a, b: Signature) -> bool { | |
if a.param_len != b.param_len { | |
return false; | |
} | |
if a.result_len != b.result_len { | |
return false; | |
} | |
return a.params == b.params && a.results == b.results; | |
} | |
signature :: proc(a: ^Assembler, params: []Val_Type, result: Val_Type) -> ^Signature { | |
assert(len(params) <= MAX_PARAM_LEN); | |
sig: Signature; | |
sig.param_len = min(u8(len(params)), len(sig.params)); | |
copy(sig.params[:], params); | |
sig.result_len = 1 if result != .void else 0; | |
sig.results[0] = result; | |
// TODO(bill): Hash Map Cache | |
for signature, i in a.signatures { | |
if signature_equal(sig, signature) { | |
return &a.signatures[i]; | |
} | |
} | |
index := u32(len(a.signatures)); | |
sig.index = index; | |
append(&a.signatures, sig); | |
return &a.signatures[index]; | |
} | |
create_function :: proc(a: ^Assembler, def: Function) -> ^Emitter { | |
assert(def.name not_in a.func_indices); | |
append(&a.funcs, def); | |
n := len(a.funcs)-1; | |
func := &a.funcs[n]; | |
a.func_indices[func.name] = u32(n + len(a.imports)); | |
return &func.code; | |
} | |
func_index :: proc(a: ^Assembler, name: string) -> u32 { | |
index, ok := a.func_indices[name]; | |
assert(ok); | |
return index; | |
} | |
func_sig_index :: proc(a: ^Assembler, name: string) -> u32 { | |
index, ok := a.func_indices[name]; | |
assert(ok); | |
return index; | |
} | |
get_func :: proc(a: ^Assembler, name: string) -> ^Function { | |
index, ok := a.func_indices[name]; | |
assert(ok); | |
return &a.funcs[index]; | |
} | |
import_function :: proc(a: ^Assembler, module, name: string, sig: ^Signature) -> u32 { | |
assert(len(a.funcs) == 0, "imports must happen before functions are declared"); | |
imp := Import{ | |
kind = .Func, | |
module = module, | |
name = name, | |
type = sig, | |
}; | |
append(&a.imports, imp); | |
return u32(len(a.imports)-1); | |
} | |
add_global :: proc(a: ^Assembler, global: Global) -> u32 { | |
n := u32(len(a.globals)); | |
g := global; | |
if g.type == .void { | |
switch x in g.expr { | |
case i32: g.type = .i32; | |
case i64: g.type = .i64; | |
case f32: g.type = .f32; | |
case f64: g.type = .f64; | |
case []byte: panic("a byte expression must specify a type"); | |
} | |
} | |
if g.expr == nil { | |
assert(g.type != .void); | |
} | |
if g.kind != .Internal { | |
assert(g.name != ""); | |
} | |
if g.name != "" { | |
a.global_indices[g.name] = u32(len(a.globals)); | |
} | |
append(&a.globals, g); | |
return n; | |
} | |
add_memory :: proc(a: ^Assembler, min: u32, name: string) { | |
assert(a.memories_len == 0); | |
min_pages := (min + PAGE_SIZE-1)/PAGE_SIZE; | |
a.memories[a.memories_len] = Memory{ | |
limit_min = min_pages, | |
limit_max = max(u32), | |
name = name, | |
}; | |
a.memories_len += 1; | |
} | |
add_datum :: proc(a: ^Assembler, offset: i32, data: []byte) { | |
datum := Datum { | |
offset = offset, | |
memory = data, | |
}; | |
append(&a.datums, datum); | |
a.data_section_size += u32(len(datum.memory)); | |
} | |
add_datum_string :: proc(a: ^Assembler, offset: i32, str: string) { | |
datum := Datum { | |
offset = offset, | |
memory = transmute([]byte)str, | |
}; | |
append(&a.datums, datum); | |
a.data_section_size += u32(len(datum.memory)); | |
} | |
generate_assembly :: proc(using a: ^Assembler) { | |
e := &emitter; | |
emit_header(e); | |
{ // Type Section | |
section_size := u32(0); | |
section_size += u32_byte_length(len(signatures)); | |
for sigature in signatures { | |
section_size += 1; // func type | |
// param vec count | |
section_size += u32_byte_length(int(sigature.param_len)); | |
// params | |
section_size += u32(sigature.param_len); | |
// result vec count | |
section_size += u32_byte_length(int(sigature.result_len)); | |
// results | |
section_size += u32(sigature.result_len); | |
} | |
emit_section(e, .Type, section_size); | |
emit_u32(e, u32(len(signatures))); | |
for sig in signatures { | |
emit_byte(e, 0x60); | |
emit_u32(e, u32(sig.param_len)); | |
for i in 0..<sig.param_len { | |
emit_type(e, sig.params[i]); | |
} | |
emit_u32(e, u32(sig.result_len)); | |
for i in 0..<sig.result_len { | |
emit_type(e, sig.results[i]); | |
} | |
} | |
} | |
{ // Import | |
section_size := u32(0); | |
section_size += u32_byte_length(len(imports)); | |
for imp in imports { | |
section_size += emit_name_byte_length(imp.module); | |
section_size += emit_name_byte_length(imp.name); | |
section_size += 1; | |
switch imp.kind { | |
case .Func: | |
sig := imp.type.(^Signature); | |
section_size += u32_byte_length(int(sig.index)); | |
case .Table: | |
panic("TODO: Import Table"); | |
case .Mem: | |
panic("TODO: Import Mem"); | |
case .Global: | |
panic("TODO: Import Global"); | |
} | |
} | |
emit_section(e, .Import, section_size); | |
emit_u32(e, u32(len(imports))); | |
for imp in imports { | |
emit_name(e, imp.module); | |
emit_name(e, imp.name); | |
emit_byte(e, byte(imp.kind)); | |
switch imp.kind { | |
case .Func: | |
sig := imp.type.(^Signature); | |
emit_u32(e, sig.index); | |
case .Table: | |
case .Mem: | |
case .Global: | |
} | |
} | |
} | |
{ // Function Section | |
section_size := u32(0); | |
section_size += u32_byte_length(len(funcs)); | |
for func in funcs { | |
section_size += u32_byte_length(int(func.sig.index)); | |
} | |
emit_section(e, .Function, section_size); | |
emit_u32(e, u32(len(funcs))); | |
for func in funcs { | |
emit_u32(e, func.sig.index); | |
} | |
} | |
{ // Memory Section | |
section_size := u32(0); | |
section_size += u32_byte_length32(memories_len); | |
for mem in memories[:memories_len] { | |
section_size += 1; | |
section_size += u32_byte_length32(mem.limit_min); | |
if mem.limit_max != max(u32) { | |
section_size += u32_byte_length32(mem.limit_max); | |
} | |
} | |
emit_section(e, .Memory, section_size); | |
emit_u32(e, memories_len); | |
for mem in memories[:memories_len] { | |
if mem.limit_max == max(u32) { | |
emit_limits_min(e, mem.limit_min); | |
} else { | |
emit_limits_min_max(e, mem.limit_min, mem.limit_max); | |
} | |
} | |
} | |
{ // Global Section | |
section_size := u32(0); | |
section_size += u32_byte_length(len(globals)); | |
for global in globals { | |
section_size += 2; | |
switch x in global.expr { | |
case i32: section_size += 1 + i32_byte_length32(x); | |
case i64: section_size += 1 + i64_byte_length32(x); | |
case f32: section_size += 1 + 4; | |
case f64: section_size += 1 + 8; | |
case []byte: section_size += u32(len(x)); | |
} | |
section_size += 1; | |
} | |
emit_section(e, .Global, section_size); | |
emit_u32(e, u32(len(globals))); | |
for global in globals { | |
emit_global_type(e, global.type, global.is_const); | |
switch x in global.expr { | |
case i32: emit_const_i32(e, x); | |
case i64: emit_const_i64(e, x); | |
case f32: emit_const_f32(e, x); | |
case f64: emit_const_f64(e, x); | |
case []byte: emit_bytes(e, ..x); | |
} | |
emit_op(e, .End); | |
} | |
} | |
{ // Export Section | |
section_size := u32(0); | |
exported_count := u32(0); | |
for f, i in funcs { | |
if f.kind != .Export do continue; | |
exported_count += 1; | |
section_size += u32_byte_length(len(f.name)); | |
section_size += u32(len(f.name)); | |
section_size += 1; | |
section_size += u32_byte_length(i); | |
} | |
for mem, i in memories[:memories_len] { | |
if mem.name == "" do continue; | |
exported_count += 1; | |
section_size += u32_byte_length(len(mem.name)); | |
section_size += u32(len(mem.name)); | |
section_size += 1; | |
section_size += u32_byte_length(i); | |
} | |
for g, i in globals { | |
if g.kind != .Export do continue; | |
exported_count += 1; | |
section_size += u32_byte_length(len(g.name)); | |
section_size += u32(len(g.name)); | |
section_size += 1; | |
section_size += u32_byte_length(i); | |
} | |
section_size += u32_byte_length32(exported_count); | |
emit_section(e, .Export, section_size); | |
emit_u32(e, exported_count); | |
for f, i in funcs { | |
if f.kind != .Export do continue; | |
emit_name(e, f.name); | |
emit_byte(e, EXPORT_DESC_FUNC); | |
emit_u32(e, func_index(a, f.name)); | |
} | |
for mem, i in memories[:memories_len] { | |
if mem.name == "" do continue; | |
emit_name(e, mem.name); | |
emit_byte(e, EXPORT_DESC_MEM); | |
emit_u32(e, u32(i)); | |
} | |
for g, i in globals { | |
if g.kind != .Export do continue; | |
emit_name(e, g.name); | |
emit_byte(e, EXPORT_DESC_GLOBAL); | |
emit_u32(e, u32(i)); | |
} | |
} | |
{ // Code Section | |
section_size := u32(0); | |
section_size += u32_byte_length(len(funcs)); | |
for f in funcs { | |
section_size += u32_byte_length(len(f.code.binary_data)); | |
section_size += u32(len(f.code.binary_data)); | |
} | |
emit_section(e, .Code, section_size); | |
emit_u32(e, u32(len(funcs))); | |
for f in funcs { | |
emit_u32(e, u32(len(f.code.binary_data))); | |
emit_bytes(e, ..f.code.binary_data[:]); | |
} | |
} | |
{ // Data Section | |
section_size := u32(0); | |
section_size += u32_byte_length(len(datums)); | |
for datum in datums { | |
section_size += u32_byte_length32(0); | |
section_size += 1; | |
section_size += i32_byte_length32(datum.offset); | |
section_size += 1; | |
section_size += u32_byte_length(len(datum.memory)); | |
section_size += u32(len(datum.memory)); | |
} | |
emit_section(e, .Data, section_size); | |
emit_u32(e, u32(len(datums))); | |
for datum in datums { | |
emit_u32(e, 0); | |
emit_const_i32(e, datum.offset); | |
emit_op(e, .End); | |
emit_u32(e, u32(len(datum.memory))); | |
emit_bytes(e, ..datum.memory); | |
} | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment