我能以某种方式构建 webassembly 代码*没有* emscripten "glue" 吗?
Can I somehow build webassembly code *without* the emscripten "glue"?
我能否以某种方式创建一个 wasm 文件,它将按照 in MDN here 的描述自行运行(通过实例化对象并调用它们的函数)?
我能找到的所有指南 (such as this one on MDN) 都推荐使用 emscripten;然而,这也将包括 ~70kB "glue code"(带有 ~50kB 可选文件系统仿真),它具有额外的逻辑(如检测 node/browser 环境和自动获取等),并且可能还有一些其他仿真。
如果我不想要 "glue code" 并且只想直接创建 WASM(可能是从 C 代码,但也可能是其他代码)怎么办?现在可以吗?
你可以,而且随着时间的推移会变得更容易!
如果你想完全避免使用 C++,可以创建 WebAssembly 模块,例如 spec tests or the WebKit test suite.
即使没有 Emscripten,即使使用 C++ 也可以。 wasm-stat.us does it for e.g. the GCC torture tests. Check out its build output, or look at its source.
例如,它将为编译执行以下操作 / link / assemble:
# Get a .o file:
src/work/wasm-install/bin/clang src/work/gcc/gcc/testsuite/gcc.c-torture/execute/20020227-1.c -o src/work/torture-o/20020227-1.c.o --std=gnu89 -DSTACK_SIZE=1044480 -w -Wno-implicit-function-declaration --target=wasm32-unknown-unknown-wasm -c -O2 --sysroot=src/work/wasm-install/sysroot
# Link with libc:
src/work/wasm-install/bin/lld -flavor wasm -entry=main --allow-undefined-file=src/work/wasm-install/sysroot/lib/wasm.syms -o src/work/torture-lld-musl/20020510-1.c.o.wasm src/work/torture-o/20020510-1.c.o src/work/wasm-install/sysroot/lib/libc.a
# Or without a libc (you need to provide one somehow):
src/work/wasm-install/bin/lld -flavor wasm -entry=main --allow-undefined-file=src/work/wasm-install/sysroot/lib/wasm.syms -o src/work/torture-lld/20020510-1.c.o.wasm src/work/torture-o/20020510-1.c.o
# Or, if you want an assembly file instead:
src/work/wasm-install/bin/clang src/work/gcc/gcc/testsuite/gcc.c-torture/execute/20020227-1.c -o src/work/torture-s/20020227-1.c.s --std=gnu89 -DSTACK_SIZE=1044480 -w -Wno-implicit-function-declaration --target=wasm32-unknown-unknown -S -O2 --sysroot=src/work/wasm-install/sysroot
# And get the binary file:
src/work/wasm-install/bin/wast2wasm src/work/torture-s2wasm/loop-6.c.s.wast -o src/work/torture-wast2wasm/loop-6.c.s.wast.wasm
您甚至可以下载瀑布的所有构建工件,包括完整的工具链。只需单击绿色框并找到您要查找的下载。
如果您胆子大,您甚至可以 write your libc in JavaScript 而不是 link 使用 C 语言编写的现有实现。
当你说 "on its own" 时,请记住 WebAssembly 目前不能在没有 link 到它的嵌入器(即 JavaScript)的情况下做任何事情。 Emscripten 遵循的模型(我希望其他人也这样做)是 JavaScript 是微内核并提供系统调用。
您可以使用 emscripten 生成相当少的代码输出。
考虑以下简单文件 adder.c
:
int adder (int a, int b) {
return a + b;
}
像这样编译它(需要相当新的 emscripten):
emcc -O2 -s WASM=1 -s SIDE_MODULE=1 -o adder.wasm
要查看它生成的内容,请使用 binaryen 的 wasm-dis
将其反汇编为 wast 文本形式(您也可以使用 wabt 的 wasm2wast):
wasm-dis adder.wasm -o adder.wast
反汇编后的源代码应该是这样的:
(module
(type [=13=] (func (param i32 i32) (result i32)))
(type (func))
(import "env" "memoryBase" (global $import[=13=] i32))
(import "env" "memory" (memory [=13=] 256))
(import "env" "table" (table 0 anyfunc))
(import "env" "tableBase" (global $import i32))
(global $global[=13=] (mut i32) (i32.const 0))
(global $global (mut i32) (i32.const 0))
(export "__post_instantiate" (func ))
(export "runPostSets" (func ))
(export "_adder" (func [=13=]))
(func [=13=] (type [=13=]) (param $var[=13=] i32) (param $var i32) (result i32)
(i32.add
(get_local $var)
(get_local $var[=13=])
)
)
(func (type )
(nop)
)
(func (type )
(block $label[=13=]
(set_global $global[=13=]
(get_global $import[=13=])
)
(set_global $global
(i32.add
(get_global $global[=13=])
(i32.const 5242880)
)
)
(call )
)
)
;; custom section "dylink", size 5
)
然后您可以在节点(v8.X 或更高版本)中 运行 像这样:
const WA = WebAssembly,
env = {memoryBase: 0,
tableBase: 0,
memory: new WA.Memory({initial: 256}),
table: new WA.Table({initial: 0, element: 'anyfunc'})},
code = new Uint8Array(require('fs').readFileSync('adder.wasm'))
WA.compile(code).then(m => {
return new WA.Instance(m, {env: env})
}).then(i => {
console.log(i.exports._adder(7, 8))
})
请注意,如果您想支持使用堆栈的代码 and/or 堆内存,事情会变得更加复杂。 IE。在调用任何其他导出之前,您至少需要设置 memoryBase 并从主机环境调用 __post_instantiate
。
如果你想在没有 JavaScript 环境的情况下解释 WebAssembly 代码,你可以 运行 使用 wac/wace (完全公开:我创建了这个项目)。请注意,wace
假定您定义了“_main”或 "main" 函数。
您可以使用 ONLY_MY_CODE 标志,这将只生成没有 glue.js 的 wasm 模块,例如
emcc -O1 ./src/foo.cpp -o release/foo.wasm -s WASM=1 -s ONLY_MY_CODE=1
来自 Settings.js https://github.com/kripken/emscripten/blob/master/src/settings.js#L583 :
var ONLY_MY_CODE = 0; // This disables linking and other causes of adding extra code
// automatically, and as a result, your output compiled code
// (in the .asm.js file, if you emit with --separate-asm) will
// contain only the functions you provide.
LLVM 现在支持使用 WASI 将 C 直接编译为 wasm。不再需要 Emscripten。
如果不需要 libc,您可以直接使用 LLVM。例如,文件 foo.c
可以编译为:
clang --target=wasm32 --no-standard-libraries -Wl,--export-all -Wl,--no-entry -o foo.wasm foo.c
否则,WASI-libc 项目有一个独立的 libc 可以使用。
使用 LLVM 和 运行 在浏览器中将 C 编译为 WebAssembly 的完整过程是 available in this post。
2020 年更新
借助 Emscripten,您可以使用 -o my_file.wasm
这样你将只有 WASM 文件,如果你使用标志 STANDALONE_WASM
它将创建 .wasm
和一个可选的 .js
粘合代码(如果需要)。
我能否以某种方式创建一个 wasm 文件,它将按照 in MDN here 的描述自行运行(通过实例化对象并调用它们的函数)?
我能找到的所有指南 (such as this one on MDN) 都推荐使用 emscripten;然而,这也将包括 ~70kB "glue code"(带有 ~50kB 可选文件系统仿真),它具有额外的逻辑(如检测 node/browser 环境和自动获取等),并且可能还有一些其他仿真。
如果我不想要 "glue code" 并且只想直接创建 WASM(可能是从 C 代码,但也可能是其他代码)怎么办?现在可以吗?
你可以,而且随着时间的推移会变得更容易!
如果你想完全避免使用 C++,可以创建 WebAssembly 模块,例如 spec tests or the WebKit test suite.
即使没有 Emscripten,即使使用 C++ 也可以。 wasm-stat.us does it for e.g. the GCC torture tests. Check out its build output, or look at its source.
例如,它将为编译执行以下操作 / link / assemble:
# Get a .o file:
src/work/wasm-install/bin/clang src/work/gcc/gcc/testsuite/gcc.c-torture/execute/20020227-1.c -o src/work/torture-o/20020227-1.c.o --std=gnu89 -DSTACK_SIZE=1044480 -w -Wno-implicit-function-declaration --target=wasm32-unknown-unknown-wasm -c -O2 --sysroot=src/work/wasm-install/sysroot
# Link with libc:
src/work/wasm-install/bin/lld -flavor wasm -entry=main --allow-undefined-file=src/work/wasm-install/sysroot/lib/wasm.syms -o src/work/torture-lld-musl/20020510-1.c.o.wasm src/work/torture-o/20020510-1.c.o src/work/wasm-install/sysroot/lib/libc.a
# Or without a libc (you need to provide one somehow):
src/work/wasm-install/bin/lld -flavor wasm -entry=main --allow-undefined-file=src/work/wasm-install/sysroot/lib/wasm.syms -o src/work/torture-lld/20020510-1.c.o.wasm src/work/torture-o/20020510-1.c.o
# Or, if you want an assembly file instead:
src/work/wasm-install/bin/clang src/work/gcc/gcc/testsuite/gcc.c-torture/execute/20020227-1.c -o src/work/torture-s/20020227-1.c.s --std=gnu89 -DSTACK_SIZE=1044480 -w -Wno-implicit-function-declaration --target=wasm32-unknown-unknown -S -O2 --sysroot=src/work/wasm-install/sysroot
# And get the binary file:
src/work/wasm-install/bin/wast2wasm src/work/torture-s2wasm/loop-6.c.s.wast -o src/work/torture-wast2wasm/loop-6.c.s.wast.wasm
您甚至可以下载瀑布的所有构建工件,包括完整的工具链。只需单击绿色框并找到您要查找的下载。
如果您胆子大,您甚至可以 write your libc in JavaScript 而不是 link 使用 C 语言编写的现有实现。
当你说 "on its own" 时,请记住 WebAssembly 目前不能在没有 link 到它的嵌入器(即 JavaScript)的情况下做任何事情。 Emscripten 遵循的模型(我希望其他人也这样做)是 JavaScript 是微内核并提供系统调用。
您可以使用 emscripten 生成相当少的代码输出。
考虑以下简单文件 adder.c
:
int adder (int a, int b) {
return a + b;
}
像这样编译它(需要相当新的 emscripten):
emcc -O2 -s WASM=1 -s SIDE_MODULE=1 -o adder.wasm
要查看它生成的内容,请使用 binaryen 的 wasm-dis
将其反汇编为 wast 文本形式(您也可以使用 wabt 的 wasm2wast):
wasm-dis adder.wasm -o adder.wast
反汇编后的源代码应该是这样的:
(module
(type [=13=] (func (param i32 i32) (result i32)))
(type (func))
(import "env" "memoryBase" (global $import[=13=] i32))
(import "env" "memory" (memory [=13=] 256))
(import "env" "table" (table 0 anyfunc))
(import "env" "tableBase" (global $import i32))
(global $global[=13=] (mut i32) (i32.const 0))
(global $global (mut i32) (i32.const 0))
(export "__post_instantiate" (func ))
(export "runPostSets" (func ))
(export "_adder" (func [=13=]))
(func [=13=] (type [=13=]) (param $var[=13=] i32) (param $var i32) (result i32)
(i32.add
(get_local $var)
(get_local $var[=13=])
)
)
(func (type )
(nop)
)
(func (type )
(block $label[=13=]
(set_global $global[=13=]
(get_global $import[=13=])
)
(set_global $global
(i32.add
(get_global $global[=13=])
(i32.const 5242880)
)
)
(call )
)
)
;; custom section "dylink", size 5
)
然后您可以在节点(v8.X 或更高版本)中 运行 像这样:
const WA = WebAssembly,
env = {memoryBase: 0,
tableBase: 0,
memory: new WA.Memory({initial: 256}),
table: new WA.Table({initial: 0, element: 'anyfunc'})},
code = new Uint8Array(require('fs').readFileSync('adder.wasm'))
WA.compile(code).then(m => {
return new WA.Instance(m, {env: env})
}).then(i => {
console.log(i.exports._adder(7, 8))
})
请注意,如果您想支持使用堆栈的代码 and/or 堆内存,事情会变得更加复杂。 IE。在调用任何其他导出之前,您至少需要设置 memoryBase 并从主机环境调用 __post_instantiate
。
如果你想在没有 JavaScript 环境的情况下解释 WebAssembly 代码,你可以 运行 使用 wac/wace (完全公开:我创建了这个项目)。请注意,wace
假定您定义了“_main”或 "main" 函数。
您可以使用 ONLY_MY_CODE 标志,这将只生成没有 glue.js 的 wasm 模块,例如
emcc -O1 ./src/foo.cpp -o release/foo.wasm -s WASM=1 -s ONLY_MY_CODE=1
来自 Settings.js https://github.com/kripken/emscripten/blob/master/src/settings.js#L583 :
var ONLY_MY_CODE = 0; // This disables linking and other causes of adding extra code
// automatically, and as a result, your output compiled code
// (in the .asm.js file, if you emit with --separate-asm) will
// contain only the functions you provide.
LLVM 现在支持使用 WASI 将 C 直接编译为 wasm。不再需要 Emscripten。
如果不需要 libc,您可以直接使用 LLVM。例如,文件 foo.c
可以编译为:
clang --target=wasm32 --no-standard-libraries -Wl,--export-all -Wl,--no-entry -o foo.wasm foo.c
否则,WASI-libc 项目有一个独立的 libc 可以使用。
使用 LLVM 和 运行 在浏览器中将 C 编译为 WebAssembly 的完整过程是 available in this post。
2020 年更新
借助 Emscripten,您可以使用 -o my_file.wasm
这样你将只有 WASM 文件,如果你使用标志 STANDALONE_WASM
它将创建 .wasm
和一个可选的 .js
粘合代码(如果需要)。