Skip to content

Instantly share code, notes, and snippets.

@gingerBill
Last active May 6, 2020 14:26
Show Gist options
  • Save gingerBill/2b6627c58bfb59122e49c3ca54b3702e to your computer and use it in GitHub Desktop.
Save gingerBill/2b6627c58bfb59122e49c3ca54b3702e to your computer and use it in GitHub Desktop.
Basic WASM Assembler in Odin
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