Skip to content

v.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 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 SSAGen.new #

fn SSAGen.new(m &ssa.Module) &SSAGen

new creates a WebAssembly generator for an SSA module.

fn WType.from #

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

from returns the WType value of an integer or of a field name, or an error if there is none.

enum WType #

enum WType {
	void
	i32
	i64
	f32
	f64
}

struct Code #

struct Code {
pub mut:
	bytes []u8
}

fn (Code) raw #

@[inline]
fn (mut c Code) raw(b u8)

fn (Code) i32_const #

fn (mut c Code) i32_const(v i64)

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)

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)

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()

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) ssa_configuration #

fn (mut g Gen) ssa_configuration() !SSAConfiguration

ssa_configuration collects export and initialization metadata without emitting instructions.

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
	start    int = -1
	table    []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) set_start #

fn (mut m Module) set_start(index int)

set_start selects the zero-argument function run when the module is instantiated.

fn (Module) set_table #

fn (mut m Module) set_table(functions []int)

set_table installs the function references used by indirect calls.

fn (Module) compile #

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

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

struct SSAConfiguration #

struct SSAConfiguration {
pub:
	exports        map[string]string
	init_fns       []string
	main_fn        string
	used_fns       map[string]bool
	source_modules map[string]string
	source_imports map[string]map[string]string
	imports        []WasmImport
}

SSAConfiguration carries source-level entry points and export names into SSA code generation.

struct SSAGen #

@[heap]
struct SSAGen {
mut:
	m           &ssa.Module = unsafe { nil }
	mod         &Module     = unsafe { nil }
	configured  bool
	exports     map[string]string
	imports     map[string]WasmImport
	init_fns    []string
	main_fn     string
	fn_index    map[string]int
	fn_table    map[string]int
	global_addr map[string]int
	strings     map[int]int
	// literal_text_addr holds the address a literal's bytes occupy, keyed by kind
	// and text, so a repeat of a literal that has already been laid out reuses it.
	literal_text_addr map[string]int
	data              []u8
	stack_global      int
	heap_global       int
	stack_bottom      int
	write_index       int
	exit_index        int
	alloc_index       int
	cur               Code
	locals            []u8
	value_local       map[int]int
	slots             map[int]int
	phi_snapshots     map[int]int
	frame_size        int
	frame_local       int
	pc_local          int
	sret_local        int = -1
	nparams           int
	cur_ret           ssa.TypeID
	warnings          []string
}

SSAGen lowers SSA to a WebAssembly binary with WASI output support. A dispatch loop represents arbitrary SSA control flow without requiring a reducible graph. Each invocation owns its locals and linear-memory frame.

fn (SSAGen) configure #

fn (mut g SSAGen) configure(exports map[string]string, init_fns []string, main_fn string)

configure supplies entry points and export names collected by the frontend.

fn (SSAGen) declare_imports #

fn (mut g SSAGen) declare_imports(imports []WasmImport)

declare_imports supplies the host functions the frontend resolved from @[wasm_import_namespace] declarations. gen() emits only the ones the reachable functions actually call, so an unused declaration does not force the host to provide it.

fn (SSAGen) gen #

fn (mut g SSAGen) gen() !

gen emits only functions reachable from the selected SSA entry points.

fn (SSAGen) write #

fn (g &SSAGen) write(path string) !

write writes the generated module to path.

fn (SSAGen) warnings_list #

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

warnings_list returns non-fatal export diagnostics.

struct WasmImport #

struct WasmImport {
pub:
	namespace string
	name      string // the name the host exports
	qualified string // the SSA name of the extern declaration behind it
	params    []u8
	results   []u8
}

WasmImport is a host function that V source declared with @[wasm_import_namespace] on a body-less fn in a .wasm.v file. To the checker and to SSA it is an ordinary C extern; resolving it into a real wasm module import happens here, because this is the only layer that still sees the signature spelled in V types. A pointer parameter or result is an offset into the module's linear memory, which is the only representation wasm32 has.