Skip to content

v3.gen.wasm #

code.v emits the WASM instruction stream for a single function body. It is a thin layer over a []u8 buffer with one method per opcode used by the backend, analogous to the arm64 backend's asm.v instruction encoders.

Constants #

const valtype_i32 = u8(0x7f)

WASM value types.

const valtype_i64 = u8(0x7e)
const valtype_f32 = u8(0x7d)
const valtype_f64 = u8(0x7c)
const export_func = u8(0x00)

Export kinds.

const export_mem = u8(0x02)

fn FrameTag.from #

fn FrameTag.from[W](input W) !FrameTag

fn Gen.new #

fn Gen.new(a &flat.FlatAst, tc &types.TypeChecker, used_fns map[string]bool) &Gen

fn Module.new #

fn Module.new() &Module

fn WType.from #

fn WType.from[W](input W) !WType

enum WType #

enum WType {
	void
	i32
	i64
	f32
	f64
}

struct Code #

struct Code {
pub mut:
	bytes []u8
}

fn (Code) raw #

fn (mut c Code) raw(b u8)

fn (Code) i32_const #

fn (mut c Code) i32_const(v i64)

---- constants ----

fn (Code) i64_const #

fn (mut c Code) i64_const(v i64)

fn (Code) f32_const #

fn (mut c Code) f32_const(v f32)

fn (Code) f64_const #

fn (mut c Code) f64_const(v f64)

fn (Code) local_get #

fn (mut c Code) local_get(idx int)

---- locals / globals ----

fn (Code) local_set #

fn (mut c Code) local_set(idx int)

fn (Code) local_tee #

fn (mut c Code) local_tee(idx int)

fn (Code) global_get #

fn (mut c Code) global_get(idx int)

fn (Code) global_set #

fn (mut c Code) global_set(idx int)

fn (Code) load #

fn (mut c Code) load(op u8, align int, offset int)

---- memory ----

fn (Code) store #

fn (mut c Code) store(op u8, align int, offset int)

fn (Code) i32_store #

fn (mut c Code) i32_store(offset int)

i32_store / i32_load with natural alignment for a full i32.

fn (Code) i32_store8 #

fn (mut c Code) i32_store8(offset int)

fn (Code) i32_load #

fn (mut c Code) i32_load(offset int)

fn (Code) block_void #

fn (mut c Code) block_void()

---- control flow ----

fn (Code) loop_void #

fn (mut c Code) loop_void()

fn (Code) if_void #

fn (mut c Code) if_void()

fn (Code) else_ #

fn (mut c Code) else_()

fn (Code) end #

fn (mut c Code) end()

fn (Code) br #

fn (mut c Code) br(depth int)

fn (Code) br_if #

fn (mut c Code) br_if(depth int)

fn (Code) ret #

fn (mut c Code) ret()

fn (Code) call #

fn (mut c Code) call(idx int)

fn (Code) drop #

fn (mut c Code) drop()

struct Gen #

@[heap]
struct Gen {
mut:
	a        &flat.FlatAst      = unsafe { nil }
	tc       &types.TypeChecker = unsafe { nil }
	used_fns map[string]bool
	mod      &Module = unsafe { nil }
	// per-function state
	cur           Code
	local_types   []u8
	nparams       int
	var_index     map[string]int
	var_wtype     map[string]WType
	var_unsigned  map[string]bool
	var_widths    map[string]int // sub-32-bit int locals: 8 or 16; else 32
	frames        []Frame
	pending_label string // label of a preceding `label:` marker, for the next loop
	cur_ret       WType
	cur_fn_module string
	cur_fn_file   string
	// module-wide; keyed by qualified function name (see qualified_fn_key)
	fn_index        map[string]int
	fn_ret          map[string]WType
	fn_params       map[string][]WType
	file_aliases    map[string]map[string]string // file -> (import alias -> full import path)
	import_paths    []string                     // every `import a.b.c` path (full)
	module_imports  map[string][]string          // module path -> imported module paths ('' = main)
	global_index    map[string]int               // __global name -> wasm global index
	global_wtype    map[string]WType
	global_unsigned map[string]bool
	global_widths   map[string]int
	data_pool       []u8
	data_off        map[string]int
	uses_print      bool
	write_idx       int
	print_int_idx   int
	has_main        bool
	init_fns        []int // function indices of init() entry points, in call order
	warnings        []string
}

fn (Gen) gen #

fn (mut g Gen) gen()

gen builds the whole module from the flat AST.

fn (Gen) warnings_list #

fn (g &Gen) warnings_list() []string

fn (Gen) write #

fn (mut g Gen) write(path string) !

write serializes the module to path.

struct Module #

@[heap]
struct Module {
mut:
	types    []FuncType
	imports  []ImportFunc
	funcs    []Func
	exports  []Export
	datas    []DataSeg
	globals  []Global
	mem_min  int = 2
	n_import int
}

fn (Module) add_type #

fn (mut m Module) add_type(params []u8, results []u8) int

add_type registers a signature, deduplicating identical ones.

fn (Module) add_import_func #

fn (mut m Module) add_import_func(module_ string, name string, type_idx int) int

add_import_func registers an imported function and returns its function index. Imports must be added before any defined function so indices stay stable.

fn (Module) add_global #

fn (mut m Module) add_global(valtype u8, init []u8) int

add_global appends a mutable global and returns its index.

fn (Module) reserve_func_index #

fn (m &Module) reserve_func_index(defined_so_far int) int

reserve_func_index returns the function index a later add_func will occupy.

fn (Module) add_func #

fn (mut m Module) add_func(type_idx int, locals []u8, code []u8) int

add_func appends a defined function and returns its global function index.

fn (Module) add_export #

fn (mut m Module) add_export(name string, kind u8, index int)

fn (Module) add_data #

fn (mut m Module) add_data(offset int, bytes []u8)

fn (Module) set_mem_min #

fn (mut m Module) set_mem_min(pages int)

fn (Module) compile #

fn (m &Module) compile() []u8

compile serializes the whole module into a .wasm byte buffer.