From bad21ab8a6f340dd0309ac5fc6bc431924c116d7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 12 Dec 2025 10:56:24 +0000 Subject: [PATCH 1/4] Initial plan From cbe4a8a3cdc1e8e46ffc55bb5c8cb975ebe684c3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 12 Dec 2025 11:05:25 +0000 Subject: [PATCH 2/4] Add wasm enhancements: tables, elements, WASI helpers, and new instructions Co-authored-by: enghitalo <63821277+enghitalo@users.noreply.github.com> --- vlib/wasm/encoding.v | 64 +++++++++++++++++++ vlib/wasm/instructions.v | 93 +++++++++++++++++++++++++++ vlib/wasm/module.v | 115 ++++++++++++++++++++++++++++++++++ vlib/wasm/tests/branch_test.v | 104 ++++++++++++++++++++++++++++++ vlib/wasm/tests/table_test.v | 61 ++++++++++++++++++ vlib/wasm/tests/wasi_test.v | 68 ++++++++++++++++++++ 6 files changed, 505 insertions(+) create mode 100644 vlib/wasm/tests/branch_test.v create mode 100644 vlib/wasm/tests/table_test.v create mode 100644 vlib/wasm/tests/wasi_test.v diff --git a/vlib/wasm/encoding.v b/vlib/wasm/encoding.v index af8d909156ac14..53456eafb5c110 100644 --- a/vlib/wasm/encoding.v +++ b/vlib/wasm/encoding.v @@ -182,6 +182,26 @@ pub fn (mut mod Module) compile() []u8 { } mod.end_section(tpatch) } + // https://webassembly.github.io/spec/core/binary/modules.html#table-section + // + if mod.tables.len > 0 { + tpatch := mod.start_section(.table_section) + { + mod.u32(u32(mod.tables.len)) + for tbl in mod.tables { + mod.buf << u8(tbl.elem_type) // element type (funcref or externref) + if max := tbl.max { + mod.buf << 0x01 // limit, max present + mod.u32(tbl.min) + mod.u32(max) + } else { + mod.buf << 0x00 // limit, max not present + mod.u32(tbl.min) + } + } + } + mod.end_section(tpatch) + } // https://webassembly.github.io/spec/core/binary/modules.html#binary-memsec // if memory := mod.memory { @@ -257,6 +277,15 @@ pub fn (mut mod Module) compile() []u8 { mod.buf << 0x03 // global mod.u32(u32(idx + mod.global_imports.len)) } + for idx, tbl in mod.tables { + if !tbl.export { + continue + } + lsz++ + mod.name(tbl.name) + mod.buf << 0x01 // table + mod.u32(u32(idx)) + } mod.patch_u32(lpatch, u32(lsz)) } mod.end_section(tpatch) @@ -271,6 +300,41 @@ pub fn (mut mod Module) compile() []u8 { } mod.end_section(tpatch) } + // https://webassembly.github.io/spec/core/binary/modules.html#element-section + // + if mod.elements.len > 0 { + tpatch := mod.start_section(.element_section) + { + mod.u32(u32(mod.elements.len)) + for elem in mod.elements { + // Element kind: active with table index (0x00) + mod.buf << 0x00 + // Table index + mod.u32(elem.table_idx) + // Offset expression + { + mut ptr := 0 + for patch in elem.offset.call_patches { + idx := mod.get_function_idx(patch) + mod.buf << elem.offset.code[ptr..patch.pos] + mod.u32(u32(idx)) + ptr = patch.pos + } + mod.buf << elem.offset.code[ptr..] + } + mod.buf << 0x0B // END expression opcode + // Element count and function indices + mod.u32(u32(elem.func_names.len)) + for fname in elem.func_names { + ftt := mod.functions[fname] or { + panic('element function ${fname} does not exist') + } + mod.u32(u32(ftt.idx + mod.fn_imports.len)) + } + } + } + mod.end_section(tpatch) + } // https://webassembly.github.io/spec/core/binary/modules.html#data-count-section // if mod.segments.len > 0 { diff --git a/vlib/wasm/instructions.v b/vlib/wasm/instructions.v index 91edb2438e31ea..449a2fea451e0b 100644 --- a/vlib/wasm/instructions.v +++ b/vlib/wasm/instructions.v @@ -1007,6 +1007,99 @@ pub fn (mut func Function) call_import(mod string, name string) { }) } +// call_indirect calls a function indirectly through a table. +// WebAssembly instruction: `call_indirect`. +pub fn (mut func Function) call_indirect(type_idx u32, table_idx u32) { + func.code << 0x11 // call_indirect + func.u32(type_idx) + func.u32(table_idx) +} + +// br branches to a loop or block at the given depth. +// WebAssembly instruction: `br`. +pub fn (mut func Function) br(depth u32) { + func.code << 0x0C // br + func.u32(depth) +} + +// br_if conditionally branches to a loop or block at the given depth. +// WebAssembly instruction: `br_if`. +pub fn (mut func Function) br_if(depth u32) { + func.code << 0x0D // br_if + func.u32(depth) +} + +// br_table performs an indirect branch through a label table. +// WebAssembly instruction: `br_table`. +pub fn (mut func Function) br_table(labels []u32, default_label u32) { + func.code << 0x0E // br_table + func.u32(u32(labels.len)) + for label in labels { + func.u32(label) + } + func.u32(default_label) +} + +// i32_eq checks if two i32 values are equal. +// WebAssembly instruction: `i32.eq`. +pub fn (mut func Function) i32_eq() { + func.code << 0x46 // i32.eq +} + +// i32_ne checks if two i32 values are not equal. +// WebAssembly instruction: `i32.ne`. +pub fn (mut func Function) i32_ne() { + func.code << 0x47 // i32.ne +} + +// i32_lt_s checks if first i32 value is less than second (signed). +// WebAssembly instruction: `i32.lt_s`. +pub fn (mut func Function) i32_lt_s() { + func.code << 0x48 // i32.lt_s +} + +// i32_lt_u checks if first i32 value is less than second (unsigned). +// WebAssembly instruction: `i32.lt_u`. +pub fn (mut func Function) i32_lt_u() { + func.code << 0x49 // i32.lt_u +} + +// i32_gt_s checks if first i32 value is greater than second (signed). +// WebAssembly instruction: `i32.gt_s`. +pub fn (mut func Function) i32_gt_s() { + func.code << 0x4A // i32.gt_s +} + +// i32_gt_u checks if first i32 value is greater than second (unsigned). +// WebAssembly instruction: `i32.gt_u`. +pub fn (mut func Function) i32_gt_u() { + func.code << 0x4B // i32.gt_u +} + +// i32_le_s checks if first i32 value is less than or equal to second (signed). +// WebAssembly instruction: `i32.le_s`. +pub fn (mut func Function) i32_le_s() { + func.code << 0x4C // i32.le_s +} + +// i32_le_u checks if first i32 value is less than or equal to second (unsigned). +// WebAssembly instruction: `i32.le_u`. +pub fn (mut func Function) i32_le_u() { + func.code << 0x4D // i32.le_u +} + +// i32_ge_s checks if first i32 value is greater than or equal to second (signed). +// WebAssembly instruction: `i32.ge_s`. +pub fn (mut func Function) i32_ge_s() { + func.code << 0x4E // i32.ge_s +} + +// i32_ge_u checks if first i32 value is greater than or equal to second (unsigned). +// WebAssembly instruction: `i32.ge_u`. +pub fn (mut func Function) i32_ge_u() { + func.code << 0x4F // i32.ge_u +} + // load loads a value with type `typ` from memory. // WebAssembly instruction: `i32|i64|f32|f64.load`. pub fn (mut func Function) load(typ NumType, align int, offset int) { diff --git a/vlib/wasm/module.v b/vlib/wasm/module.v index 4829510a8d3176..1c7f2b8a9e9700 100644 --- a/vlib/wasm/module.v +++ b/vlib/wasm/module.v @@ -69,6 +69,8 @@ mut: fn_imports []FunctionImport global_imports []GlobalImport segments []DataSegment + tables []Table + elements []Element debug bool mod_name ?string } @@ -108,6 +110,21 @@ struct DataSegment { name ?string } +struct Table { + name string + export bool + min u32 + max ?u32 + elem_type RefType = .funcref_t +} + +struct Element { + table_idx u32 + offset ConstExpression + func_names []string + name ?string +} + pub type LocalIndex = int pub type GlobalIndex = int pub type GlobalImportIndex = int @@ -278,3 +295,101 @@ pub fn (mut mod Module) new_global_import(modn string, name string, typ ValType, pub fn (mut mod Module) assign_global_init(global GlobalIndex, init ConstExpression) { mod.globals[global].init = init } + +// new_table creates a new table and returns its index. +pub fn (mut mod Module) new_table(name string, export bool, min u32, max ?u32) int { + len := mod.tables.len + mod.tables << Table{ + name: name + export: export + min: min + max: max + elem_type: .funcref_t + } + return len +} + +// new_element_segment creates a new element segment for initializing tables. +pub fn (mut mod Module) new_element_segment(name ?string, table_idx u32, offset ConstExpression, func_names []string) int { + len := mod.elements.len + mod.elements << Element{ + table_idx: table_idx + offset: offset + func_names: func_names + name: name + } + return len +} + +// wasi_import_signature returns the function signature for a WASI preview1 function. +pub fn wasi_import_signature(name string) ?FuncType { + return match name { + 'fd_write' { + FuncType{ + parameters: [.i32_t, .i32_t, .i32_t, .i32_t] + results: [.i32_t] + name: none + } + } + 'proc_exit' { + FuncType{ + parameters: [.i32_t] + results: [] + name: none + } + } + 'args_get' { + FuncType{ + parameters: [.i32_t, .i32_t] + results: [.i32_t] + name: none + } + } + 'args_sizes_get' { + FuncType{ + parameters: [.i32_t, .i32_t] + results: [.i32_t] + name: none + } + } + 'environ_get' { + FuncType{ + parameters: [.i32_t, .i32_t] + results: [.i32_t] + name: none + } + } + 'environ_sizes_get' { + FuncType{ + parameters: [.i32_t, .i32_t] + results: [.i32_t] + name: none + } + } + 'clock_time_get' { + FuncType{ + parameters: [.i32_t, .i64_t, .i32_t] + results: [.i32_t] + name: none + } + } + 'random_get' { + FuncType{ + parameters: [.i32_t, .i32_t] + results: [.i32_t] + name: none + } + } + else { + none + } + } +} + +// add_wasi_import adds a WASI preview1 import to the module. +pub fn (mut mod Module) add_wasi_import(name string) int { + sig := wasi_import_signature(name) or { panic('Unknown WASI function: ${name}') } + mod.new_function_import('wasi_snapshot_preview1', name, sig.parameters, sig.results) + // Return the index of the imported function + return mod.fn_imports.len - 1 +} diff --git a/vlib/wasm/tests/branch_test.v b/vlib/wasm/tests/branch_test.v new file mode 100644 index 00000000000000..9798295ce33f13 --- /dev/null +++ b/vlib/wasm/tests/branch_test.v @@ -0,0 +1,104 @@ +module main + +import wasm + +fn test_br_instructions() { + mut m := wasm.Module{} + + // Test direct br instruction + mut func1 := m.new_function('test_br', [], [.i32_t]) + { + func1.i32_const(1) + blk := func1.c_block([], [.i32_t]) + { + func1.i32_const(2) + func1.br(0) // branch to block + func1.i32_const(3) // unreachable + } + func1.c_end(blk) + } + m.commit(func1, true) + + // Test br_if instruction + mut func2 := m.new_function('test_br_if', [.i32_t], [.i32_t]) + { + func2.i32_const(10) + blk := func2.c_block([], [.i32_t]) + { + func2.i32_const(20) + func2.local_get(0) + func2.br_if(0) // conditional branch + func2.drop() + func2.i32_const(30) + } + func2.c_end(blk) + } + m.commit(func2, true) + + // Test br_table instruction + mut func3 := m.new_function('test_br_table', [.i32_t], [.i32_t]) + { + blk0 := func3.c_block([], [.i32_t]) + { + blk1 := func3.c_block([], []) + { + blk2 := func3.c_block([], []) + { + func3.local_get(0) + func3.br_table([u32(0), u32(1)], u32(2)) + } + func3.c_end(blk2) + func3.i32_const(2) + func3.c_br(blk0) + } + func3.c_end(blk1) + func3.i32_const(1) + func3.c_br(blk0) + } + func3.c_end(blk0) + func3.i32_const(0) + } + m.commit(func3, true) + + code := m.compile() + assert code.len > 0 + + validate(code) or { panic(err) } +} + +fn test_i32_comparison_shortcuts() { + mut m := wasm.Module{} + + // Test various comparison shortcuts + mut func := m.new_function('test_comparisons', [.i32_t, .i32_t], [.i32_t]) + { + // Test i32_eq + func.local_get(0) + func.local_get(1) + func.i32_eq() + + // Test i32_ne + func.local_get(0) + func.local_get(1) + func.i32_ne() + func.b_and(.i32_t) + + // Test i32_lt_s + func.local_get(0) + func.local_get(1) + func.i32_lt_s() + func.b_or(.i32_t) + + // Test i32_gt_s + func.local_get(0) + func.local_get(1) + func.i32_gt_s() + func.b_or(.i32_t) + } + m.commit(func, true) + + code := m.compile() + assert code.len > 0 + + validate(code) or { panic(err) } +} diff --git a/vlib/wasm/tests/table_test.v b/vlib/wasm/tests/table_test.v new file mode 100644 index 00000000000000..bbb8fde0ca0335 --- /dev/null +++ b/vlib/wasm/tests/table_test.v @@ -0,0 +1,61 @@ +module main + +import wasm + +fn test_table_section() { + mut m := wasm.Module{} + + // Create a simple function to reference in the table + mut func1 := m.new_function('table_func', [], [.i32_t]) + { + func1.i32_const(42) + } + m.commit(func1, false) + + // Create a table + table_idx := m.new_table('my_table', true, 1, 10) + + // Create an element segment to initialize the table + mut offset_expr := wasm.ConstExpression{} + offset_expr.i32_const(0) + m.new_element_segment(none, u32(table_idx), offset_expr, ['table_func']) + + code := m.compile() + assert code.len > 0 + + validate(code) or { panic(err) } +} + +fn test_call_indirect() { + mut m := wasm.Module{} + + // Create a function to call indirectly + mut target := m.new_function('target', [], [.i32_t]) + { + target.i32_const(99) + } + m.commit(target, false) + + // Create a table + table_idx := m.new_table('func_table', false, 1, 10) + + // Initialize table with the target function + mut offset_expr := wasm.ConstExpression{} + offset_expr.i32_const(0) + m.new_element_segment(none, u32(table_idx), offset_expr, ['target']) + + // Create a function that does an indirect call + // Use the same type as 'target': [] -> [.i32_t] + mut caller := m.new_function('caller', [], [.i32_t]) + { + caller.i32_const(0) // table index + // The type index should be 0 (first function type registered) + caller.call_indirect(0, u32(table_idx)) + } + m.commit(caller, true) + + code := m.compile() + assert code.len > 0 + + validate(code) or { panic(err) } +} diff --git a/vlib/wasm/tests/wasi_test.v b/vlib/wasm/tests/wasi_test.v new file mode 100644 index 00000000000000..0fd602254463f9 --- /dev/null +++ b/vlib/wasm/tests/wasi_test.v @@ -0,0 +1,68 @@ +module main + +import wasm + +fn test_wasi_fd_write() { + mut m := wasm.Module{} + + // Add WASI fd_write import + fd_write_idx := m.add_wasi_import('fd_write') + + // Create a function that uses fd_write + mut func := m.new_function('write_test', [], [.i32_t]) + { + func.i32_const(1) // fd (stdout) + func.i32_const(0) // iovs pointer + func.i32_const(1) // iovs_len + func.i32_const(0) // nwritten pointer + func.call_import('wasi_snapshot_preview1', 'fd_write') + } + m.commit(func, true) + + code := m.compile() + assert code.len > 0 + + validate(code) or { panic(err) } +} + +fn test_wasi_proc_exit() { + mut m := wasm.Module{} + + // Add WASI proc_exit import + m.add_wasi_import('proc_exit') + + // Create a function that exits + mut func := m.new_function('exit_test', [], []) + { + func.i32_const(0) // exit code + func.call_import('wasi_snapshot_preview1', 'proc_exit') + } + m.commit(func, true) + + code := m.compile() + assert code.len > 0 + + validate(code) or { panic(err) } +} + +fn test_wasi_multiple_imports() { + mut m := wasm.Module{} + + // Add multiple WASI imports + m.add_wasi_import('fd_write') + m.add_wasi_import('proc_exit') + m.add_wasi_import('args_get') + m.add_wasi_import('environ_get') + + // Create a simple function + mut func := m.new_function('test', [], []) + { + func.nop() + } + m.commit(func, true) + + code := m.compile() + assert code.len > 0 + + validate(code) or { panic(err) } +} From 3a3afc0c74628c907e2d349151ab7684f7017526 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 12 Dec 2025 11:07:18 +0000 Subject: [PATCH 3/4] Add documentation for new wasm features Co-authored-by: enghitalo <63821277+enghitalo@users.noreply.github.com> --- vlib/wasm/README.md | 91 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/vlib/wasm/README.md b/vlib/wasm/README.md index 66e9af42a4cca9..575627ddd4a4ee 100644 --- a/vlib/wasm/README.md +++ b/vlib/wasm/README.md @@ -37,3 +37,94 @@ fn main() { This module does not perform verification of the WebAssembly output. Use a tool like `wasm-validate` to validate, and `wasm-dis` to show a decompiled form. + +## New Features (Phase 1 - 2026) + +### Tables and Indirect Calls + +Tables allow dynamic function dispatch through indirect calls: + +```v +mut m := wasm.Module{} + +// Create a function to call indirectly +mut target := m.new_function('target', [], [.i32_t]) +{ + target.i32_const(42) +} +m.commit(target, false) + +// Create a table +table_idx := m.new_table('func_table', false, 1, 10) + +// Initialize table with the target function +mut offset := wasm.ConstExpression{} +offset.i32_const(0) +m.new_element_segment(none, u32(table_idx), offset, ['target']) + +// Call indirectly through the table +mut caller := m.new_function('caller', [], [.i32_t]) +{ + caller.i32_const(0) // table index + caller.call_indirect(0, u32(table_idx)) +} +m.commit(caller, true) +``` + +### WASI Support + +Easily import WASI functions for system interactions: + +```v +mut m := wasm.Module{} + +// Add WASI imports +m.add_wasi_import('fd_write') +m.add_wasi_import('proc_exit') + +mut func := m.new_function('hello', [], []) +{ + // Use fd_write to print to stdout + func.i32_const(1) // fd (stdout) + func.i32_const(0) // iovs pointer + func.i32_const(1) // iovs_len + func.i32_const(0) // nwritten pointer + func.call_import('wasi_snapshot_preview1', 'fd_write') +} +m.commit(func, true) +``` + +Supported WASI functions: `fd_write`, `proc_exit`, `args_get`, `args_sizes_get`, `environ_get`, `environ_sizes_get`, `clock_time_get`, `random_get`. + +### Enhanced Control Flow + +New branch instructions for more flexible control flow: + +```v +mut func := m.new_function('branch_demo', [.i32_t], [.i32_t]) +{ + // Direct branch + func.br(0) + + // Conditional branch + func.local_get(0) + func.br_if(0) + + // Branch table (switch-case) + func.local_get(0) + func.br_table([u32(0), u32(1)], u32(2)) +} +``` + +### i32 Comparison Shortcuts + +Convenient shortcuts for common i32 comparisons: + +```v +func.local_get(0) +func.local_get(1) +func.i32_eq() // i32.eq +func.i32_lt_s() // i32.lt_s (signed) +func.i32_gt_u() // i32.gt_u (unsigned) +// Also available: i32_ne, i32_le_s, i32_le_u, i32_ge_s, i32_ge_u +``` From 34dc7330558c19eefdac500786879c4805b64d30 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 12 Dec 2025 11:21:44 +0000 Subject: [PATCH 4/4] Add real-world WASM compilation tests Co-authored-by: enghitalo <63821277+enghitalo@users.noreply.github.com> --- vlib/v/gen/wasm/tests/advanced_flow.vv | 152 +++++++++++++++++++++ vlib/v/gen/wasm/tests/advanced_flow.vv.out | 32 +++++ vlib/v/gen/wasm/tests/advanced_flow.wasm | Bin 0 -> 4060 bytes vlib/v/gen/wasm/tests/memory_ops.vv | 136 ++++++++++++++++++ vlib/v/gen/wasm/tests/memory_ops.vv.out | 29 ++++ vlib/v/gen/wasm/tests/memory_ops.wasm | Bin 0 -> 4046 bytes vlib/v/gen/wasm/tests/realworld.vv | 126 +++++++++++++++++ vlib/v/gen/wasm/tests/realworld.vv.out | 21 +++ vlib/v/gen/wasm/tests/realworld.wasm | Bin 0 -> 3771 bytes 9 files changed, 496 insertions(+) create mode 100644 vlib/v/gen/wasm/tests/advanced_flow.vv create mode 100644 vlib/v/gen/wasm/tests/advanced_flow.vv.out create mode 100644 vlib/v/gen/wasm/tests/advanced_flow.wasm create mode 100644 vlib/v/gen/wasm/tests/memory_ops.vv create mode 100644 vlib/v/gen/wasm/tests/memory_ops.vv.out create mode 100644 vlib/v/gen/wasm/tests/memory_ops.wasm create mode 100644 vlib/v/gen/wasm/tests/realworld.vv create mode 100644 vlib/v/gen/wasm/tests/realworld.vv.out create mode 100644 vlib/v/gen/wasm/tests/realworld.wasm diff --git a/vlib/v/gen/wasm/tests/advanced_flow.vv b/vlib/v/gen/wasm/tests/advanced_flow.vv new file mode 100644 index 00000000000000..83de50f9028af3 --- /dev/null +++ b/vlib/v/gen/wasm/tests/advanced_flow.vv @@ -0,0 +1,152 @@ +// Test advanced control flow and function pointers simulation +// This exercises complex conditionals and branching + +fn operation_add(a i64, b i64) i64 { + return a + b +} + +fn operation_sub(a i64, b i64) i64 { + return a - b +} + +fn operation_mul(a i64, b i64) i64 { + return a * b +} + +fn operation_div(a i64, b i64) i64 { + if b == 0 { + return 0 + } + return a / b +} + +// Simulate function dispatch using if-else chain +fn calculate(op int, a i64, b i64) i64 { + if op == 0 { + return operation_add(a, b) + } else if op == 1 { + return operation_sub(a, b) + } else if op == 2 { + return operation_mul(a, b) + } else if op == 3 { + return operation_div(a, b) + } else { + return 0 + } +} + +// Complex nested control flow +fn categorize_number(n i64) int { + if n < 0 { + if n < -100 { + return -3 // very negative + } else if n < -10 { + return -2 // negative + } else { + return -1 // slightly negative + } + } else if n == 0 { + return 0 // zero + } else { + if n > 100 { + return 3 // very positive + } else if n > 10 { + return 2 // positive + } else { + return 1 // slightly positive + } + } +} + +// Test various comparison operations +fn compare_values(a i64, b i64) int { + if a == b { + return 0 // equal + } else if a < b { + return -1 // less + } else if a > b { + return 1 // greater + } else { + return 99 // unknown + } +} + +// Nested loops with breaks and continues +fn sum_with_skip(limit int, skip int) i64 { + mut sum := i64(0) + for i := 0; i < limit; i++ { + if i == skip { + continue + } + if i > limit / 2 && i % 2 == 0 { + break + } + sum += i + } + return sum +} + +// Multiple return paths +fn classify_range(val i64) int { + if val < 0 { + return -1 + } + if val == 0 { + return 0 + } + if val < 10 { + return 1 + } + if val < 100 { + return 2 + } + return 3 +} + +fn abs_diff(a i64, b i64) i64 { + if a > b { + return a - b + } + return b - a +} + +fn main() { + println('=== Advanced Control Flow Tests ===') + + println('--- Calculator Dispatch') + println(calculate(0, 10, 5)) + println(calculate(1, 10, 5)) + println(calculate(2, 10, 5)) + println(calculate(3, 10, 5)) + println(calculate(99, 10, 5)) + + println('--- Number Categorization') + println(categorize_number(-150)) + println(categorize_number(-50)) + println(categorize_number(-5)) + println(categorize_number(0)) + println(categorize_number(5)) + println(categorize_number(50)) + println(categorize_number(150)) + + println('--- Value Comparison') + println(compare_values(10, 10)) + println(compare_values(5, 10)) + println(compare_values(15, 10)) + + println('--- Loop with Skip') + println(sum_with_skip(10, 3)) + println(sum_with_skip(20, 5)) + + println('--- Range Classification') + println(classify_range(-5)) + println(classify_range(0)) + println(classify_range(5)) + println(classify_range(50)) + println(classify_range(500)) + + println('--- Absolute Difference') + println(abs_diff(10, 20)) + println(abs_diff(20, 10)) + println(abs_diff(5, 5)) +} diff --git a/vlib/v/gen/wasm/tests/advanced_flow.vv.out b/vlib/v/gen/wasm/tests/advanced_flow.vv.out new file mode 100644 index 00000000000000..c6f1ac9ed68d6d --- /dev/null +++ b/vlib/v/gen/wasm/tests/advanced_flow.vv.out @@ -0,0 +1,32 @@ +=== Advanced Control Flow Tests === +--- Calculator Dispatch +15 +5 +50 +2 +0 +--- Number Categorization +-3 +-2 +-1 +0 +1 +2 +3 +--- Value Comparison +0 +-1 +1 +--- Loop with Skip +6 +15 +--- Range Classification +-1 +0 +1 +2 +3 +--- Absolute Difference +10 +10 +0 diff --git a/vlib/v/gen/wasm/tests/advanced_flow.wasm b/vlib/v/gen/wasm/tests/advanced_flow.wasm new file mode 100644 index 0000000000000000000000000000000000000000..cabc72bb37689c9e8b193044c32ba1043bf022b9 GIT binary patch literal 4060 zcmbtXO>7&-6`q;hC8=FfS~+UtxM(|TsjU=11-V@Ak5Qu<+A)$eHP9w8(38Z-lue2f z1(LGT#v*kp+~kr|kV9JJQm6+T>8a>3xB8Uk*7RneJ_J3vKn^L8L%%o6m8isWQ{u{D26F%W$IOIc&>MuSV zBfv2_Tw_dJ5#QWv^joX_?Z!_3W_Pf<(`(*pHTNoojq9s>z1E<~@~1>h?ex0qtIgM2 z1ICSKmlziU3=PBJ`eR!9lgs5Svw+W(?G)`osZ_EI&Uj8*Bd(Z(e+PHCGh*t&Tl(wZ zP3{bNojLiZL~gY|X!Hi`+1yrhtK0jfU`5XD&z@O&PEZl%xhia)=L=knBvb4qb47CX z_8lGy24gxDrsVD{bLN>R=9y&e1tvuiKIXL-xMOoZ5-XCObDXEh94Y3pdEp@xQ4`pY zfv4CvvN$f;3iG(+6#2sGbbj|$I+D4<6H+MT{K7d&smRj3T+sxyBe@h`*eoOU80^e3 zhIE?yGI;9pcpN(f*+s#0S;#U`83pPJ4RC4qm(pl=^R#Hh-ldHcXi_MOdNxyqPFrCc zI>&!sQT$&k((IEf+6-G`E=L1`Y`aQRVJ> z22^26qt1Q?eKGZ?G%=$(m__bj)S}Us&6PTn#y;Y?XE{=MIkcF!(d#@qotL?`SMa7} zK~7y@Oy(5d^m1awSfRB!=7Dv6f1J@YGpC2v}m1G{B5*K$f=_?7;*}Si0*=+yP)&fo+EAP z9OsefemiT|L}1Y?$)YSB2TpduP;)O#bit%LPlG5eA*YY(Y{}`g&ZTjk-E)-XU&4iq zPYU^@M~58uL|xbF6dhYq%$upg@4Tr`P^RWc;lgp$+(a2saR@upyVyq$xR-NKU6Q#a z^Cx7UCzW*bDNaj$vkT$I0{$ZUT4(_3i_ECWk#}!7d37x08@R;!1M-`rkoDg~% zsY?tK?Z%`zJfJf5Apg_`H3(gEo)o$%^CWa?Kz>r_Rwnd!AEb{~C2W7G_orDEu+Xd|cfS`DAI<=U(|=r1?XVCdUk>-pgb}*9{Iz zK<{UeMG&-m`RLp>aEoJR`Xn3b_i3b|W)Ue3y2T}vsV=2RXJ^+q?#v+#-lmM1vp?w! z$vI15c(H)U>v)gQ`9?JGQ$0Uj84thvog%kDmeZL+{&$Ole3ALGXpnDYzE?CYWHcT9 z`GvlW_46TF{k;)nr}lM*`Yp1Dn5{-d!*P&Avr#M^e#l#ZdbX|j1bQ|_C0=7DP?@5OZDmZLiz!-eqrMYxIfdi4dX#a2{Y=UwP6t0^ z?fO55?!5ann848l?*qI##$apgV_Aom^H>=YwS~C{uqO;u}8q( z;bRbv20v$Q^_#cGXFNG{&Vv|CJ$}I3*q`k#Xu9w;dIzX=gu>J4+dyZIPx5@tlRJ&=*7^lmE|)81zg#T`?fF3sruDwHK^9AVKu78)dYzufgeEMnP1I!YGR3C;_2L z?8ntOh-+~eM{yh{pjb)#q?!atEeVq-iIXH@(PhT&Gscz^(5yxv7sj<9!fNGs(A#Zp zG&=p}pxa+ux*)e(9oZlBTH7}+Uc4yP^;?bY_2zYXsk=SsbvyD(r@JR#Y4!(wiSgy- zWqGO5S>Nq62Hl>#-0JT%2J1HozPh{hTC<1ILGwnp*ZNgs(CTi}z_mtaw~2gPJB?ne zpJFd{yE}5PHMl8X-fZnq)Xy8+HxSin^!u%i*80(G^;*B%*&Q^o*2YG&*95u$08BQX ANdN!< literal 0 HcmV?d00001 diff --git a/vlib/v/gen/wasm/tests/memory_ops.vv b/vlib/v/gen/wasm/tests/memory_ops.vv new file mode 100644 index 00000000000000..9b152dd060b5c1 --- /dev/null +++ b/vlib/v/gen/wasm/tests/memory_ops.vv @@ -0,0 +1,136 @@ +// Test memory operations and bitwise algorithms +// This tests the wasm memory model and complex data manipulation + +fn abs_i64(n i64) i64 { + if n < 0 { + return -n + } + return n +} + +fn max_i64(a i64, b i64) i64 { + if a > b { + return a + } + return b +} + +fn min_i64(a i64, b i64) i64 { + if a < b { + return a + } + return b +} + +fn clamp(val i64, min_val i64, max_val i64) i64 { + if val < min_val { + return min_val + } + if val > max_val { + return max_val + } + return val +} + +fn count_bits(n i64) int { + mut count := 0 + mut num := n + for num != 0 { + count += int(num & 1) + num >>= 1 + } + return count +} + +fn reverse_bits_32(n u32) u32 { + mut result := u32(0) + mut val := n + for i := 0; i < 32; i++ { + result <<= 1 + result |= val & 1 + val >>= 1 + } + return result +} + +fn is_power_of_two(n i64) bool { + if n <= 0 { + return false + } + return (n & (n - 1)) == 0 +} + +fn next_power_of_two(n i64) i64 { + if n <= 1 { + return 1 + } + mut power := i64(1) + for power < n { + power <<= 1 + } + return power +} + +fn hamming_distance(a i64, b i64) int { + xor_val := a ^ b + return count_bits(xor_val) +} + +fn rotate_left_32(n u32, shift int) u32 { + s := u32(shift) & 31 + return (n << s) | (n >> (32 - s)) +} + +fn rotate_right_32(n u32, shift int) u32 { + s := u32(shift) & 31 + return (n >> s) | (n << (32 - s)) +} + +fn swap_bytes_32(n u32) u32 { + return ((n & 0xFF) << 24) | ((n & 0xFF00) << 8) | ((n & 0xFF0000) >> 8) | ((n & 0xFF000000) >> 24) +} + +fn sign_extend_8(n u8) i64 { + if n & 0x80 != 0 { + return i64(n) | i64(0xFFFFFFFFFFFFFF00) + } + return i64(n) +} + +fn main() { + println('=== Memory and Bit Operation Tests ===') + + println('--- Basic Math Operations') + println(abs_i64(-42)) + println(abs_i64(42)) + println(max_i64(10, 20)) + println(min_i64(10, 20)) + println(clamp(15, 10, 20)) + println(clamp(5, 10, 20)) + println(clamp(25, 10, 20)) + + println('--- Bit Counting Operations') + println(count_bits(15)) + println(count_bits(255)) + println(count_bits(0)) + println(count_bits(1023)) + + println('--- Power of Two Operations') + println(is_power_of_two(16)) + println(is_power_of_two(15)) + println(next_power_of_two(10)) + println(next_power_of_two(16)) + + println('--- Hamming Distance') + println(hamming_distance(7, 15)) + println(hamming_distance(0, 255)) + + println('--- Bit Rotation') + println(rotate_left_32(0x12345678, 8)) + println(rotate_right_32(0x12345678, 8)) + + println('--- Byte Operations') + println(swap_bytes_32(0x12345678)) + println(sign_extend_8(0x7F)) + println(sign_extend_8(0xFF)) +} diff --git a/vlib/v/gen/wasm/tests/memory_ops.vv.out b/vlib/v/gen/wasm/tests/memory_ops.vv.out new file mode 100644 index 00000000000000..3087c8deb1c644 --- /dev/null +++ b/vlib/v/gen/wasm/tests/memory_ops.vv.out @@ -0,0 +1,29 @@ +=== Memory and Bit Operation Tests === +--- Basic Math Operations +42 +42 +20 +10 +15 +10 +20 +--- Bit Counting Operations +4 +8 +0 +10 +--- Power of Two Operations +true +false +16 +16 +--- Hamming Distance +4 +8 +--- Bit Rotation +305419896 +2018915346 +--- Byte Operations +2018915346 +127 +-1 diff --git a/vlib/v/gen/wasm/tests/memory_ops.wasm b/vlib/v/gen/wasm/tests/memory_ops.wasm new file mode 100644 index 0000000000000000000000000000000000000000..54282abf05d045d9e6fe4e29505570cb91277733 GIT binary patch literal 4046 zcmbtXO>7&-6`q;ppSYyBvXxeLTj#B%CUR;O{8{cUjT+T%9M@?Zv`rkhH;HiQhLl9g zBo!qmqG<<0XYS|B}d-yskLjU64o2PY&2mM`k=zuO z0P?->{mh&9zMWZ$dT&DtA>_}jRXLPHe27(nZ(}%QmA}NQiarKnH340_4dh@|4fxX- z=r1{dJ0tjvT!t_Tp)RYhY}b3um0r8P*?YOuU)k(7UTHSAOPL!lt!#Ij{f0;%<1w|_ z?Ob1JyxQywX)K)=QYqAG7>3k;iKPBZrBca6W@;*H3Nw?-=f!lPP)Hh5$P^`qG7u&{ zc5g{@D1y)4)L*-ANOK@VVWv;2)Jm^k@Akz~YNN5y>At2!PRi`xPMv>7aYyE5kXbt` z7o-}J2*eK)%Or!_x1_5CglShLNLn+(oENs57bIE>f>aJV;#v#R%t|>_%Op;l=1CTZ zfw0BAvf&D^3GBPTBkZx*Pm)*`wj{}(&mLdS@4r|M2}{|ERDhiK&BG<-M6{R7x&W<^ zBy~S4;-ZegPK+_6)3q<5PTd|`VuvVpQ3>4^mJ;YpfYJjDz~bFM9eG=8$9co|4sRsG zL9WW_Xc1)ATLHGAbNu%e)&I33M;~4>E3h?TNesY=x2xH}G}w>(J{8em8lHtfTGn{{ zAF2XB8OQ%~=kC}BSdkzj6h8sKMDSjeSa2T_IcdV0Wa|rwVgoI3gnudQp?6N2fz4CUgG;xG2FAWWb9Ek_*AT~1+tH&v#4R- zzyK-HT0*E!W!gD1DK}0<7-`E+e8DkqPg9?+bsN=7mY3zni~ z3bsWBvJj0OQEJVe0nP=`4DT&_nz)`B>l>W4pa+8HC#@5Y)YA{yAJ%h`{dX$==;7E3 z-8cA2x_tC(a%dm-1w%+u0^1UldM(cWm^P4nk?UbpU)C|od>Etl$d^&6Gmzpqb&$>DOMakV5+vb`A}00oJQ*As zp?rO>WozL#Hos;(Dxv$NRE;4u^Zw7N5xchJ8W{~N3S=_y!hP=h{ z1N=dmi*i1x+aK&ANPLe6vmdg9{u95{GuY>~-%m!2xhL1o3QZZ@$T6#)V6F%&kL;ON z@Q)aVd`eq@YNOQ(uUTg-++BecA#<(psSz?4A=C=9;jHzz_M!+|j4;yJVuTf2VL^M^ z8ZAbs-wKlxs2`yph*o%N0*0>Zxhq=1zv60H`C3#iL~Z{FaRuwF^;HxMSz{jqdmu+3 z6b*g?P>e9h8oS4i=@AG;gP&oM+i#AJc~(X%Le%!p5oB2l*4MEjWR2}gt|G!9YwQ-V zto2R3b;ugK4a^#0kTv#}Yz2Rc*JFjR7UYJg?e8EajqFe8JwnsyAAt(qWduXj@VkhA zKTgJq6(xhH?SBUJ`JKP~7H|31lhz!T0irD*Ojs7R1&FrXh2_IICo3mMBN*8~iX*La zIu5#y`xy8GVLhcg07ZkJFepbL6f4g^IqpgR&2d6li3NTVc6VRJ-MGJyFjK-hZ+*)& z|87XZKVQth8XNWX_0ILhHht$J70#Zs|ctQ4JMwdfYTqF<~5 zDwWEmO35izOK!<4`K211O6797Qg+JKvRn4bez}IQQl(s}RGdn+;#RzhU#THc$tgP( z$8oBT>v)du)R4MVEmtd5r&_JLRj=w-Ygnx0mfec$xK-D6J=b?@Sia(uLao9&-pym*nG(?1NU-hPR~W}kkv+342$%}$%H zHhTRY!DVS_i9-C!y-v^7`!A0@dfI@Pr#oBiKGHp8`f+Ew(WTA}y1LzY$aJN?vB3eC Yn)rR%zTSv4J>Ti;#kKxgzcCT^AISr@I{*Lx literal 0 HcmV?d00001 diff --git a/vlib/v/gen/wasm/tests/realworld.vv b/vlib/v/gen/wasm/tests/realworld.vv new file mode 100644 index 00000000000000..5c97601d6973ab --- /dev/null +++ b/vlib/v/gen/wasm/tests/realworld.vv @@ -0,0 +1,126 @@ +// Test real-world scenarios using algorithms and computations +// This demonstrates complex calculations and logic + +fn factorial_recursive(n i64) i64 { + if n <= 1 { + return 1 + } + return n * factorial_recursive(n - 1) +} + +fn fibonacci(n int) i64 { + if n <= 1 { + return i64(n) + } + mut a := i64(0) + mut b := i64(1) + for i := 2; i <= n; i++ { + tmp := a + b + a = b + b = tmp + } + return b +} + +fn is_prime(n i64) bool { + if n < 2 { + return false + } + if n == 2 { + return true + } + if n % 2 == 0 { + return false + } + mut i := i64(3) + for i * i <= n { + if n % i == 0 { + return false + } + i += 2 + } + return true +} + +fn prime_count(limit int) int { + mut count := 0 + for i := 2; i < limit; i++ { + if is_prime(i64(i)) { + count++ + } + } + return count +} + +fn gcd(a_ i64, b_ i64) i64 { + mut a := a_ + mut b := b_ + if a < 0 { + a = -a + } + if b < 0 { + b = -b + } + for b != 0 { + a %= b + if a == 0 { + return b + } + b %= a + } + return a +} + +fn lcm(a i64, b i64) i64 { + if a == 0 { + return a + } + res := a * (b / gcd(b, a)) + if res < 0 { + return -res + } + return res +} + +fn power(base i64, exp i64) i64 { + mut result := i64(1) + mut e := exp + mut b := base + for e > 0 { + if e & 1 > 0 { + result *= b + } + b *= b + e >>= 1 + } + return result +} + +fn main() { + println('=== Real World WASM Tests ===') + + println('--- Factorial Tests') + println(factorial_recursive(5)) + println(factorial_recursive(10)) + println(factorial_recursive(0)) + + println('--- Fibonacci Tests') + println(fibonacci(10)) + println(fibonacci(20)) + println(fibonacci(0)) + + println('--- Prime Number Tests') + println(is_prime(17)) + println(is_prime(18)) + println(prime_count(100)) + + println('--- GCD and LCM Tests') + println(gcd(48, 18)) + println(lcm(12, 18)) + println(gcd(17, 19)) + + println('--- Power Tests') + println(power(2, 10)) + println(power(3, 5)) + println(power(5, 0)) +} diff --git a/vlib/v/gen/wasm/tests/realworld.vv.out b/vlib/v/gen/wasm/tests/realworld.vv.out new file mode 100644 index 00000000000000..4cd701767bc76b --- /dev/null +++ b/vlib/v/gen/wasm/tests/realworld.vv.out @@ -0,0 +1,21 @@ +=== Real World WASM Tests === +--- Factorial Tests +120 +3628800 +1 +--- Fibonacci Tests +55 +6765 +0 +--- Prime Number Tests +true +false +25 +--- GCD and LCM Tests +6 +36 +1 +--- Power Tests +1024 +243 +1 diff --git a/vlib/v/gen/wasm/tests/realworld.wasm b/vlib/v/gen/wasm/tests/realworld.wasm new file mode 100644 index 0000000000000000000000000000000000000000..d7e975b40a52720e77c26e8718c192842cff3771 GIT binary patch literal 3771 zcmbtWO>7&-6@D|j{1KOwR<@EFMxDO3R7W|eAi2x^acfk&{%L}^g`1@DsgN0&X}S6{ zNG48!h_q3lHJn3pDsm`_0BxZhk zCZE0A?sqr(yX`yupY9Dd?({lucRP0*Se|{M>sWoDroTzo$R<9tvkDGIC0% zWpSfFX!i#6d~v6FaSXNM07~s?}dM-tN{(-tawC3n{BXp{(lBl$N*i6wA;t{`-vb|C&*a zK0afGurzW73J^?|tJR=n@MyyOoRPsiJQq^oy3_LiNiL|3aPq%m?$EQK3Y)Ereh7V0 z`bQI(6c2V)IOr{?-dFAQjM&_;3z^kxzz?JkdZEvMwfT=6UVc9|f+0NH6REKiBDmz@AhGLX7_v|k@ z*1UPH@%+?LDN9YCBSAY|dJ(HKmcs?pMq=rjnP$Ord4XMw#)`PO?gyK4G>Eo)CNjwivsrFPP_C)cJ5`V=oHLV;~8i@2Z) zQS!=2yF4XD7Xoo!R)vrte;1{aJa<$Ei|25N=qgyc3OY{3bGX9JG!CQs`J`Mkj#aP5 zRjy4PXRBbPOE1k-!B#xaBT+R4&mZwz;CaJyZOpTKRz>-TbRpy8LOyP(Lr!=y(_EdP z;{wZN+bI0OLw$l$x}+2?9VcCyp%IKj*b}|WebhjBMF;FEnHRWpOy(se^O9kAEOV)3 z4wXx;pf%h-$i%_m5z1IOBKTm;Fk;Q<+{D5*5$_%`JmWbdbmOThMib@6pe1ZjHWuV> z+DHvT*E!D$-R6=Kx+*|vR_KL^&_94Mk5fKF_*WqNIHO-dm>IpUg$cs$8DVPbGVu%W zHWlWC4JiBx%6t-U@O-*7v!&Pm7ioS=NmF11rN5cTNL@ELC;@#qfeH$OcCQ?r+ZJwd zj5IshO8;Oyt#lEdY^htEiH$BDk>~X1v$}1J> zl|YAZ!*&oEwuM|zrDv&gnCZG=83(|-5WMsRDkH}`T8l|N9-Iv!hU${eBovk6MS{#S ziLH~G$uoIh?=jt#S?WqfaD>J5H&uMB)1ebuQGd)d5eJ8f`l?G;{Bkyde_BQ_ABnB=qy>Zfc%rFuN-5;lP~>u+{$&EAeOmVb)tzph z72M}s++3+^kTZE!86#&5!g)qxgblKqr+=M&O*tjeQF2)EGlsW1nH@V{$$BYjA|2 z!M_3g)qCTkTp7=p;Arapzavl`q0Z~PVQKVVK&wEPH400k_XHBjPLF+IY4m}})89{K zP{x9p0ZrZiK1NAUyP{*k(&z_3EtJ5Zur&H1&;m#|C@i<8-sv9+^-w=WuDwF(J^a0c z3+2&@?G(wq;C|C_4lP0I&F}oq+G%fZ?`^K`@^>zA{e`!WS(VrMPJ6ezd6Db&dZXT~ z`}LsSs)zNc9@i5=_5e$)5;z;F4X zANjGLAW$P{27V9(tso4dAPy3QZnT;$zZJAvt*{lf;#Pvm8euc^!ys&hVHkyRm|*%w z)QtQnh+0t?MNu3jNT?AvV?Pe!RvgAr9LEV#Y$VOZPlBYCgh`adNs>@>h3FF^T1$|o zA0fFgZUqr$tH*=hTb(!C+x^a9ufO`jMc(ahbAQn5?*8P`rAz!ur@hTL_j=p6`DXg+ z_xZI>f6!<2tgWr_wf5#