Thread overview
Some questions with D and webassembly
Mar 03, 2023
TheZipCreator
Mar 03, 2023
ryuukk_
Mar 03, 2023
TheZipCreator
Mar 03, 2023
Johan
March 03, 2023

In webassembly, there's a type called externref, which opaquely represents a javascript object. So, you could do this for example, with this javascript:

class Foo {
	constructor(x) {
		this.x = x;
	}
}

const imports = {
	env: {
		fooNew: (x) => new Foo(x),
		fooX: (foo) => foo.x,
		fooSetX: (foo, x) => { foo.x = x; }
	}
}

WebAssembly.instantiateStreaming(fetch("./foo.wasm"), imports).then(module => {
	let foo = module.instance.exports.f();
	console.log(foo.x); // 5
	module.instance.exports.g(foo);
	console.log(foo.x); // 8
});

and this webassembly:

(module
	(import "env" "fooNew" (func $fooNew (param i32) (result externref)))
	(import "env" "fooX" (func $fooX (result i32)))
	(import "env" "fooSetX" (func $fooSetX (param externref) (param i32)))
	(func (export "f") (result externref)
		i32.const 5
		call $fooNew)
	(func (export "g") (param $foo externref)
		local.get $foo
		i32.const 8
		call $fooSetX))

5 and 8 get logged. Equivalent D to the webassembly part would be:

extern(C):

// how to get this externref type?
externref fooNew(int);
externref fooX(externref);
void fooSetX(externref, int);

externref f() {
	return fooNew();
}

void g(externref foo) {
	fooSetX(foo, 8);
}

problem being, there exists no externref type. So how would you achieve it with ldc2?

B) In the javascript WebAssembly API, you can pass in memory like so:

const imports = {
	"mem": new WebAssembly.Memory({ initial: 1 })
}

WebAssembly.instantiateStreaming(fetch("bar.wasm"), imports, module => { ... });

then in WebAssembly

(import "mem" (memory 1))

so how could I do that in D? That is, I want the memory that D uses to be accessible to javascript (this way I can pass pointers between JS and D)

March 03, 2023

On Friday, 3 March 2023 at 03:32:37 UTC, TheZipCreator wrote:

>

In webassembly, there's a type called externref, which opaquely represents a javascript object. So, you could do this for example, with this javascript:

class Foo {
	constructor(x) {
		this.x = x;
	}
}

const imports = {
	env: {
		fooNew: (x) => new Foo(x),
		fooX: (foo) => foo.x,
		fooSetX: (foo, x) => { foo.x = x; }
	}
}

WebAssembly.instantiateStreaming(fetch("./foo.wasm"), imports).then(module => {
	let foo = module.instance.exports.f();
	console.log(foo.x); // 5
	module.instance.exports.g(foo);
	console.log(foo.x); // 8
});

and this webassembly:

(module
	(import "env" "fooNew" (func $fooNew (param i32) (result externref)))
	(import "env" "fooX" (func $fooX (result i32)))
	(import "env" "fooSetX" (func $fooSetX (param externref) (param i32)))
	(func (export "f") (result externref)
		i32.const 5
		call $fooNew)
	(func (export "g") (param $foo externref)
		local.get $foo
		i32.const 8
		call $fooSetX))

5 and 8 get logged. Equivalent D to the webassembly part would be:

extern(C):

// how to get this externref type?
externref fooNew(int);
externref fooX(externref);
void fooSetX(externref, int);

externref f() {
	return fooNew();
}

void g(externref foo) {
	fooSetX(foo, 8);
}

problem being, there exists no externref type. So how would you achieve it with ldc2?

B) In the javascript WebAssembly API, you can pass in memory like so:

const imports = {
	"mem": new WebAssembly.Memory({ initial: 1 })
}

WebAssembly.instantiateStreaming(fetch("bar.wasm"), imports, module => { ... });

then in WebAssembly

(import "mem" (memory 1))

so how could I do that in D? That is, I want the memory that D uses to be accessible to javascript (this way I can pass pointers between JS and D)

https://discourse.llvm.org/t/rfc-webassembly-reference-types-in-clang/66939

it says it is an opaque type, maybe just need to be void*?

Also there are new intrinsics, maybe you can define them like this:

    alias externref_t = void*;

    pragma(LDC_intrinsic, "llvm.wasm.table.set.externref.i32")
    extern(C) void llvm_wasm_table_set_externref(void*, int, externref_t);

    pragma(LDC_intrinsic, "llvm.wasm.table.get.externref.i32")
    extern(C) externref_t llvm_wasm_table_get_externref(void*, int);
March 03, 2023

On Friday, 3 March 2023 at 13:42:55 UTC, ryuukk_ wrote:

>

On Friday, 3 March 2023 at 03:32:37 UTC, TheZipCreator wrote:

>

[...]

https://discourse.llvm.org/t/rfc-webassembly-reference-types-in-clang/66939

it says it is an opaque type, maybe just need to be void*?

Also there are new intrinsics, maybe you can define them like this:

    alias externref_t = void*;

    pragma(LDC_intrinsic, "llvm.wasm.table.set.externref.i32")
    extern(C) void llvm_wasm_table_set_externref(void*, int, externref_t);

    pragma(LDC_intrinsic, "llvm.wasm.table.get.externref.i32")
    extern(C) externref_t llvm_wasm_table_get_externref(void*, int);

Using void* doesn't appear to work (and looking at the generated wasm it seems to reduce it to i32, which is not the right type unfortunately). I guess what I could do is have a global array in js which stores all objects I'll need to access, then just access them via the index into that array but that feels like a hack especially when externref is a thing that exists.

March 03, 2023

On Friday, 3 March 2023 at 18:34:24 UTC, TheZipCreator wrote:

>

On Friday, 3 March 2023 at 13:42:55 UTC, ryuukk_ wrote:

>

On Friday, 3 March 2023 at 03:32:37 UTC, TheZipCreator wrote:

>

[...]

https://discourse.llvm.org/t/rfc-webassembly-reference-types-in-clang/66939

It looks like this needs some more compiler support. Please submit the feature request (including the link to this LLVM page) on LDC's github page.

cheers,
Johan