mirror of
https://github.com/odin-lang/Odin.git
synced 2026-06-19 08:32:33 +00:00
Add core:rexcode/wasm/module
This commit is contained in:
@@ -19,8 +19,7 @@ import "base:runtime"
|
||||
// its immediates in declaration order, reconstructing Operands.
|
||||
//
|
||||
// WASM control flow is structured (branches carry relative label depths, not
|
||||
// byte offsets), so there is no PC-relative label inference -- `label_defs`
|
||||
// is part of the universal signature but left untouched. Object-file index
|
||||
// byte offsets), so there is no PC-relative label inference. Object-file index
|
||||
// relocations *are* re-attached: when an input relocation lands on a decoded
|
||||
// index field, that operand is marked `symbolic` and carries the label id.
|
||||
//
|
||||
@@ -39,7 +38,6 @@ decode :: proc(
|
||||
relocs: []Relocation,
|
||||
instructions: ^[dynamic]Instruction,
|
||||
inst_info: ^[dynamic]Instruction_Info,
|
||||
label_defs: ^[dynamic]Label_Definition,
|
||||
errors: ^[dynamic]Error,
|
||||
targets_allocator := context.allocator,
|
||||
) -> (byte_count: u32, ok: bool) {
|
||||
@@ -157,6 +155,8 @@ decode_one :: proc(
|
||||
op.index = lid
|
||||
op.flags.symbolic = true
|
||||
op.size = 5
|
||||
} else if m == .CALL {
|
||||
op.flags.symbolic = true
|
||||
}
|
||||
inst.ops[slot] = op
|
||||
slot += 1
|
||||
@@ -229,15 +229,15 @@ decode_one :: proc(
|
||||
@(private="file")
|
||||
idx_kind_for :: #force_inline proc "contextless" (m: Mnemonic, which: int) -> Index_Kind {
|
||||
#partial switch m {
|
||||
case .BR, .BR_IF: return .LABEL
|
||||
case .CALL, .REF_FUNC: return .FUNC
|
||||
case .CALL_INDIRECT: return which == 0 ? .TYPE : .TABLE
|
||||
case .LOCAL_GET, .LOCAL_SET, .LOCAL_TEE: return .LOCAL
|
||||
case .GLOBAL_GET, .GLOBAL_SET: return .GLOBAL
|
||||
case .MEMORY_INIT, .DATA_DROP: return .DATA
|
||||
case .TABLE_INIT: return which == 0 ? .ELEM : .TABLE
|
||||
case .ELEM_DROP: return .ELEM
|
||||
case .TABLE_COPY: return .TABLE
|
||||
case .BR, .BR_IF: return .LABEL
|
||||
case .CALL, .REF_FUNC: return .FUNC
|
||||
case .CALL_INDIRECT: return which == 0 ? .TYPE : .TABLE
|
||||
case .LOCAL_GET, .LOCAL_SET, .LOCAL_TEE: return .LOCAL
|
||||
case .GLOBAL_GET, .GLOBAL_SET: return .GLOBAL
|
||||
case .MEMORY_INIT, .DATA_DROP: return .DATA
|
||||
case .TABLE_INIT: return which == 0 ? .ELEM : .TABLE
|
||||
case .ELEM_DROP: return .ELEM
|
||||
case .TABLE_COPY: return .TABLE
|
||||
case .TABLE_GROW, .TABLE_SIZE, .TABLE_FILL: return .TABLE
|
||||
}
|
||||
return .NONE
|
||||
|
||||
@@ -45,9 +45,8 @@ encode :: proc(
|
||||
) -> (byte_count: u32, ok: bool) {
|
||||
errors_start := u32(len(errors))
|
||||
|
||||
for i in 0..<u32(len(instructions)) {
|
||||
inst := &instructions[i]
|
||||
n := encode_one(inst, byte_count, u16(i), code, relocs, errors) or_return
|
||||
for &inst, i in instructions {
|
||||
n := encode_one(&inst, byte_count, u16(i), code, relocs, errors) or_return
|
||||
inst.length = u8(min(n, 255))
|
||||
byte_count += n
|
||||
}
|
||||
@@ -56,11 +55,7 @@ encode :: proc(
|
||||
return
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Internal
|
||||
// =============================================================================
|
||||
|
||||
@(private="file")
|
||||
encode_one :: #force_inline proc(
|
||||
inst: ^Instruction,
|
||||
pc: u32,
|
||||
|
||||
@@ -78,7 +78,9 @@ write_uleb :: #force_inline proc "contextless" (code: []u8, offset: ^u32, value:
|
||||
for {
|
||||
b := u8(v & 0x7F)
|
||||
v >>= 7
|
||||
if v != 0 { b |= 0x80 }
|
||||
if v != 0 {
|
||||
b |= 0x80
|
||||
}
|
||||
code[offset^] = b
|
||||
offset^ += 1
|
||||
if v == 0 {
|
||||
@@ -94,7 +96,9 @@ write_sleb :: #force_inline proc "contextless" (code: []u8, offset: ^u32, value:
|
||||
b := u8(v & 0x7F)
|
||||
v >>= 7 // arithmetic shift on signed value sign-extends
|
||||
done := (v == 0 && (b & 0x40) == 0) || (v == -1 && (b & 0x40) != 0)
|
||||
if !done { b |= 0x80 }
|
||||
if !done {
|
||||
b |= 0x80
|
||||
}
|
||||
code[offset^] = b
|
||||
offset^ += 1
|
||||
if done {
|
||||
@@ -109,7 +113,9 @@ write_uleb_padded5 :: #force_inline proc "contextless" (code: []u8, offset: ^u32
|
||||
for i := 0; i < 5 && offset^ < u32(len(code)); i += 1 {
|
||||
b := u8(v & 0x7F)
|
||||
v >>= 7
|
||||
if i != 4 { b |= 0x80 }
|
||||
if i != 4 {
|
||||
b |= 0x80
|
||||
}
|
||||
code[offset^] = b
|
||||
offset^ += 1
|
||||
}
|
||||
|
||||
151
core/rexcode/wasm/module/module.odin
Normal file
151
core/rexcode/wasm/module/module.odin
Normal file
@@ -0,0 +1,151 @@
|
||||
package rexcode_wasm_module
|
||||
|
||||
import "base:runtime"
|
||||
import "core:rexcode/wasm"
|
||||
|
||||
WASM_MAGIC :: u32(0x6d736100) // "\0asm" as a little-endian u32
|
||||
WASM_VERSION :: u32(1)
|
||||
|
||||
Section_Id :: enum u8 { // Binary section ids (WebAssembly core spec §5.5.2).
|
||||
CUSTOM = 0,
|
||||
TYPE = 1,
|
||||
IMPORT = 2,
|
||||
FUNCTION = 3,
|
||||
TABLE = 4,
|
||||
MEMORY = 5,
|
||||
GLOBAL = 6,
|
||||
EXPORT = 7,
|
||||
START = 8,
|
||||
ELEMENT = 9,
|
||||
CODE = 10,
|
||||
DATA = 11,
|
||||
DATA_COUNT = 12,
|
||||
}
|
||||
|
||||
Section :: struct {
|
||||
id: Section_Id,
|
||||
offset: u32, // file offset of the section *contents*
|
||||
size: u32, // contents length in bytes
|
||||
count: u32, // element count
|
||||
name: string, // custom-section name (borrowed)
|
||||
}
|
||||
|
||||
External_Kind :: enum u8 {
|
||||
FUNC = 0,
|
||||
TABLE = 1,
|
||||
MEMORY = 2,
|
||||
GLOBAL = 3,
|
||||
}
|
||||
|
||||
@(rodata)
|
||||
external_kind_string := [External_Kind]string{
|
||||
.FUNC = "func",
|
||||
.TABLE = "table",
|
||||
.MEMORY = "memory",
|
||||
.GLOBAL = "global",
|
||||
}
|
||||
|
||||
Func_Type :: struct {
|
||||
params: []wasm.Value_Type,
|
||||
results: []wasm.Value_Type,
|
||||
}
|
||||
|
||||
Import :: struct {
|
||||
kind: External_Kind,
|
||||
module_name: string, // borrowed
|
||||
field_name: string, // borrowed
|
||||
index: u32, // typeidx for FUNC, 0 for other kinds
|
||||
}
|
||||
|
||||
Export :: struct {
|
||||
kind: External_Kind,
|
||||
name: string, // borrowed
|
||||
index: u32,
|
||||
}
|
||||
|
||||
// A compressed run of declared locals (e.g. `3 x i32`)
|
||||
Local_Group :: struct {
|
||||
count: u32,
|
||||
type: wasm.Value_Type,
|
||||
}
|
||||
|
||||
// A function in the module's function index space.
|
||||
// Imported functions occupy the low indices, followed by the module-defined functions.
|
||||
Function :: struct {
|
||||
func_index: u32,
|
||||
type_index: u32,
|
||||
type: Func_Type, // resolved signature ({} if the type id was out of range)
|
||||
imported: bool,
|
||||
name: string, // export / name-section / import field (borrowed)
|
||||
import_module: string, // borrowed, "" for defined functions
|
||||
import_field: string, // borrowed, "" for defined functions
|
||||
|
||||
// defined functions only:
|
||||
locals: []Local_Group,
|
||||
body_offset: u32, // file offset of the instruction stream
|
||||
body_size: u32, // instruction-stream length in bytes
|
||||
}
|
||||
|
||||
Module :: struct {
|
||||
version: u32,
|
||||
sections: []Section,
|
||||
types: []Func_Type,
|
||||
imports: []Import,
|
||||
functions: []Function, // whole function index space (imports + defined)
|
||||
exports: []Export,
|
||||
start: i64, // -1 if absent, else the start funcidx
|
||||
|
||||
data: []u8, // borrowed reference to the whole file (body decode reads from it)
|
||||
|
||||
allocator: runtime.Allocator,
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Small display helpers
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
|
||||
@(require_results)
|
||||
section_name :: proc(id: Section_Id) -> string {
|
||||
switch id {
|
||||
case .CUSTOM: return "custom"
|
||||
case .TYPE: return "type"
|
||||
case .IMPORT: return "import"
|
||||
case .FUNCTION: return "function"
|
||||
case .TABLE: return "table"
|
||||
case .MEMORY: return "memory"
|
||||
case .GLOBAL: return "global"
|
||||
case .EXPORT: return "export"
|
||||
case .START: return "start"
|
||||
case .ELEMENT: return "element"
|
||||
case .CODE: return "code"
|
||||
case .DATA: return "data"
|
||||
case .DATA_COUNT: return "data.count"
|
||||
}
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
valtype_name :: proc(t: wasm.Value_Type) -> string {
|
||||
switch t {
|
||||
case .I32: return "i32"
|
||||
case .I64: return "i64"
|
||||
case .F32: return "f32"
|
||||
case .F64: return "f64"
|
||||
case .V128: return "v128"
|
||||
case .FUNCREF: return "funcref"
|
||||
case .EXTERNREF: return "externref"
|
||||
}
|
||||
return "?"
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
external_kind_name :: proc(k: External_Kind) -> string {
|
||||
switch k {
|
||||
case .FUNC: return "func"
|
||||
case .TABLE: return "table"
|
||||
case .MEMORY: return "memory"
|
||||
case .GLOBAL: return "global"
|
||||
}
|
||||
return "?"
|
||||
}
|
||||
402
core/rexcode/wasm/module/parse.odin
Normal file
402
core/rexcode/wasm/module/parse.odin
Normal file
@@ -0,0 +1,402 @@
|
||||
package rexcode_wasm_module
|
||||
|
||||
import "base:runtime"
|
||||
import "core:rexcode/wasm"
|
||||
|
||||
Parse_Error :: enum {
|
||||
NONE = 0,
|
||||
TRUNCATED,
|
||||
BAD_MAGIC,
|
||||
BAD_TYPE_FORM, // a functype did not start with 0x60
|
||||
BAD_SECTION, // section contents extend past the section size
|
||||
BAD_ULEB, // ULEB number didn't stop after 10 bytes
|
||||
}
|
||||
|
||||
Reader_Error :: union #shared_nil {
|
||||
Parse_Error,
|
||||
runtime.Allocator_Error,
|
||||
}
|
||||
|
||||
Reader :: struct {
|
||||
data: []u8,
|
||||
off: u32,
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
reader :: proc(data: []u8, off: u32) -> Reader {
|
||||
return Reader{data = data, off = off}
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
rd_byte :: proc(r: ^Reader) -> (u8, Parse_Error) {
|
||||
if r.off >= u32(len(r.data)) {
|
||||
return 0, .TRUNCATED
|
||||
}
|
||||
b := r.data[r.off]
|
||||
r.off += 1
|
||||
return b, .NONE
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
rd_u32le_block :: proc(r: ^Reader) -> (u32, Parse_Error) {
|
||||
if r.off + 4 > u32(len(r.data)) {
|
||||
return 0, .TRUNCATED
|
||||
}
|
||||
v := u32(r.data[r.off]) |
|
||||
u32(r.data[r.off+1])<<8 |
|
||||
u32(r.data[r.off+2])<<16 |
|
||||
u32(r.data[r.off+3])<<24
|
||||
r.off += 4
|
||||
return v, .NONE
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
rd_uleb :: proc(r: ^Reader) -> (u64, Parse_Error) {
|
||||
shift: uint = 0
|
||||
value: u64 = 0
|
||||
for _ in 0..<10 {
|
||||
if r.off >= u32(len(r.data)) {
|
||||
return 0, .TRUNCATED
|
||||
}
|
||||
b := r.data[r.off]
|
||||
r.off += 1
|
||||
value |= u64(b & 0x7F) << shift
|
||||
if b & 0x80 == 0 {
|
||||
return value, .NONE
|
||||
}
|
||||
shift += 7
|
||||
}
|
||||
return 0, .BAD_ULEB
|
||||
}
|
||||
|
||||
// Signed-LEB128 reader.
|
||||
@(require_results)
|
||||
rd_sleb :: proc(r: ^Reader) -> (i64, Parse_Error) {
|
||||
shift: uint = 0
|
||||
value: i64 = 0
|
||||
b: u8 = 0
|
||||
for _ in 0..<10 {
|
||||
if r.off >= u32(len(r.data)) {
|
||||
return 0, .TRUNCATED
|
||||
}
|
||||
b = r.data[r.off]
|
||||
r.off += 1
|
||||
value |= i64(b & 0x7F) << shift
|
||||
shift += 7
|
||||
if b & 0x80 == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if shift < 64 && (b & 0x40) != 0 {
|
||||
value |= -(i64(1) << shift)
|
||||
}
|
||||
return value, .NONE
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
rd_u32 :: proc(r: ^Reader) -> (u32, Parse_Error) {
|
||||
v, err := rd_uleb(r)
|
||||
return u32(v), err
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
rd_name :: proc(r: ^Reader) -> (val: string, err: Parse_Error) {
|
||||
n := rd_u32(r) or_return
|
||||
if r.off + n > u32(len(r.data)) {
|
||||
err = .TRUNCATED
|
||||
return
|
||||
}
|
||||
val = string(r.data[r.off:][:n])
|
||||
r.off += n
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
rd_valtype_vec :: proc(r: ^Reader, allocator: runtime.Allocator) -> (out: []wasm.Value_Type, err: Reader_Error) {
|
||||
n := rd_u32(r) or_return
|
||||
|
||||
// now actually parse it
|
||||
out = make([]wasm.Value_Type, int(n), allocator) or_return
|
||||
for &v in out {
|
||||
v = wasm.Value_Type(rd_byte(r) or_return)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
rd_limits :: proc(r: ^Reader) -> (min: u64, max: Maybe(u64), err: Parse_Error) {
|
||||
flags := rd_byte(r) or_return
|
||||
min = rd_uleb(r) or_return
|
||||
if flags & 0x01 != 0 {
|
||||
max = rd_uleb(r) or_return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
parse :: proc(data: []u8, allocator := context.allocator) -> (m: Module, err: Reader_Error) {
|
||||
context.allocator = allocator
|
||||
|
||||
m.data = data
|
||||
m.start = -1
|
||||
m.allocator = allocator
|
||||
|
||||
r := reader(data, 0)
|
||||
if (rd_u32le_block(&r) or_else 0) != WASM_MAGIC {
|
||||
return m, .BAD_MAGIC
|
||||
}
|
||||
m.version = rd_u32le_block(&r) or_return
|
||||
|
||||
secs: [dynamic]Section
|
||||
for r.off < u32(len(data)) {
|
||||
id := Section_Id(rd_byte(&r) or_return)
|
||||
size := rd_u32(&r) or_return
|
||||
content := r.off
|
||||
if content + size > u32(len(data)) {
|
||||
return m, .BAD_SECTION
|
||||
}
|
||||
|
||||
sec := Section{id = id, offset = content, size = size}
|
||||
switch id {
|
||||
case .CUSTOM:
|
||||
sub := reader(data, content)
|
||||
sec.name = rd_name(&sub) or_return
|
||||
case .START:
|
||||
// funcidx, no vector count
|
||||
case .TYPE, .IMPORT, .FUNCTION, .TABLE, .MEMORY, .GLOBAL,
|
||||
.EXPORT, .ELEMENT, .CODE, .DATA, .DATA_COUNT:
|
||||
sub := reader(data, content)
|
||||
sec.count = rd_u32(&sub) or_return
|
||||
}
|
||||
append(&secs, sec) or_return
|
||||
r.off = content + size
|
||||
}
|
||||
m.sections = secs[:]
|
||||
|
||||
|
||||
func_typeidx: []u32
|
||||
codes: []Code_Body
|
||||
|
||||
for &sec in m.sections {
|
||||
s := reader(data, sec.offset)
|
||||
#partial switch sec.id {
|
||||
case .TYPE: m.types = parse_types (&s, allocator) or_return
|
||||
case .IMPORT: m.imports = parse_imports (&s, allocator) or_return
|
||||
case .FUNCTION: func_typeidx = parse_function_section(&s, allocator) or_return
|
||||
case .EXPORT: m.exports = parse_exports (&s, allocator) or_return
|
||||
case .CODE: codes = parse_code (&s, allocator) or_return
|
||||
case .START: m.start = i64(rd_u32(&s) or_return)
|
||||
}
|
||||
}
|
||||
|
||||
build_functions(&m, func_typeidx, codes, allocator) or_return
|
||||
apply_name_section(&m)
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
parse_types :: proc(r: ^Reader, allocator: runtime.Allocator) -> (out: []Func_Type, err: Reader_Error) {
|
||||
n := rd_u32(r) or_return
|
||||
out = make([]Func_Type, int(n), allocator) or_return
|
||||
for &func in out {
|
||||
form := rd_byte(r) or_return
|
||||
if form != 0x60 {
|
||||
return out, .BAD_TYPE_FORM
|
||||
}
|
||||
func.params = rd_valtype_vec(r, allocator) or_return
|
||||
func.results = rd_valtype_vec(r, allocator) or_return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
parse_imports :: proc(r: ^Reader, allocator: runtime.Allocator) -> (out: []Import, err: Reader_Error) {
|
||||
n := rd_u32(r) or_return
|
||||
out = make([]Import, int(n), allocator) or_return
|
||||
for &imp in out {
|
||||
imp.module_name = rd_name(r) or_return
|
||||
imp.field_name = rd_name(r) or_return
|
||||
imp.kind = External_Kind(rd_byte(r) or_return)
|
||||
switch imp.kind {
|
||||
case .FUNC:
|
||||
imp.index = rd_u32(r) or_return
|
||||
case .TABLE:
|
||||
_ = rd_byte(r) or_return // reftype
|
||||
_, _ = rd_limits(r) or_return
|
||||
case .MEMORY:
|
||||
_, _ = rd_limits(r) or_return
|
||||
case .GLOBAL:
|
||||
_ = rd_byte(r) or_return // valtype
|
||||
_ = rd_byte(r) or_return // mutability
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
parse_function_section :: proc(r: ^Reader, allocator: runtime.Allocator) -> (out: []u32, err: Reader_Error) {
|
||||
n := rd_u32(r) or_return
|
||||
out = make([]u32, int(n), allocator) or_return
|
||||
for &idx in out {
|
||||
idx = rd_u32(r) or_return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
parse_exports :: proc(r: ^Reader, allocator: runtime.Allocator) -> (out: []Export, err: Reader_Error) {
|
||||
n := rd_u32(r) or_return
|
||||
out = make([]Export, int(n), allocator) or_return
|
||||
for &e in out {
|
||||
e.name = rd_name(r) or_return
|
||||
e.kind = External_Kind(rd_byte(r) or_return)
|
||||
e.index = rd_u32(r) or_return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
Code_Body :: struct {
|
||||
locals: []Local_Group,
|
||||
body_offset: u32,
|
||||
body_size: u32,
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
parse_code :: proc(r: ^Reader, allocator: runtime.Allocator) -> (out: []Code_Body, err: Reader_Error) {
|
||||
n := rd_u32(r) or_return
|
||||
out = make([]Code_Body, int(n), allocator) or_return
|
||||
for &code_body in out {
|
||||
total := rd_u32(r) or_return
|
||||
body_start := r.off
|
||||
body_end := body_start + total
|
||||
|
||||
nl := rd_u32(r) or_return
|
||||
locals := make([]Local_Group, int(nl), allocator) or_return
|
||||
for &local in locals {
|
||||
cnt := rd_u32(r) or_return
|
||||
t := wasm.Value_Type(rd_byte(r) or_return)
|
||||
local = Local_Group{count = cnt, type = t}
|
||||
}
|
||||
code_body = Code_Body{
|
||||
locals = locals,
|
||||
body_offset = r.off,
|
||||
body_size = body_end > r.off ? body_end - r.off : 0,
|
||||
}
|
||||
r.off = body_end // jump past the expression to the next entry
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
build_functions :: proc(m: ^Module, func_typeidx: []u32, codes: []Code_Body, allocator: runtime.Allocator) -> runtime.Allocator_Error {
|
||||
num_imports := 0
|
||||
for imp in m.imports {
|
||||
if imp.kind == .FUNC {
|
||||
num_imports += 1
|
||||
}
|
||||
}
|
||||
total := num_imports + len(func_typeidx)
|
||||
if total == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
funcs := make([]Function, total, allocator) or_return
|
||||
|
||||
idx := 0
|
||||
for imp in m.imports {
|
||||
(imp.kind == .FUNC) or_continue
|
||||
f := Function{
|
||||
func_index = u32(idx),
|
||||
type_index = imp.index,
|
||||
imported = true,
|
||||
name = imp.field_name,
|
||||
import_module = imp.module_name,
|
||||
import_field = imp.field_name,
|
||||
}
|
||||
if int(imp.index) < len(m.types) {
|
||||
f.type = m.types[imp.index]
|
||||
}
|
||||
funcs[idx] = f
|
||||
idx += 1
|
||||
}
|
||||
|
||||
for tidx, i in func_typeidx {
|
||||
fi := num_imports + i
|
||||
f := Function{
|
||||
func_index = u32(fi),
|
||||
type_index = tidx,
|
||||
imported = false,
|
||||
}
|
||||
if int(tidx) < len(m.types) {
|
||||
f.type = m.types[tidx]
|
||||
}
|
||||
if i < len(codes) {
|
||||
c := &codes[i]
|
||||
f.locals = c.locals
|
||||
f.body_offset = c.body_offset
|
||||
f.body_size = c.body_size
|
||||
}
|
||||
funcs[fi] = f
|
||||
}
|
||||
|
||||
for e in m.exports {
|
||||
if e.kind == .FUNC && int(e.index) < total && funcs[e.index].name == "" {
|
||||
funcs[e.index].name = e.name
|
||||
}
|
||||
}
|
||||
|
||||
m.functions = funcs
|
||||
return nil
|
||||
}
|
||||
|
||||
// Override function names with debug names from the "name" custom section's function-names subsection (id 1), if it exists.
|
||||
apply_name_section :: proc(m: ^Module) {
|
||||
for sec in m.sections {
|
||||
if sec.id != .CUSTOM || sec.name != "name" {
|
||||
continue
|
||||
}
|
||||
|
||||
r := reader(m.data, sec.offset)
|
||||
_ = rd_name(&r) or_break // re-read the section name to position at the subsections
|
||||
end := sec.offset + sec.size
|
||||
|
||||
for r.off < end {
|
||||
sub_id := rd_byte(&r) or_break
|
||||
sub_size := rd_u32(&r) or_break
|
||||
payload_end := r.off + sub_size
|
||||
if sub_id == 1 {
|
||||
count := rd_u32(&r) or_break
|
||||
for _ in 0..<count {
|
||||
fidx := rd_u32(&r) or_break
|
||||
name := rd_name(&r) or_break
|
||||
if int(fidx) < len(m.functions) {
|
||||
m.functions[fidx].name = name
|
||||
}
|
||||
}
|
||||
}
|
||||
// skip any subsection we do not need to interpret
|
||||
r.off = payload_end
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
module_destroy :: proc(m: ^Module) {
|
||||
for t in m.types {
|
||||
delete(t.params, m.allocator)
|
||||
delete(t.results, m.allocator)
|
||||
}
|
||||
for f in m.functions {
|
||||
if !f.imported {
|
||||
delete(f.locals, m.allocator)
|
||||
}
|
||||
}
|
||||
delete(m.sections, m.allocator)
|
||||
delete(m.types, m.allocator)
|
||||
delete(m.imports, m.allocator)
|
||||
delete(m.functions, m.allocator)
|
||||
delete(m.exports, m.allocator)
|
||||
m^ = {}
|
||||
}
|
||||
241
core/rexcode/wasm/module/print.odin
Normal file
241
core/rexcode/wasm/module/print.odin
Normal file
@@ -0,0 +1,241 @@
|
||||
package rexcode_wasm_module
|
||||
|
||||
import "core:strings"
|
||||
import "core:os"
|
||||
import "core:fmt"
|
||||
import wasm "../"
|
||||
|
||||
print_module :: proc(m: Module) {
|
||||
sb := strings.builder_make(context.allocator)
|
||||
defer strings.builder_destroy(&sb)
|
||||
sbprint_module(&sb, m)
|
||||
s := strings.to_string(sb)
|
||||
os.write_string(os.stdout, s)
|
||||
}
|
||||
|
||||
sbprint_module :: proc(sb: ^strings.Builder, m: Module) {
|
||||
strings.write_string(sb, "WebAssembly Module, Version: ")
|
||||
write_u64(sb, u64(m.version))
|
||||
strings.write_byte(sb, '\n')
|
||||
|
||||
label_names: map[u32]string
|
||||
defer delete(label_names)
|
||||
for f in m.functions {
|
||||
if f.name != "" {
|
||||
label_names[f.func_index] = f.name
|
||||
}
|
||||
}
|
||||
|
||||
relocs_group, _ := parse_relocations(m, m.allocator)
|
||||
defer delete(relocs_group, m.allocator)
|
||||
|
||||
// sections
|
||||
for sec in m.sections {
|
||||
strings.write_string(sb, ".")
|
||||
write_padded(sb, section_name(sec.id), 12)
|
||||
#partial switch sec.id {
|
||||
case .CUSTOM:
|
||||
strings.write_string(sb, " \"")
|
||||
strings.write_string(sb, sec.name)
|
||||
strings.write_byte(sb, '"')
|
||||
case .START:
|
||||
// do nothing
|
||||
case:
|
||||
strings.write_string(sb, " (")
|
||||
write_u64(sb, u64(sec.count))
|
||||
strings.write_string(sb, " entries)")
|
||||
}
|
||||
strings.write_byte(sb, '\n')
|
||||
|
||||
data := m.data[sec.offset:][:sec.size]
|
||||
|
||||
section_printing: #partial switch sec.id {
|
||||
case .DATA:
|
||||
r := reader(data, 0)
|
||||
count := rd_u32(&r) or_break section_printing
|
||||
assert(count == sec.count)
|
||||
for i in 0..<sec.count {
|
||||
fmt.sbprintf(sb, " [%d]\n", i)
|
||||
kind := rd_u32(&r) or_break section_printing
|
||||
switch kind {
|
||||
case 2: // memidx + expr + []byte
|
||||
memidx := rd_u32(&r) or_break section_printing
|
||||
fmt.sbprintf(sb, " memidx:%d\n", memidx)
|
||||
fallthrough
|
||||
case 0: // expr + []byte
|
||||
relocs: []wasm.Relocation
|
||||
for rg in relocs_group {
|
||||
if rg.target_section == sec.id {
|
||||
relocs = rg.relocs
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
for r.off < u32(len(r.data)) {
|
||||
inst, info, next := wasm.decode_one(r.data[r.off:], relocs=relocs, pc=0, targets_allocator=context.temp_allocator) or_break section_printing
|
||||
r.off += next
|
||||
if inst.mnemonic == .END {
|
||||
break
|
||||
}
|
||||
wasm.sbprint(sb, {inst}, {info}, nil, &label_names)
|
||||
}
|
||||
size := rd_u32(&r) or_break section_printing
|
||||
fmt.sbprintf(sb, " %q\n", r.data[r.off:][:size])
|
||||
case 1: // []byte
|
||||
fmt.sbprintf(sb, " %q\n", r.data[r.off:])
|
||||
}
|
||||
}
|
||||
case .MEMORY:
|
||||
r := reader(data, 0)
|
||||
count := rd_u32(&r) or_break section_printing
|
||||
assert(count == sec.count)
|
||||
for i in 0..<sec.count {
|
||||
fmt.sbprintf(sb, " [%d]\n", i)
|
||||
min, max := rd_limits(&r) or_break section_printing
|
||||
if max == nil {
|
||||
fmt.sbprintf(sb, " limits: %v..inf\n", min)
|
||||
} else {
|
||||
fmt.sbprintf(sb, " limits: %v..%v\n", min, max)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(m.imports) > 0 {
|
||||
strings.write_string(sb, "\n.import\n")
|
||||
for imp, i in m.imports {
|
||||
fmt.sbprintf(sb, " [%d] %s %q %q idx:%d\n", i, external_kind_string[imp.kind], imp.module_name, imp.field_name, imp.index)
|
||||
}
|
||||
}
|
||||
|
||||
if len(m.exports) > 0 {
|
||||
strings.write_string(sb, "\n.export\n")
|
||||
for e, i in m.exports {
|
||||
fmt.sbprintf(sb, " [%d] %s %q idx:%d\n", i, external_kind_string[e.kind], e.name, e.index)
|
||||
}
|
||||
}
|
||||
|
||||
if len(m.types) > 0 {
|
||||
strings.write_string(sb, "\n.")
|
||||
strings.write_string(sb, section_name(.TYPE))
|
||||
strings.write_string(sb, "\n")
|
||||
for t, i in m.types {
|
||||
strings.write_string(sb, " [")
|
||||
write_u64(sb, u64(i))
|
||||
strings.write_string(sb, "] ")
|
||||
write_func_type(sb, t)
|
||||
strings.write_byte(sb, '\n')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func_relocs: []wasm.Relocation
|
||||
for rg in relocs_group {
|
||||
if rg.target_section == .FUNCTION {
|
||||
func_relocs = rg.relocs
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
strings.write_string(sb, "\nfunctions:\n")
|
||||
for f in m.functions {
|
||||
strings.write_string(sb, " [")
|
||||
write_u64(sb, u64(f.func_index))
|
||||
strings.write_string(sb, "] ")
|
||||
if f.name != "" {
|
||||
strings.write_byte(sb, '$')
|
||||
strings.write_quoted_string(sb, f.name)
|
||||
strings.write_byte(sb, ' ')
|
||||
}
|
||||
write_func_type(sb, f.type)
|
||||
|
||||
if f.imported {
|
||||
strings.write_string(sb, " @ import ")
|
||||
strings.write_quoted_string(sb, f.import_module)
|
||||
strings.write_string(sb, " ")
|
||||
strings.write_quoted_string(sb, f.import_field)
|
||||
strings.write_string(sb, "\n")
|
||||
continue
|
||||
}
|
||||
|
||||
strings.write_byte(sb, '\n')
|
||||
write_locals(sb, f.locals)
|
||||
write_body(sb, m, f, func_relocs, &label_names)
|
||||
}
|
||||
}
|
||||
|
||||
// Disassemble and print one function body. Returns the empty string for
|
||||
// imported functions (which have no body).
|
||||
aprint_function :: proc(m: Module, f: Function, relocs: []wasm.Relocation, label_names: ^map[u32]string, allocator := context.allocator) -> string {
|
||||
if f.imported || f.body_size == 0 {
|
||||
return ""
|
||||
}
|
||||
body := m.data[f.body_offset:][:f.body_size]
|
||||
|
||||
insts: [dynamic]wasm.Instruction
|
||||
info: [dynamic]wasm.Instruction_Info
|
||||
errs: [dynamic]wasm.Error
|
||||
defer delete(insts)
|
||||
defer delete(info)
|
||||
defer delete(errs)
|
||||
|
||||
wasm.decode(body, relocs, &insts, &info, &errs)
|
||||
return wasm.aprint(insts[:], info[:], allocator=allocator, label_names=label_names)
|
||||
}
|
||||
|
||||
write_body :: proc(sb: ^strings.Builder, m: Module, f: Function, relocs: []wasm.Relocation, label_names: ^map[u32]string) {
|
||||
if f.body_size == 0 { return }
|
||||
text := aprint_function(m, f, relocs, label_names, context.temp_allocator)
|
||||
for line in strings.split_lines_iterator(&text) {
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
strings.write_string(sb, " ")
|
||||
strings.write_string(sb, line)
|
||||
strings.write_byte(sb, '\n')
|
||||
}
|
||||
}
|
||||
|
||||
write_locals :: proc(sb: ^strings.Builder, locals: []Local_Group) {
|
||||
if len(locals) == 0 { return }
|
||||
strings.write_string(sb, " locals:")
|
||||
for g in locals {
|
||||
strings.write_byte(sb, ' ')
|
||||
if g.count > 1 {
|
||||
write_u64(sb, u64(g.count))
|
||||
strings.write_string(sb, "x")
|
||||
}
|
||||
strings.write_string(sb, valtype_name(g.type))
|
||||
}
|
||||
strings.write_byte(sb, '\n')
|
||||
}
|
||||
|
||||
write_func_type :: proc(sb: ^strings.Builder, t: Func_Type) {
|
||||
strings.write_byte(sb, '(')
|
||||
for p, i in t.params {
|
||||
if i > 0 { strings.write_string(sb, ", ") }
|
||||
strings.write_string(sb, valtype_name(p))
|
||||
}
|
||||
strings.write_string(sb, ") -> ")
|
||||
strings.write_byte(sb, '(')
|
||||
for rt, i in t.results {
|
||||
if i > 0 { strings.write_string(sb, ", ") }
|
||||
strings.write_string(sb, valtype_name(rt))
|
||||
}
|
||||
strings.write_byte(sb, ')')
|
||||
}
|
||||
|
||||
write_u64 :: proc(sb: ^strings.Builder, v: u64) {
|
||||
if v == 0 { strings.write_byte(sb, '0'); return }
|
||||
buf: [20]u8
|
||||
i := 0
|
||||
n := v
|
||||
for n > 0 { buf[i] = '0' + u8(n % 10); n /= 10; i += 1 }
|
||||
for j := i - 1; j >= 0; j -= 1 { strings.write_byte(sb, buf[j]) }
|
||||
}
|
||||
|
||||
write_padded :: proc(sb: ^strings.Builder, s: string, width: int) {
|
||||
strings.write_string(sb, s)
|
||||
for _ in len(s)..<width { strings.write_byte(sb, ' ') }
|
||||
}
|
||||
93
core/rexcode/wasm/module/relocs.odin
Normal file
93
core/rexcode/wasm/module/relocs.odin
Normal file
@@ -0,0 +1,93 @@
|
||||
package rexcode_wasm_module
|
||||
|
||||
import "base:runtime"
|
||||
import "core:strings"
|
||||
import wasm "core:rexcode/wasm"
|
||||
|
||||
Reloc_Group :: struct {
|
||||
target_section: Section_Id, // index of the section these apply to
|
||||
relocs: []wasm.Relocation,
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
parse_relocations :: proc(m: Module, allocator: runtime.Allocator) -> (reloc_groups: []Reloc_Group, err: Reader_Error) {
|
||||
groups: [dynamic]Reloc_Group
|
||||
groups.allocator = m.allocator
|
||||
for sec in m.sections {
|
||||
if !(sec.id == .CUSTOM && strings.has_prefix(sec.name, "reloc.")) {
|
||||
continue
|
||||
}
|
||||
|
||||
r := reader(m.data[sec.offset:][:sec.size], 0)
|
||||
_ = rd_name(&r) or_return // step past the custom-section name
|
||||
target := Section_Id(rd_u32(&r) or_return)
|
||||
count := rd_u32(&r) or_return
|
||||
|
||||
out := make([]wasm.Relocation, int(count), m.allocator)
|
||||
w := 0
|
||||
for _ in 0..<count {
|
||||
code := rd_byte(&r) or_return
|
||||
offset := rd_u32(&r) or_return // offset of the field within target_section
|
||||
index := rd_u32(&r) or_return // symbol / target index
|
||||
addend: i32 = 0
|
||||
if reloc_has_addend(code) {
|
||||
addend = i32(rd_sleb(&r) or_return)
|
||||
}
|
||||
|
||||
t := reloc_type_from_wire(code) or_continue
|
||||
|
||||
out[w] = wasm.Relocation{
|
||||
offset = offset,
|
||||
label_id = index,
|
||||
addend = addend,
|
||||
type = t,
|
||||
size = reloc_field_size(t),
|
||||
}
|
||||
w += 1
|
||||
}
|
||||
append(&groups, Reloc_Group{target_section = target, relocs = out[:w]}) or_return
|
||||
}
|
||||
reloc_groups = groups[:]
|
||||
return
|
||||
}
|
||||
|
||||
relocations_destroy :: proc(groups: []Reloc_Group, allocator: runtime.Allocator) {
|
||||
context.allocator = allocator
|
||||
for g in groups { delete(g.relocs) }
|
||||
delete(groups)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
reloc_type_from_wire :: proc(code: u8) -> (wasm.Relocation_Type, bool) {
|
||||
switch code {
|
||||
case 0: return .FUNCTION_INDEX_LEB, true // R_WASM_FUNCTION_INDEX_LEB
|
||||
case 1: return .TABLE_INDEX_SLEB, true // R_WASM_TABLE_INDEX_SLEB
|
||||
case 2: return .TABLE_INDEX_I32, true // R_WASM_TABLE_INDEX_I32
|
||||
case 3: return .MEMORY_ADDR_LEB, true // R_WASM_MEMORY_ADDR_LEB
|
||||
case 4: return .MEMORY_ADDR_SLEB, true // R_WASM_MEMORY_ADDR_SLEB
|
||||
case 5: return .MEMORY_ADDR_I32, true // R_WASM_MEMORY_ADDR_I32
|
||||
case 6: return .TYPE_INDEX_LEB, true // R_WASM_TYPE_INDEX_LEB
|
||||
case 7: return .GLOBAL_INDEX_LEB, true // R_WASM_GLOBAL_INDEX_LEB
|
||||
case 20: return .TABLE_NUMBER_LEB, true // R_WASM_TABLE_NUMBER_LEB
|
||||
}
|
||||
return .NONE, false
|
||||
}
|
||||
|
||||
// MEMORY_ADDR_* (3,4,5) and the *_OFFSET_I32 (8,9) forms carry a trailing
|
||||
// signed-LEB addend; the index-type relocations do not.
|
||||
@(require_results)
|
||||
reloc_has_addend :: proc(code: u8) -> bool {
|
||||
switch code {
|
||||
case 3, 4, 5, 8, 9: return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
reloc_field_size :: proc(t: wasm.Relocation_Type) -> u8 {
|
||||
#partial switch t {
|
||||
case .TABLE_INDEX_I32, .MEMORY_ADDR_I32:
|
||||
return 4 // 4-byte LE field
|
||||
}
|
||||
return 5 // 5-byte padded (S)LEB field
|
||||
}
|
||||
@@ -55,8 +55,6 @@ 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,
|
||||
) {
|
||||
@@ -98,7 +96,7 @@ sbprint :: proc(
|
||||
write_decimal_u32(sb, u32(bb))
|
||||
}
|
||||
case:
|
||||
for slot in 0..<int(inst.operand_count) {
|
||||
for slot in 0..<inst.operand_count {
|
||||
strings.write_byte(sb, ' ')
|
||||
write_operand(sb, &inst.ops[slot], inst.mnemonic, label_names, opts)
|
||||
}
|
||||
@@ -106,20 +104,16 @@ sbprint :: proc(
|
||||
|
||||
strings.write_string(sb, opts.separator)
|
||||
}
|
||||
_ = tokens
|
||||
_ = label_defs
|
||||
}
|
||||
|
||||
sbprintln :: 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,
|
||||
) {
|
||||
sbprint(sb, instructions, inst_info, label_defs, tokens, options, label_names)
|
||||
sbprint(sb, instructions, inst_info, options, label_names)
|
||||
strings.write_byte(sb, '\n')
|
||||
}
|
||||
|
||||
@@ -128,118 +122,106 @@ sbprintln :: proc(
|
||||
// =============================================================================
|
||||
|
||||
print :: 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,
|
||||
instructions: []Instruction, inst_info: []Instruction_Info, 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)
|
||||
sbprint(&sb, instructions, inst_info, options, label_names)
|
||||
os.write_string(os.stdout, strings.to_string(sb))
|
||||
}
|
||||
|
||||
println :: 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,
|
||||
instructions: []Instruction, inst_info: []Instruction_Info, 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)
|
||||
sbprintln(&sb, instructions, inst_info, options, label_names)
|
||||
os.write_string(os.stdout, strings.to_string(sb))
|
||||
}
|
||||
|
||||
aprint :: 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,
|
||||
instructions: []Instruction, inst_info: []Instruction_Info, options: ^Print_Options = nil, label_names: ^map[u32]string = nil,
|
||||
allocator := context.allocator,
|
||||
) -> string {
|
||||
sb := strings.builder_make(allocator)
|
||||
sbprint(&sb, instructions, inst_info, label_defs, tokens, options, label_names)
|
||||
sbprint(&sb, instructions, inst_info, 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,
|
||||
instructions: []Instruction, inst_info: []Instruction_Info, 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)
|
||||
sbprintln(&sb, instructions, inst_info, 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,
|
||||
instructions: []Instruction, inst_info: []Instruction_Info, 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)
|
||||
sbprint(&sb, instructions, inst_info, 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,
|
||||
instructions: []Instruction, inst_info: []Instruction_Info, 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)
|
||||
sbprintln(&sb, instructions, inst_info, 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,
|
||||
instructions: []Instruction, inst_info: []Instruction_Info, 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)
|
||||
sbprint(&sb, instructions, inst_info, 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,
|
||||
instructions: []Instruction, inst_info: []Instruction_Info, 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)
|
||||
sbprintln(&sb, instructions, inst_info, 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,
|
||||
instructions: []Instruction, inst_info: []Instruction_Info, 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)
|
||||
sbprint(&sb, instructions, inst_info, 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,
|
||||
instructions: []Instruction, inst_info: []Instruction_Info, 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)
|
||||
sbprintln(&sb, instructions, inst_info, 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,
|
||||
instructions: []Instruction, inst_info: []Instruction_Info, 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)
|
||||
sbprint(&sb, instructions, inst_info, 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,
|
||||
instructions: []Instruction, inst_info: []Instruction_Info, 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)
|
||||
sbprintln(&sb, instructions, inst_info, options, label_names)
|
||||
io.write_string(w, strings.to_string(sb))
|
||||
}
|
||||
|
||||
@@ -247,7 +229,6 @@ wprintln :: proc(
|
||||
// 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 }
|
||||
@@ -265,7 +246,6 @@ write_mnemonic :: proc(sb: ^strings.Builder, m: Mnemonic, uppercase: bool) {
|
||||
}
|
||||
}
|
||||
|
||||
@(private="file")
|
||||
write_operand :: proc(
|
||||
sb: ^strings.Builder,
|
||||
op: ^Operand,
|
||||
@@ -293,19 +273,18 @@ write_operand :: proc(
|
||||
}
|
||||
|
||||
case .MEMARG:
|
||||
// WAT prints non-trivial memargs as `offset=N align=N` (omitting either
|
||||
// when it is the natural default is a refinement; we print both).
|
||||
strings.write_string(sb, "offset=")
|
||||
write_decimal_u32(sb, op.memarg.offset)
|
||||
strings.write_string(sb, " align=")
|
||||
// WAT prints non-trivial memargs as `align=N offset=N`
|
||||
// omitting either when it is the natural default is a refinement.
|
||||
strings.write_string(sb, "align=")
|
||||
write_decimal_u32(sb, op.memarg.align)
|
||||
strings.write_string(sb, " offset=")
|
||||
write_decimal_u32(sb, op.memarg.offset)
|
||||
|
||||
case .BLOCK_TYPE:
|
||||
write_block_type(sb, op.immediate)
|
||||
}
|
||||
}
|
||||
|
||||
@(private="file")
|
||||
write_block_type :: proc(sb: ^strings.Builder, v: i64) {
|
||||
switch Block_Type(v) {
|
||||
case .EMPTY: // no result annotation
|
||||
@@ -324,7 +303,6 @@ write_block_type :: proc(sb: ^strings.Builder, v: i64) {
|
||||
}
|
||||
}
|
||||
|
||||
@(private="file")
|
||||
write_heap_type :: proc(sb: ^strings.Builder, b: u8) {
|
||||
#partial switch Value_Type(b) {
|
||||
case .FUNCREF: strings.write_string(sb, "func")
|
||||
@@ -334,7 +312,6 @@ write_heap_type :: proc(sb: ^strings.Builder, b: u8) {
|
||||
}
|
||||
}
|
||||
|
||||
@(private="file")
|
||||
write_float :: proc(sb: ^strings.Builder, op: ^Operand) {
|
||||
buf: [40]u8
|
||||
if op.size == 4 {
|
||||
@@ -348,7 +325,6 @@ write_float :: proc(sb: ^strings.Builder, op: ^Operand) {
|
||||
}
|
||||
}
|
||||
|
||||
@(private="file")
|
||||
write_label :: proc(
|
||||
sb: ^strings.Builder,
|
||||
label_id: u32,
|
||||
@@ -357,15 +333,15 @@ write_label :: proc(
|
||||
) {
|
||||
if label_names != nil {
|
||||
if name, ok := label_names^[label_id]; ok {
|
||||
strings.write_string(sb, name)
|
||||
strings.write_string(sb, "$")
|
||||
strings.write_quoted_string(sb, name)
|
||||
return
|
||||
}
|
||||
}
|
||||
strings.write_string(sb, opts.label_prefix)
|
||||
strings.write_string(sb, "$")
|
||||
write_decimal_u32(sb, label_id)
|
||||
}
|
||||
|
||||
@(private="file")
|
||||
write_decimal_u32 :: proc(sb: ^strings.Builder, v: u32) {
|
||||
if v == 0 {
|
||||
strings.write_byte(sb, '0')
|
||||
@@ -383,7 +359,6 @@ write_decimal_u32 :: proc(sb: ^strings.Builder, v: u32) {
|
||||
}
|
||||
}
|
||||
|
||||
@(private="file")
|
||||
write_signed_decimal :: proc(sb: ^strings.Builder, v: i64) {
|
||||
if v < 0 {
|
||||
strings.write_byte(sb, '-')
|
||||
@@ -393,7 +368,6 @@ write_signed_decimal :: proc(sb: ^strings.Builder, v: i64) {
|
||||
}
|
||||
}
|
||||
|
||||
@(private="file")
|
||||
write_decimal_u64 :: proc(sb: ^strings.Builder, v: u64) {
|
||||
if v == 0 {
|
||||
strings.write_byte(sb, '0')
|
||||
|
||||
Reference in New Issue
Block a user