// rexcode ยท Brendan Punsky (dotbmp@github), original author // Ginger Bill (gingerBill@github) package rexcode_wasm import "core:strings" import "core:strconv" import "core:os" import "core:io" import "core:rexcode/isa" // ============================================================================= // WebAssembly PRINTER // ============================================================================= // // Emits WebAssembly text-format (WAT) instruction syntax: the folded-stack // form is not reconstructed (that needs structure the linear stream does not // carry); instead each instruction prints on its own line as // // * // // Examples: // // i32.const 42 // local.get 0 // i32.add // call 3 // block (result i32) // i32.load offset=8 align=2 // br_table 0 1 2 ; cases 0 1, default 2 // ref.null func // f64.const 3.14 // // Mnemonic spelling comes from the explicit MNEMONIC_NAMES table (WASM mixes // '.' and '_' irregularly, e.g. `local.get` vs `i32.trunc_f32_s`). WASM has // no register file, so register printing is vestigial. Token :: isa.Token Token_Kind :: isa.Token_Kind Print_Options :: isa.Print_Options Print_Result :: isa.Print_Result DEFAULT_PRINT_OPTIONS :: isa.DEFAULT_PRINT_OPTIONS mnemonic_to_string :: proc(m: Mnemonic, lowercase: bool = true, allocator := context.temp_allocator) -> string { sb := strings.builder_make(allocator) write_mnemonic(&sb, m, !lowercase) return strings.to_string(sb) } // ============================================================================= // Core sbprint // ============================================================================= sbprint :: proc( sb: ^strings.Builder, instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition, tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil, ) { opts := options if opts == nil { @(static) defaults := DEFAULT_PRINT_OPTIONS opts = &defaults } for &inst, i in instructions { offset := inst_info[i].offset if i < len(inst_info) else u32(0) strings.write_string(sb, opts.indent) if opts.show_offsets { isa.print_hex(sb, u64(offset), opts) strings.write_string(sb, ": ") } write_mnemonic(sb, inst.mnemonic, opts.uppercase) // br_table prints its case vector followed by the default depth. #partial switch inst.mnemonic { case .BR_TABLE: for t in inst.targets { strings.write_byte(sb, ' ') write_decimal_u32(sb, t) } strings.write_byte(sb, ' ') write_decimal_u32(sb, inst.ops[0].index) case .V128_CONST: strings.write_string(sb, " i8x16") for bb in inst.bytes { strings.write_byte(sb, ' ') isa.print_hex(sb, u64(bb), opts) } case .I8X16_SHUFFLE: for bb in inst.bytes { strings.write_byte(sb, ' ') write_decimal_u32(sb, u32(bb)) } case: for slot in 0.. string { sb := strings.builder_make(allocator) sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names) return strings.to_string(sb) } aprintln :: proc( instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition, tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil, allocator := context.allocator, ) -> string { sb := strings.builder_make(allocator) sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names) return strings.to_string(sb) } tprint :: proc( instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition, tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil, ) -> string { sb := strings.builder_make(context.temp_allocator) sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names) return strings.to_string(sb) } tprintln :: proc( instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition, tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil, ) -> string { sb := strings.builder_make(context.temp_allocator) sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names) return strings.to_string(sb) } bprint :: proc( buf: []u8, instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition, tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil, ) -> string { sb := strings.builder_from_bytes(buf) sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names) return strings.to_string(sb) } bprintln :: proc( buf: []u8, instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition, tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil, ) -> string { sb := strings.builder_from_bytes(buf) sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names) return strings.to_string(sb) } fprint :: proc( fd: ^os.File, instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition, tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil, ) { sb := strings.builder_make(context.temp_allocator) sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names) os.write_string(fd, strings.to_string(sb)) } fprintln :: proc( fd: ^os.File, instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition, tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil, ) { sb := strings.builder_make(context.temp_allocator) sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names) os.write_string(fd, strings.to_string(sb)) } wprint :: proc( w: io.Writer, instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition, tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil, ) { sb := strings.builder_make(context.temp_allocator) sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names) io.write_string(w, strings.to_string(sb)) } wprintln :: proc( w: io.Writer, instructions: []Instruction, inst_info: []Instruction_Info, label_defs: []Label_Definition, tokens: ^[dynamic]Token = nil, options: ^Print_Options = nil, label_names: ^map[u32]string = nil, ) { sb := strings.builder_make(context.temp_allocator) sbprintln(&sb, instructions, inst_info, label_defs, tokens, options, label_names) io.write_string(w, strings.to_string(sb)) } // ============================================================================= // Internal writers // ============================================================================= @(private="file") write_mnemonic :: proc(sb: ^strings.Builder, m: Mnemonic, uppercase: bool) { name := MNEMONIC_NAMES[m] if name == "" { strings.write_string(sb, ""); return } if uppercase { for i in 0.. 0; i += 1 { buf[i] = '0' + u8(n % 10) n /= 10 } for j := i - 1; j >= 0; j -= 1 { strings.write_byte(sb, buf[j]) } } @(private="file") write_signed_decimal :: proc(sb: ^strings.Builder, v: i64) { if v < 0 { strings.write_byte(sb, '-') write_decimal_u64(sb, u64(-(v + 1)) + 1) } else { write_decimal_u64(sb, u64(v)) } } @(private="file") write_decimal_u64 :: proc(sb: ^strings.Builder, v: u64) { if v == 0 { strings.write_byte(sb, '0') return } buf: [20]u8 i := 0 for n := v; n > 0; i += 1 { buf[i] = '0' + u8(n % 10) n /= 10 } for j := i - 1; j >= 0; j -= 1 { strings.write_byte(sb, buf[j]) } }