Browse Source

wasm: fix runtime.js even more for wasm64p32

- make the int size configurable in the `runWasm` call, no more
  constants to hunt down and change
- make storeU64 and storeI64 handle bigints, this is needed in the
  odin_dom library
- fix alignment issues within init_event_raw
Laytan Laats 1 year ago
parent
commit
8a521648b9
1 changed files with 63 additions and 29 deletions
  1. 63 29
      vendor/wasm/js/runtime.js

+ 63 - 29
vendor/wasm/js/runtime.js

@@ -13,14 +13,18 @@ function stripNewline(str) {
     return str.replace(/\n/, ' ')
 }
 
-const INT_SIZE    = 4; // NOTE: set to `8` if the target has 64 bit ints (`wasm64p32` for example).
-const STRING_SIZE = 2*INT_SIZE;
-
 class WasmMemoryInterface {
 	constructor() {
 		this.memory = null;
 		this.exports = null;
 		this.listenerMap = {};
+
+		// Size (in bytes) of the integer type, should be 4 on `js_wasm32` and 8 on `js_wasm64p32`
+		this.intSize = 4;
+	}
+
+	setIntSize(size) {
+		this.intSize = size;
 	}
 
 	setMemory(memory) {
@@ -73,21 +77,21 @@ class WasmMemoryInterface {
 	loadF32(addr) { return this.mem.getFloat32(addr, true); }
 	loadF64(addr) { return this.mem.getFloat64(addr, true); }
 	loadInt(addr) {
-		if (INT_SIZE == 8) {
+		if (this.intSize == 8) {
 			return this.loadI64(addr);
-		} else if (INT_SIZE == 4) {
+		} else if (this.intSize == 4) {
 			return this.loadI32(addr);
 		} else {
-			throw new Error('Unhandled `INT_SIZE`, expected `4` or `8`');
+			throw new Error('Unhandled `intSize`, expected `4` or `8`');
 		}
 	};
 	loadUint(addr) {
-		if (INT_SIZE == 8) {
+		if (this.intSize == 8) {
 			return this.loadU64(addr);
-		} else if (INT_SIZE == 4) {
+		} else if (this.intSize == 4) {
 			return this.loadU32(addr);
 		} else {
-			throw new Error('Unhandled `INT_SIZE`, expected `4` or `8`');
+			throw new Error('Unhandled `intSize`, expected `4` or `8`');
 		}
 	};
 	loadPtr(addr) { return this.loadU32(addr); }
@@ -108,31 +112,43 @@ class WasmMemoryInterface {
 	storeU32(addr, value) { this.mem.setUint32 (addr, value, true); }
 	storeI32(addr, value) { this.mem.setInt32  (addr, value, true); }
 	storeU64(addr, value) {
-		this.mem.setUint32(addr + 0, value, true);
-		this.mem.setUint32(addr + 4, Math.floor(value / 4294967296), true);
+		this.mem.setUint32(addr + 0, Number(value), true);
+
+		let div = 4294967296;
+		if (typeof value == 'bigint') {
+			div = BigInt(div);
+		}
+
+		this.mem.setUint32(addr + 4, Math.floor(Number(value / div)), true);
 	}
 	storeI64(addr, value) {
-		this.mem.setUint32(addr + 0, value, true);
-		this.mem.setInt32 (addr + 4, Math.floor(value / 4294967296), true);
+		this.mem.setUint32(addr + 0, Number(value), true);
+
+		let div = 4294967296;
+		if (typeof value == 'bigint') {
+			div = BigInt(div);
+		}
+
+		this.mem.setInt32(addr + 4, Math.floor(Number(value / div)), true);
 	}
 	storeF32(addr, value) { this.mem.setFloat32(addr, value, true); }
 	storeF64(addr, value) { this.mem.setFloat64(addr, value, true); }
 	storeInt(addr, value) {
-		if (INT_SIZE == 8) {
+		if (this.intSize == 8) {
 			this.storeI64(addr, value);
-		} else if (INT_SIZE == 4) {
+		} else if (this.intSize == 4) {
 			this.storeI32(addr, value);
 		} else {
-			throw new Error('Unhandled `INT_SIZE`, expected `4` or `8`');
+			throw new Error('Unhandled `intSize`, expected `4` or `8`');
 		}
 	}
 	storeUint(addr, value) {
-		if (INT_SIZE == 8) {
+		if (this.intSize == 8) {
 			this.storeU64(addr, value);
-		} else if (INT_SIZE == 4) {
+		} else if (this.intSize == 4) {
 			this.storeU32(addr, value);
 		} else {
-			throw new Error('Unhandled `INT_SIZE`, expected `4` or `8`');
+			throw new Error('Unhandled `intSize`, expected `4` or `8`');
 		}
 	}
 
@@ -241,10 +257,11 @@ class WebGLInterface {
 		}
 	}
 	getSource(shader, strings_ptr, strings_length) {
+		const stringSize = this.mem.intSize*2;
 		let source = "";
 		for (let i = 0; i < strings_length; i++) {
-			let ptr = this.mem.loadPtr(strings_ptr + i*STRING_SIZE);
-			let len = this.mem.loadPtr(strings_ptr + i*STRING_SIZE + 4);
+			let ptr = this.mem.loadPtr(strings_ptr + i*stringSize);
+			let len = this.mem.loadPtr(strings_ptr + i*stringSize + 4);
 			let str = this.mem.loadString(ptr, len);
 			source += str;
 		}
@@ -1151,10 +1168,11 @@ class WebGLInterface {
 			},
 			TransformFeedbackVaryings: (program, varyings_ptr, varyings_len, bufferMode) => {
 				this.assertWebGL2();
+				const stringSize = this.mem.intSize*2;
 				let varyings = [];
 				for (let i = 0; i < varyings_len; i++) {
-					let ptr = this.mem.loadPtr(varyings_ptr + i*STRING_SIZE + 0*4);
-					let len = this.mem.loadPtr(varyings_ptr + i*STRING_SIZE + 1*4);
+					let ptr = this.mem.loadPtr(varyings_ptr + i*stringSize + 0*4);
+					let len = this.mem.loadPtr(varyings_ptr + i*stringSize + 1*4);
 					varyings.push(this.mem.loadString(ptr, len));
 				}
 				this.ctx.transformFeedbackVaryings(this.programs[program], varyings, bufferMode);
@@ -1393,7 +1411,7 @@ function odinSetupDefaultImports(wasmMemoryInterface, consoleElement) {
 		},
 		"odin_dom": {
 			init_event_raw: (ep) => {
-				const W = 4;
+				const W = wasmMemoryInterface.intSize;
 				let offset = ep;
 				let off = (amount, alignment) => {
 					if (alignment === undefined) {
@@ -1407,6 +1425,13 @@ function odinSetupDefaultImports(wasmMemoryInterface, consoleElement) {
 					return x;
 				};
 
+				let align = (alignment) => {
+					const modulo = offset & (alignment-1);
+					if (modulo != 0) {
+						offset += alignment - modulo
+					}
+				};
+
 				let wmi = wasmMemoryInterface;
 
 				let e = event_temp_data.event;
@@ -1427,10 +1452,12 @@ function odinSetupDefaultImports(wasmMemoryInterface, consoleElement) {
 					wmi.storeU32(off(4), 0);
 				}
 
-				wmi.storeUint(off(W), event_temp_data.id_ptr);
+				align(W);
+
+				wmi.storeI32(off(W), event_temp_data.id_ptr);
 				wmi.storeUint(off(W), event_temp_data.id_len);
-				wmi.storeUint(off(W), 0); // padding
 
+				align(8);
 				wmi.storeF64(off(8), e.timeStamp*1e-3);
 
 				wmi.storeU8(off(1), e.eventPhase);
@@ -1442,7 +1469,7 @@ function odinSetupDefaultImports(wasmMemoryInterface, consoleElement) {
 				wmi.storeU8(off(1), !!e.isComposing);
 				wmi.storeU8(off(1), !!e.isTrusted);
 
-				let base = off(0, 8);
+				align(8);
 				if (e instanceof WheelEvent) {
 					wmi.storeF64(off(8), e.deltaX);
 					wmi.storeF64(off(8), e.deltaY);
@@ -1689,8 +1716,15 @@ function odinSetupDefaultImports(wasmMemoryInterface, consoleElement) {
 	};
 };
 
-async function runWasm(wasmPath, consoleElement, extraForeignImports) {
-	let wasmMemoryInterface = new WasmMemoryInterface();
+/**
+ * @param {string} wasmPath                          - Path to the WASM module to run
+ * @param {?HTMLPreElement} consoleElement           - Optional console/pre element to append output to, in addition to the console
+ * @param {any} extraForeignImports                  - Imports, in addition to the default runtime to provide the module
+ * @param {?int} intSize                             - Size (in bytes) of the integer type, should be 4 on `js_wasm32` and 8 on `js_wasm64p32`
+ */
+async function runWasm(wasmPath, consoleElement, extraForeignImports, intSize = 4) {
+	const wasmMemoryInterface = new WasmMemoryInterface();
+	wasmMemoryInterface.setIntSize(intSize);
 
 	let imports = odinSetupDefaultImports(wasmMemoryInterface, consoleElement);
 	let exports = {};