跳到正文
源码达人

【wasm】自定义函数

WebAssembly 柠檬鱼

c语言片段

新建文件名为side_module.c的文件

  

int Increment(int value) {
	return value + 1;
}

  

int Decrement(int value) {
	return value - 1;
}

O1编译

命令

emcc side_module.c -s SIDE_MODULE=2 -O1 -s EXPORTED_FUNCTIONS='["_Increment", "_Decrement"]' -o build/side_module.wasm

SIDE_MODULE=2标记会告诉Emscripten不想在生成的模块中包含像C标准库这样的东西,也不想生成JavaScript plumbing文件。

新建html导入

将编译好的wasm文件通过js加载,内容如下:

(module
  (global $env.__memory_base (;0;) (import "env" "__memory_base") i32)
  (global $env.__table_base (;1;) (import "env" "__table_base") i32)
  (memory $env.memory (;0;) (import "env" "memory") 0)
  (func $__wasm_call_ctors (;0;) (export "__wasm_call_ctors")
  )
  (func $__wasm_apply_data_relocs (;1;) (export "__wasm_apply_data_relocs")
  )
  (func $Increment (;2;) (export "Increment") (param $var0 i32) (result i32)
    local.get $var0
    i32.const 1
    i32.add
  )
  (func $Decrement (;3;) (export "Decrement") (param $var0 i32) (result i32)
    local.get $var0
    i32.const -1
    i32.add
  )
)

此时js要正常加载,需要配置全局变量和内存,完整代码:

<script>

	let memoryBase = new WebAssembly.Global({value: 'i32'}, 1024);
	
	let tableBase = new WebAssembly.Global({value: 'i32'}, 1024);
	
	let memory = new WebAssembly.Memory({initial: 256, maximum: 256});
	
	  
	
	const importObject = {
	
	env: {
			memory: memory,	
			__memory_base: memoryBase,
			__table_base: tableBase,
	
		}
	
	};
	
	  
	
	WebAssembly.instantiateStreaming(fetch('side_module.wasm'), importObject).then(result => {
	
	console.log(result.instance.exports);
	
	const value = result.instance.exports.Increment(17);
	
	console.log(value);
	
	})

</script>

O2或O3编译

wasm内容与上面有些不同:

(module
  (func $__wasm_call_ctors (;0;) (export "__wasm_call_ctors") (export "__wasm_apply_data_relocs")
    nop
  )
  (func $Increment (;1;) (export "Increment") (param $var0 i32) (result i32)
    local.get $var0
    i32.const 1
    i32.add
  )
  (func $Decrement (;2;) (export "Decrement") (param $var0 i32) (result i32)
    local.get $var0
    i32.const 1
    i32.sub
  )
)

此时不用定义全局变量和内存就可以正常调用:

<script>
	WebAssembly.instantiateStreaming(fetch('side_module.wasm')).then(result => {
	
	console.log(result.instance.exports);
	
	const value = result.instance.exports.Increment(17);
	
	console.log(value);
	
	})

</script>

总结

选项O2比O1优化更多内容,O1选择需要额外定义env配置来适配模块的全局变量和内存。模块文件分前导、已知段和自定义段,其中Table段定义函数引用数组,Memory段定义模块线性内存,相当于堆内存,Global定义全局变量。 程序运行时,操作系统会先分配内存给全局变量和函数使用,由于wasm运行在js虚拟机不能直接申请内存,只能在实例化模块时预先申请ArrayBuffer充当内存使用。 在学习阶段可以多次编译,查看wasm内容或程序的需要来决定是否需要设置env。