Skip to content

WASM 编译管线 — 概念

编译管线总览

Clang 编译阶段

目标三元组

编译使用 --target=wasm32-unknown-unknown,这意味着:

  • wasm32 — 生成 32 位 WebAssembly 代码
  • unknown-unknown — 不指定具体操作系统和供应商,产生独立的 WASM 模块
  • 不依赖 WASI(WebAssembly System Interface),所有系统调用由 JavaScript 侧模拟

关键编译标志

标志作用
-DMODERN=1启用"现代"代码路径,绕过原始 GBA 硬件依赖
-DWASM=1启用 WASM 特定代码分支,替代原生 ARM 汇编
-O2启用二级优化,平衡编译速度和运行性能
-fno-builtin禁用内置函数替换,避免与 GBA 自定义实现冲突
-ffreestanding声明为独立环境(无标准库),因为 WASM 模块不链接 libc

为什么需要 -DMODERN=1 和 -DWASM=1

原始 Pokemon Emerald 代码包含大量直接操作 GBA 硬件的代码,例如:

  • ARM 汇编内联函数(如 Log、除法运算)
  • 直接读写 I/O 寄存器地址
  • 硬件中断处理

定义 MODERN=1WASM=1 后,条件编译会:

  1. 用纯 C 实现替代 ARM 汇编内联
  2. 将硬件寄存器访问重定向到 WASM 线性内存地址
  3. 禁用硬件中断,改用轮询方式

wasm-ld 链接阶段

wasm-ld 是 LLVM 工具链中的 WebAssembly 链接器,负责将多个目标文件合并为单一 .wasm 模块。

关键链接选项

选项详细说明
--no-entryWebAssembly 模块通常需要 _startmain 入口。本项目由 JS 侧调用导出函数,不需要入口点
--allow-undefined游戏代码引用了 GBA BIOS 函数(如 LZ77UnCompVram),这些函数在 WASM 中不存在,由 JS 模拟实现
--initial-memory=268435456256MB,远超 GBA 原始 32KB WRAM + 256KB 外部 RAM,为资源数据提供充足空间
--export=AgbMain导出游戏初始化入口,JS 在加载 WASM 后首先调用此函数
--export=WasmRunFrame导出逐帧执行函数,JS 的 requestAnimationFrame 循环每帧调用
--export-all将所有全局符号导出,方便调试和直接访问内部函数

链接后的符号解析

目标文件编译流程

每个 .c 源文件独立编译为 .wasm.o 目标文件:

相关概念