介绍
将C汇编为WASM的流行工具链是emscripten
。它为标准C库和其他接口(例如SDL2)提供绑定,并允许将C代码编译为WASM并在浏览器中运行。
不幸的是,emscripten
很复杂。它在HTML和JavaScript文件中生成了许多JavaScript代码样板。
如果您只需要编译不使用标准C库或其他接口的C代码,则可以使用更简单的工具链-clang
与llc
。
下面的示例演示了如何将C代码编译到WASM,在浏览器中运行并使用标准C类型(包括指示)与JavaScript进行通信。
先决条件
在Mac上,仅需要从brew
安装llvm
才能使clang
编译器生成WASM代码:
brew install llvm
然后,必须将llc
工具链的路径添加到PATH
环境中:
export PATH=/opt/homebrew/opt/llvm/bin:$PATH
之后,clang
编译器应支持--target=wasm32
目标:
llc --version | grep wasm
编译C到WASM
main.c
:
unsigned char mem[0x10000];
const char *const upper(char *const str, const int sz)
{
for (int i = 0; i < sz; i++)
if (str[i] >= 'a' && str[i] <= 'z')
str[i] -= 0x20;
return str;
}
将其编译到WASM:
clang \
--target=wasm32 \
--no-standard-libraries \
-Wl,--export-all -Wl,--no-entry \
-o main.wasm \
main.c
该命令创建一个文件main.wasm
,即二进制WASM模块。
这个微不足道的示例演示了简单的C到 - - 瓦斯接口的基本需求:
- 使用预先分配的静态内存缓冲区(例如,大小为64KB)
- 可以从JavaScript和通过原始类型(整数,指针等)调用C函数的能力
- 从C函数返回原始类型的能力
- 观察内存缓冲区从JavaScript变化的能力
标准C库未链接到WASM模块,因此C函数不应使用任何标准C函数。
有趣的是,函数upper()
的结果(是指指针)作为数字返回到JavaScript,而不是指针。该数字是javaScript侧的WebAssembly内存缓冲区开头的偏移。
c处理指针,但JavaScript将内存缓冲区视为平坦的数组,并使用偏移访问其元素。
实际上,WASM“直接内存访问”并不是它的含义。 WASM内存是一个常规的JavaScript对象,它是字节的平坦数组。它的生命周期由JavaScript垃圾收集器管理为任何其他JavaScript对象。
在浏览器中运行WASM
使用以下main.html
文件在浏览器中运行WASM模块:
<!DOCTYPE html>
<html>
<body>
<script type="module">
const log = console.log;
console.log = (...x) => {
document.body.innerHTML += x.join(" ") + "<br>";
log(...x);
};
const wa = await WebAssembly.instantiateStreaming(
fetch("main.wasm"),
{ js: { mem: new WebAssembly.Memory({ initial: 0 }) } }
);
const memPtr = wa.instance.exports.mem.value;
console.log("memPtr", memPtr);
const mem = new Uint8Array(wa.instance.exports.memory.buffer);
const str = "Hello, world!";
new TextEncoder().encodeInto(
str,
mem.subarray(memPtr, memPtr + str.length)
);
console.log(
"upper()",
wa.instance.exports.upper(memPtr, str.length)
);
console.log(
new TextDecoder().decode(
mem.subarray(memPtr, memPtr + str.length)
)
);
</script>
</body>
</html>
main.html
和main.wasm
文件需要由Web服务器提供。
例如,由Python的SimpleHttpserver:
python -m SimpleHTTPServer
或VSCODE的LIVE服务器扩展。
您应该在浏览器和DevTool控制台中看到类似的东西:
memPtr 1024
upper() 1024
HELLO, WORLD!
1024
实际上是“指针”。 1024
是WASM模块内存中mem
C数组的开始的偏移。
upper()
函数转换“你好,世界!”到上案,并将相同的“指针” 1024
返回到javascript。
就是这样。 WASM模块已编译并在浏览器中运行。