Skip to content

Commit

Permalink
merged wasp.js
Browse files Browse the repository at this point in the history
IMPOSSIBLE to ln !!!
  • Loading branch information
pannous committed Apr 23, 2024
1 parent 75857c4 commit 166678c
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 48 deletions.
64 changes: 50 additions & 14 deletions docs/wasp.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*
/*
* WASP: WebAssembly Programming Language API/ABI
* This file contains the javascript counterpoint to the WASP runtime,
* offering host functions to wasi/wasp modules, like download() and run_wasm()
Expand Down Expand Up @@ -49,8 +49,8 @@ function format(object) {
}

function error(msg) {
if (results) results.value = "⚠️ ERROR: " + msg + "\n";
// if(msg is error
if (typeof results !== 'undefined')
results.value = "⚠️ ERROR: " + msg + "\n";
if (msg instanceof Error)
throw msg
else
Expand Down Expand Up @@ -94,7 +94,46 @@ var resume; // callback function resuming after run_wasm finished
class YieldThread { // unwind wasm, reenter through resume() after run_wasm finished
}

function matrix_multiply(a, b, k = 1) {
warn("vector.dot / mul shim via webgpu-blas until wasm vector proposal is available")
a = node(a, memory)
b = node(b, memory)
// todo other types
if (a.kind == kinds.list && a.type == kinds.float32)
a.kind = kinds.vector // todo add offset and length
if (a.kind == kinds.buffer && a.type == kinds.float32)
a.kind = kinds.vector
if (b.kind == kinds.buffer && b.type == kinds.float32)
b.kind = kinds.vector
if (b.kind == kinds.matrix)
k = b.row_count
if (b.kind == kinds.matrix)
k = b.column_count
if (a.kind != kinds.vector || b.kind != kinds.vector)
throw new Error("dot product only for vectors")
// efficient implementation, the condition m % 32 === 0 && n % 64 === 0 && k % 4 === 0 && alpha === 1.0 have to met.

include("https://github.com/milhidaka/webgpu-blas/releases/download/v1.2.3/webgpublas.js")
let array_a = new Float32Array(buffer, a.offset, a.length)
let array_b = new Float32Array(buffer, b.offset, b.length)

const alpha = 1.0; // ?
let m = a.length / k
let n = b.length / k
// await
const result = webgpublas.sgemm(m, n, k, alpha, array_a, array_b);
console.log(result); // m*n row-major matrix (Float32Array)
return result
}

let imports = {
vector: { // todo: use wasm vector proposal when available, using webgpu-blas as a shim
dot: (a, b) => {
matrix_multiply(a, b, 1)
},
mul: matrix_multiply,
matrix_multiply
},
env: { // MY_WASM custom wasp helpers
memory, // optionally provide js Memory … alternatively use exports.memory in js, see below
HEAP_END: new WebAssembly.Global({value: "i32", mutable: true}, 0),// todo: use as HEAP_END
Expand Down Expand Up @@ -724,7 +763,7 @@ function load_runtime_bytes() {
// console.log(obj.instance.exports._ZN6StringC2EPKcb.getArguments())
// getArguments(obj.instance.exports._ZN6StringC2EPKcb)
})
copy_runtime_bytes_to_compiler()
// copy_runtime_bytes()
}
)
}
Expand All @@ -750,7 +789,11 @@ function wasp_ready() {
// moduleReflection(wasm_data);
loadKindMap()
load_runtime_bytes() // smaller than compiler
register_wasp_functions(compiler_instance.exports)
try {
register_wasp_functions(compiler_instance.exports)
} catch (err) {
error(err)
}
// testRun1()
if (run_tests)
setTimeout(test, 1);// make sync
Expand Down Expand Up @@ -806,10 +849,7 @@ async function run_wasm(buf_pointer, buf_size) {

if (needs_runtime && use_big_runtime) {
// app = await WebAssembly.instantiate(wasm_buffer, {env: Wasp}, memory) // todo: tweaked imports if it calls out
// let merged_imports = {...Wasp, ...imports};
// let merged_imports = {env:Wasp}
let merged_imports = imports // compiler_exports
app = await WebAssembly.instantiate(wasm_buffer, merged_imports, memory) // todo: tweaked imports if it calls out
app = await WebAssembly.instantiate(wasm_buffer, imports, memory) // todo: tweaked imports if it calls out
} else if (needs_runtime) {
print("needs_runtime runtime loading")
if (!RUNTIME_BYTES) // Cannot compile WebAssembly.Module from an already read Response TODO reuse!
Expand Down Expand Up @@ -888,11 +928,7 @@ function wasm_to_wat(buffer) {
memory64: true,
}

// var readDebugNames = true;
var readDebugNames = false;
console.log("wasm_to_wat")
// console.log(wabt.validate(buffer, {readDebugNames}));
var module = wabt.readWasm(buffer, {readDebugNames}, wabtFeatures);
var module = wabt.readWasm(buffer, {readDebugNames: true}, wabtFeatures);
module.generateNames();
module.applyNames();
const result = module.toText({foldExprs: true, inlineExport: true});
Expand Down
85 changes: 51 additions & 34 deletions source/wasp.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ let WASP_RUNTIME = 'wasp-runtime.wasm'

var RUNTIME_BYTES = null // for reflection or linking
var needs_runtime = false;
var use_big_runtime = true; // use compiler as runtime for now
var run_tests = true; // todo NOT IN PRODUCTION!
var app_module
let kinds = {}
Expand Down Expand Up @@ -48,7 +49,11 @@ function format(object) {
}

function error(msg) {
if (results) results.value = "⚠️ ERROR: " + msg + "\n";
if (typeof results !== 'undefined')
results.value = "⚠️ ERROR: " + msg + "\n";
if (msg instanceof Error)
throw msg
else
throw new Error("⚠️ ERROR: " + msg)
}

Expand Down Expand Up @@ -131,7 +136,7 @@ let imports = {
},
env: { // MY_WASM custom wasp helpers
memory, // optionally provide js Memory … alternatively use exports.memory in js, see below
heap_end: new WebAssembly.Global({value: "i32", mutable: true}, 0),// todo: use as heap_end
HEAP_END: new WebAssembly.Global({value: "i32", mutable: true}, 0),// todo: use as HEAP_END
grow_memory: x => memory.grow(1), // à 64k … NO NEED, host grows memory automagically!
async_yield: x => { // called from inside wasm, set callback handler resume before!
throw new YieldThread() // unwind wasm, reenter through resume() after run_wasm
Expand All @@ -149,7 +154,7 @@ let imports = {
}, // allow wasm modules to run plugins / compiler output
assert_expect: x => {
if (expect_test_result)
error("already expecting value " + expect_test_result + " -> " + x)
error("already expecting value " + expect_test_result + " -> " + x + "\n Did you emit in testCurrent?")
expect_test_result = new node(x).Value()
},
// HTML DOM JS functions
Expand Down Expand Up @@ -266,9 +271,9 @@ function debugMemory(pointer, num, mem) {
function string(data) { // wasm<>js interop
switch (typeof data) {
case "string":
while (heap_end % 8) heap_end++
let p = heap_end
new_int(heap_end + 8)
while (HEAP_END % 8) HEAP_END++
let p = HEAP_END
new_int(HEAP_END + 8)
new_int(data.length)
chars(data);
return p;
Expand Down Expand Up @@ -313,10 +318,10 @@ function chars(data) {
if (!data) return 0;// MAKE SURE!
if (typeof data != "string") return load_chars(data)
const uint8array = new TextEncoder("utf-8").encode(data + "\0");
buffer = new Uint8Array(memory.buffer, heap_end, uint8array.length);
buffer = new Uint8Array(memory.buffer, HEAP_END, uint8array.length);
buffer.set(uint8array, 0);
let c = heap_end
heap_end += uint8array.length;
let c = HEAP_END
HEAP_END += uint8array.length;
return c;
}

Expand All @@ -327,19 +332,19 @@ function set_int(address, val) {
}

function new_int(val) {
while (heap_end % 4) heap_end++;
let buf = new Uint32Array(memory.buffer, heap_end, 4);
while (HEAP_END % 4) HEAP_END++;
let buf = new Uint32Array(memory.buffer, HEAP_END, 4);
buf[0] = val
heap_end += 4
HEAP_END += 4
}


function new_long(val) {
// memory.setUint32(addr + 0, val, true);
// memory.setUint32(addr + 4, Math.floor(val / 4294967296), true);
let buf = new Uint64Array(memory.buffer, heap_end / 8, memory.length); // todo: /8??
let buf = new Uint64Array(memory.buffer, HEAP_END / 8, memory.length); // todo: /8??
buf[0] = val
heap_end += 8
HEAP_END += 8
}

function read_byte(pointer, mem) {
Expand Down Expand Up @@ -380,12 +385,12 @@ function read_int64(pointer, mem) { // little endian
function reset_heap() {
HEAP = runtime_exports.__heap_base; // ~68000
DATA_END = runtime_exports.__data_end
heap_end = HEAP || DATA_END;
heap_end += 0x100000 * run++; // todo
HEAP_END = HEAP || DATA_END;
HEAP_END += 0x100000 * run++; // todo
}

function compile_and_run(code) {
// if (typeof runtime_exports === 'undefined') load_runtime();
if (typeof runtime_exports === 'undefined') load_runtime();
// reset_heap();
expect_test_result = false
runtime_exports.run(chars(code));
Expand Down Expand Up @@ -676,15 +681,15 @@ function binary_diff(old_mem, new_mem) {
}
}


function copy_runtime_bytes() {
// let compiler link/merge emitted wasm with small runtime
function copy_runtime_bytes_to_compiler() {
let length = runtime_bytes.byteLength
let pointer = heap_end
let pointer = HEAP_END
let src = new Uint8Array(runtime_bytes, 0, length);
let dest = new Uint8Array(memory.buffer, heap_end, length);
dest.set(src) // memcpy
heap_end += length
runtime_exports.testRuntime(pointer, length)
let dest = new Uint8Array(memory.buffer, HEAP_END, length);
dest.set(src) // memcpy ⚠️ todo MAY FUCK UP compiler bytes!!!
HEAP_END += length
// compiler_exports.testRuntime(pointer, length)
}

// minimal demangler for wasm functions does not offer full reflection
Expand Down Expand Up @@ -743,7 +748,10 @@ function addSynonyms(exports) {
return exports
}

// runtime_bytes for linking small wasp programs with runtime
// runtime_bytes for linking small wasp programs with runtime. The result SHOULD BE standalone wasm!
// this is NOT NEEDED when
// 1. the wasm module is already standalone and doesn't import any functions from the runtime
// 2. the host environment uses the full wasp.wasm (with compiler) as runtime
function load_runtime_bytes() {
fetch(WASP_RUNTIME).then(resolve => resolve.arrayBuffer()).then(buffer => {
runtime_bytes = buffer
Expand Down Expand Up @@ -780,14 +788,20 @@ function wasp_ready() {
console.log("wasp is ready")
// moduleReflection(wasm_data);
loadKindMap()
// load_runtime_bytes()
load_runtime_bytes() // smaller than compiler
try {
register_wasp_functions(compiler_instance.exports)
} catch (err) {
error(err)
}
// testRun1()
if (run_tests)
setTimeout(test, 1);// make sync
}

// todo: CONFUSION between runtime and compiler!
function load_runtime() {
if (typeof compiler_exports !== 'undefined') return
WASP_COMPILER_BYTES = fetch(WASP_COMPILER)
WebAssembly.instantiateStreaming(WASP_COMPILER_BYTES, imports).then(obj => {
compiler_instance = obj.instance
Expand All @@ -797,8 +811,8 @@ function load_runtime() {
addSynonyms(compiler_exports)
HEAP = compiler_exports.__heap_base; // ~68000
DATA_END = compiler_exports.__data_end
heap_end = HEAP || DATA_END;
heap_end += 0x100000
HEAP_END = HEAP || DATA_END;
HEAP_END += 0x100000
memory = compiler_exports.memory || compiler_exports._memory || memory
buffer = new Uint8Array(memory.buffer, 0, memory.length);
main = compiler_instance.start || compiler_exports.teste || compiler_exports.main || compiler_exports.wasp_main || compiler_exports._start
Expand All @@ -818,15 +832,15 @@ function load_runtime() {
}

async function run_wasm(buf_pointer, buf_size) {
try {
try { // WE WANT PURE STACK TRACE
wasm_buffer = buffer.subarray(buf_pointer, buf_pointer + buf_size)
wasm_to_wat(wasm_buffer)
// wasm_to_wat(wasm_buffer)
// download_file(wasm_buffer, "emit.wasm", "wasm")

app_module = await WebAssembly.compile(wasm_buffer)
if (WebAssembly.Module.imports(app_module).length > 0) {
needs_runtime = true
use_big_runtime = true
// use_big_runtime = false
print(app_module) // visible in browser console, not in terminal
Wasp.download = download
// print(WebAssembly.Module.customSections(app_module)) // Argument 1 is required ?
Expand All @@ -841,6 +855,7 @@ async function run_wasm(buf_pointer, buf_size) {
if (!RUNTIME_BYTES) // Cannot compile WebAssembly.Module from an already read Response TODO reuse!
RUNTIME_BYTES = await fetch(WASP_RUNTIME)
let runtime_instance = await WebAssembly.instantiateStreaming(RUNTIME_BYTES, imports)
runtime_exports = runtime_instance.exports
// {
// wasi_unstable: {
// fd_write,
Expand Down Expand Up @@ -887,9 +902,11 @@ async function run_wasm(buf_pointer, buf_size) {
}
results.value = result // JSON.stringify( Do not know how to serialize a BigInt
return result; // useless, returns Promise!
} catch (ex) {
console.error(ex)
error(ex)
} catch (error) {
throw new Error(`Error in run_wasm: ${error.message}\nStack: ${error.stack}`);
// console.error(ex)
// throw ex
// // error(ex)
}
}

Expand Down

0 comments on commit 166678c

Please sign in to comment.