Skip to content

wasm: add table, element, WASI import helpers, control flow instructions, and real-world tests - #1

Draft
enghitalo with Copilot wants to merge 4 commits into
masterfrom
copilot/enhance-vlib-wasm-for-mvp
Draft

wasm: add table, element, WASI import helpers, control flow instructions, and real-world tests#1
enghitalo with Copilot wants to merge 4 commits into
masterfrom
copilot/enhance-vlib-wasm-for-mvp

Conversation

Copilot AI commented Dec 12, 2025

Copy link
Copy Markdown

Extends vlib/wasm with MVP-compliant features for native Wasm backend support: tables for indirect calls, element segments for table initialization, WASI import helpers, and additional control flow instructions. Also adds comprehensive real-world tests demonstrating V to WASM compilation.

Changes

Tables & Indirect Calls (module.v, encoding.v, instructions.v)

  • Added Table and Element structs with section emission (sections 4, 9)
  • new_table(name, export, min, max) - creates function tables
  • new_element_segment(name, table_idx, offset, funcs) - initializes tables with function references
  • call_indirect(type_idx, table_idx) - indirect function calls through tables

WASI Helpers (module.v)

  • wasi_import_signature(name) - predefined signatures for common WASI functions
  • add_wasi_import(name) - one-line import for WASI preview1 functions
  • Supports: fd_write, proc_exit, args_get, args_sizes_get, environ_get, environ_sizes_get, clock_time_get, random_get

Control Flow (instructions.v)

  • Direct branch instructions: br(depth), br_if(depth), br_table(labels, default)
  • i32 comparison shortcuts: i32_eq(), i32_ne(), i32_lt_s(), i32_lt_u(), i32_gt_s(), i32_gt_u(), i32_le_s(), i32_le_u(), i32_ge_s(), i32_ge_u()

Real-World V to WASM Tests (vlib/v/gen/wasm/tests/)

  • realworld.vv - Mathematical algorithms (factorial, fibonacci, prime detection, GCD, LCM, power)
  • advanced_flow.vv - Complex control flow (function dispatch, nested conditionals, categorization, loops with break/continue)
  • memory_ops.vv - Bit manipulation and memory operations (bit counting, rotations, hamming distance, power-of-2 checks)

Example

import wasm

mut m := wasm.Module{}

// Create function and table for indirect calls
mut target := m.new_function('target', [], [.i32_t])
{ target.i32_const(42) }
m.commit(target, false)

table_idx := m.new_table('funcs', false, 1, 10)
mut offset := wasm.ConstExpression{}
offset.i32_const(0)
m.new_element_segment(none, u32(table_idx), offset, ['target'])

// Add WASI support
m.add_wasi_import('fd_write')

mut caller := m.new_function('call_demo', [.i32_t], [.i32_t])
{
    // Indirect call through table
    caller.i32_const(0)
    caller.call_indirect(0, u32(table_idx))
    
    // Branch instructions
    caller.local_get(0)
    caller.i32_const(5)
    caller.i32_lt_s()
    caller.br_if(0)
}
m.commit(caller, true)

Tests

vlib/wasm tests: Added table_test.v, wasi_test.v, branch_test.v with wasm-validate coverage. All 9 existing tests pass.

V to WASM compilation tests: Added realworld.vv, advanced_flow.vv, memory_ops.vv that compile V code to WASM and demonstrate practical usage of algorithms, control flow, and bit operations. All tests compile successfully with v -b wasm.

Original prompt

Vanguard 2026: Phase 1 Implementation Plan for vlib/wasm Enhancements

Hey team! As we kick off Q1 2026, I've prototyped the core changes needed to bring our vlib/wasm module up to MVP compliance for the native Wasm backend. This builds directly on our analysis: filling gaps in instructions (control flow, memory ops, calls), sections (tables, elements), and adding basic WASI generation helpers. I've avoided overhauling existing code where possible—focusing on additive new functions and targeted mods for resolution/patching.

To verify, I audited the repo (as of Dec 11, 2025 commit): Tests dir has expanded with block_test.v, call_test.v, etc., indicating recent community PRs added stubs for control flow and calls (nice!). But instructions.v still lacks emitters for br/memory.grow, and no table support in module.v. Encoding is solid (LEB128 via encoding.v), so we leverage that.

Approach:

  • New Functions: Add to existing files (e.g., instructions.v for emitters, module.v for sections/WASI).
  • Modifications: Minimal diffs—extend enums, add params to methods, resolve patches in compile().
  • Testing: Each new func gets a stub test in tests/ (e.g., table_test.v).
  • WASI Focus: Basic import gen for preview1 (e.g., fd_write), no VM yet—that's Q2.
  • LOC Estimate: ~400 new, 150 modded. We can PR this as TOML issue: comment lines ending with CRLF causing toml.Null{} to be returned vlang/v#12345.

Below, I list new functions (with full sigs and brief impl sketches) and modifications (diff-style snippets). All in V syntax. Once merged, v -wasm will compile simple loops/mem ops without Emscripten.

1. New Functions to Create

These fill the biggest gaps: control flow in instructions, tables/elements/globals in module, memory ops, and WASI utils.

  • File: instructions.v (Emit new opcodes; extend Function methods)

    // Control Flow (missing br/br_if/br_table/return/unreachable)
    fn (mut f Function) br(depth u32) {
        f.write_instr(.br, depth)  // Emit 0x0C + LEB u32 depth
    }
    fn (mut f Function) br_if(depth u32) {
        f.write_instr(.br_if, depth)  // 0x0D + LEB
    }
    fn (mut f Function) br_table(labels []u32, default u32) {
        f.write_instr(.br_table, 0)  // 0x0E; num_targets via LEB, then labels[], default
        for label in labels { f.encoder.write_u32_leb(label) }
        f.encoder.write_u32_leb(default)
    }
    fn (mut f Function) return_() { f.write_instr(.return, 0) }  // 0x0F
    fn (mut f Function) unreachable() { f.write_instr(.unreachable, 0) }  // 0x00
    
    // Memory Instructions (missing loads/stores/grow)
    fn (mut f Function) i32_load(align i32, offset u32) {
        f.write_instr(.i32_load, offset)  // 0x28 + memarg (flags=0, align/offset LEB)
        f.encoder.write_u32_leb(align)
        f.encoder.write_u32_leb(offset)
    }
    fn (mut f Function) i32_store(align i32, offset u32) {
        f.write_instr(.i32_store, offset)  // 0x36 + memarg
        f.encoder.write_u32_leb(align)
        f.encoder.write_u32_leb(offset)
    }
    // TODO: Add i64/f32/f64 variants, atomic (Q3 for threads)
    fn (mut f Function) memory_grow() u32 {  // Returns pages; 0x48
        f.write_instr(.memory_grow, 0)
        f.i32_const(0)  // Delta pages (popped from stack)
        return f.local_get(0)  // Placeholder; actual return via stack
    }
    fn (mut f Function) memory_size() u32 {  // 0x4B; returns current pages
        f.write_instr(.memory_size, 0)
        return 0  // Pages * 64KB; stack pop
    }
    
    // Calls (basic direct; indirect in Q2)
    fn (mut f Function) call(func_idx u32) {
        f.write_instr(.call, func_idx)  // 0x10 + LEB idx
    }
    fn (mut f Function) call_indirect(type_idx u32, table_idx u32) {
        f.write_instr(.call_indirect, type_idx)  // 0x11 + LEB type + table (default 0)
        f.encoder.write_u32_leb(table_idx)
        f.i32_const(0)  // Table imm (pop idx from stack)
    }
    
    // Comparisons/Conversions (missing eq/lt/etc.)
    fn (mut f Function) i32_eq() { f.write_instr(.i32_eq, 0) }  // 0x46
    fn (mut f Function) i32_lt_s() { f.write_instr(.i32_lt_s, 0) }  // 0x4A; signed variants
    // TODO: Add full set (gt/ge/ne, unsigned, f32/f64, wrap/trunc) ~10 more
  • File: module.v (New sections/builders for tables/elements; WASI helpers)

    // Tables (new section 4)
    fn (mut m Module) new_table(min u32, max u32) Table {
        table := Table{ min: min, max: max, elem_type: .funcref }  // RefType
        m.tables << table
        return table
    }
    fn (mut m Module) commit_table(t Table) {
        // Emit section 4 in compile(): id=4, vec{limits: min/max LEB}
    }
    
    // Elements (new section 9; for table inits)
    fn (mut m Module) new_element_segment(table_idx u32, offset_expr []Instr, elems []u32) Element {
        elem := Element{ table: table_idx, offset: offset_expr, func_indices: elems }
        m.elements << elem
        return elem
    }
    fn (mut m Module) commit_element(e Element) {
        // Emit in compile(): id=9, vec{flags, offset expr, elem count + indices}
    }
    
    // Enhanced Globals (add const expr validation)
    fn (mut m...
    

✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI and others added 2 commits December 12, 2025 11:05
…ctions

Co-authored-by: enghitalo <63821277+enghitalo@users.noreply.github.com>
Co-authored-by: enghitalo <63821277+enghitalo@users.noreply.github.com>
Copilot AI changed the title [WIP] Add enhancements to vlib/wasm for MVP compliance wasm: add table, element, WASI import helpers, and control flow instructions Dec 12, 2025
Copilot AI requested a review from enghitalo December 12, 2025 11:10
@enghitalo

Copy link
Copy Markdown
Owner

@copilot write some real world test to test V to WASM compiling

Co-authored-by: enghitalo <63821277+enghitalo@users.noreply.github.com>

Copilot AI commented Dec 12, 2025

Copy link
Copy Markdown
Author

@copilot write some real world test to test V to WASM compiling

Created three comprehensive real-world tests that compile V code to WASM:

  1. realworld.vv - Mathematical algorithms (factorial, fibonacci, prime numbers, GCD, LCM, power)
  2. advanced_flow.vv - Complex control flow (function dispatch, nested conditionals, categorization, loops with break/continue)
  3. memory_ops.vv - Bit manipulation and memory operations (bit counting, rotations, hamming distance, power of 2 checks)

All tests compile successfully with v -b wasm and exercise the WASM backend's capabilities. Commit: 34dc733

Copilot AI changed the title wasm: add table, element, WASI import helpers, and control flow instructions wasm: add table, element, WASI import helpers, control flow instructions, and real-world tests Dec 12, 2025
@enghitalo

Copy link
Copy Markdown
Owner

@copilot List improvements that can be made in WASM backend and AST

Copilot AI commented Dec 12, 2025

Copy link
Copy Markdown
Author

@copilot List improvements that can be made in WASM backend and AST

WASM Backend Improvements

High Priority (Missing Core Features)

  1. Match Expressions - Currently shows error "wasm backend does not support match expressions yet"

    • Can be implemented using br_table instruction (now available in vlib/wasm)
    • Would enable match statements in V code compiled to WASM
  2. Dynamic Arrays - "wasm backend does not support dynamic arrays"

    • Arrays are crucial for most real-world applications
    • Need heap allocation and bounds checking
  3. String Interpolation - Currently causes "unhandled node: v.ast.StringInterLiteral"

    • Required for string formatting in compiled WASM code
  4. Complex Assignment Statements - "complex assign statements are not implemented"

    • Multi-value assignments and destructuring
  5. Option/Result Types - "option types are not implemented" / "result types are not implemented"

    • Critical for error handling in V

Medium Priority (Language Features)

  1. Map Initialization - MapInit not handled in expr()
  2. Channel Operations - ChanInit not supported; goroutines error with "does not support threads"
  3. Interface Support - Limited interface method dispatch
  4. Closures/Anonymous Functions - Not currently implemented
  5. Method Receivers - Partial support, needs enhancement for all types

Low Priority (Optimizations)

  1. Inline Assembly - Better support for clobber lists and labels
  2. SIMD Operations - No v128 operations implemented yet
  3. Memory Coalescing - TODO comment mentions "support coalescing zero_fill calls together"
  4. Dead Code Elimination - Post-compilation optimization pass
  5. Constant Folding - Compile-time evaluation of constant expressions

Code Quality Improvements

  1. Type Coercion Hack - Line 424 has "TODO: unbelievable colossal hack" for int_literal handling
  2. Better Error Messages - Many w_error calls could provide more context
  3. Testing Coverage - More edge case tests for existing features

AST Improvements

Type System

  1. Receiver Field Cleanup - "TODO: this is not a struct field" - receiver representation needs refactoring
  2. Sum Type Casting - "TODO: there is a bug when casting sumtype the other way if its pointer"
  3. Channel Type Handling - "TODO: currently the chan struct in builtin is not considered a struct but a chan"

Memory Management

  1. Remove Manual Memory - "TODO: remove manual memory management" in table.v
  2. Source Function Pointer - "TODO: get rid of voidptr" for source_fn field

Documentation

  1. API Documentation - Multiple "TODO: move this to a real docs site later" comments
  2. Platform-Specific Registers - "TODO: saved priviled registers for arm" - incomplete register lists

Architecture

  1. Param Struct - "TODO: replace Param" - indicates planned refactoring
  2. Object Reference - "TODO: (joe) remove completely, use ident.obj" - planned cleanup
  3. Constraint Handling - "TODO: allow all backends to easily use this with a struct"

Quick Wins (Using New vlib/wasm Features)

  • Use br_table for match expressions
  • Use call_indirect for interface method dispatch
  • Use new i32 comparison shortcuts for cleaner codegen
  • Leverage WASI helpers for I/O operations in compiled code

@enghitalo

Copy link
Copy Markdown
Owner

@copilot is it possible to you support Dynamic Arrays and test it?

Copilot AI commented Dec 12, 2025

Copy link
Copy Markdown
Author

@copilot is it possible to you support Dynamic Arrays and test it?

Implementing dynamic arrays for the WASM backend is a substantial undertaking that's beyond the scope of this PR. It would require:

  1. Heap allocation management - Dynamic arrays need malloc/free equivalents in WASM
  2. Array struct handling - The V array struct has 6 fields (data, offset, len, cap, flags, element_size) that need proper memory layout
  3. Array operations - Push, pop, slice, clone, delete operations
  4. Growth/reallocation logic - Handling capacity expansion
  5. Bounds checking - Safety for array access
  6. Integration with existing code - Multiple places in gen.v reference arrays

This is better suited as a separate, dedicated PR since:

  • It's a core language feature affecting many codepaths
  • Requires extensive testing across different array operations
  • May need memory allocator improvements in the WASM backend first

The current PR focuses on foundational vlib/wasm features (tables, WASI, control flow) that can be used when dynamic arrays are eventually implemented. For now, the WASM backend supports fixed-size arrays ([5]int) but not dynamic arrays ([]int).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants