2025 年 4 月 4 日,从解决水排序谜题的程序开始,类似英国方块求解器,添加 SDL2 用户界面后可在桌面运行,妻子希望在手机上玩,于是需将其重写为 JavaScript 或使用 WebAssembly(WASM),最终成功,游戏及 u-config 克隆都能在浏览器中运行且二进制文件小。
- WASM 介绍:是定义抽象栈机及相关格式的规范,有 i32、i64、f32、f64 四种类型,线性可寻址内存从 0 开始,无对齐限制,有 32 位和 64 位版本,适合 64 位主机小指针,浏览器中它类似 JavaScript 之于 Java。
相关组件:
- WASM 模块:编译链接的图像,包含代码等部分,出口表列出入口点,可选 start 部分指示初始化函数,只能通过导入函数影响外部世界。
- WASM 运行时:加载模块,链接导入表,类型检查,执行 start 函数及入口点。
- WASM 编译器:将高级语言编译为低级 WASM,需应用二进制接口(ABI),编译时函数索引未知需链接器修补。
- WASM 链接器:确定 WASM 模块形状并链接编译器生成的函数,LLVM 的 wasm-ld 与 Clang 配合。
- 语言运行时:高级语言通常有标准库,需映射到标准化的导入集(如 WASI),本文直接使用 raw WASI 避免语言运行时。
示例程序:
- 简单 C 函数编译为 WASM:
float norm(float x, float y)
,Clang 将float
映射为f32
,生成$norm
函数。 - 带有导入和导出的 C 函数:
void f(int *);
和void example(int x)
,展示import_name
和export_name
属性的作用及 Clang ABI 中指针的映射等。 - 处理 null 指针的函数:
int get(int *p)
,使用-fsanitize=undefined -fsanitize-trap
检测 null 指针,运行时陷阱处理。 - 包含
memset
和memmove
的函数:void clear(void *buf, long len)
,使用memory.fill
指令。 - 处理结构:结构通过地址传递,返回结构时需在 callee 的地址空间分配空间。
- 简单 C 函数编译为 WASM:
- 水排序游戏:游戏仅导出三个函数
game_init
、game_render
、game_update
,使用 IMGUI 风格渲染,在 SDL 和 web 版本中表现相同,先开发 SDL 版本对提高开发效率很关键,游戏有 10000 个种子按难度排序。 WASM 系统接口(WASI):
- Hello World 程序:导出传统
_start
入口点,使用fd_write
函数输出,处理返回的errno
值,还可通过proc_exit
退出程序,获取命令行参数需调用args_sizes_get
和args_get
,打开文件更复杂,需处理预打开等。 - u-config WASM 端口:可下载
pkg-config.wasm
模块在 WASI 兼容的运行时中运行,通过-mount
选项映射文件系统路径。
- Hello World 程序:导出传统
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。