mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4
1640 字
5 分钟
WebAssembly 编译
2026-03-04

WebAssembly(WASM)是继 JavaScript 之后 Web 平台的第二个原生语言。但 WASM 的野心远不止浏览器——它正在成为通用编译目标,让任何语言都能编译到任何平台运行。

一、WASM 设计哲学#

1.1 为什么需要 WASM#

graph TB subgraph "Web 性能困境" JS["JavaScript<br/>动态类型<br/>解释执行<br/>GC 开销"] JS_PERF["性能瓶颈:<br/>数值计算慢<br/>游戏渲染卡<br/>视频处理差"] end subgraph "WASM 解决方案" WASM["WebAssembly<br/>静态类型<br/>接近原生执行<br/>无 GC(可选)"] WASM_PERF["性能:<br/>接近原生速度<br/>安全沙箱<br/>跨平台"] end style JS_PERF fill:#ffcdd2,stroke:#c62828 style WASM_PERF fill:#c8e6c9,stroke:#2e7d32
设计目标实现方式
高效执行线性内存 + 结构化控制流 + 近原生速度
安全沙箱无直接内存访问 + 间接调用表 + 类型验证
可移植二进制格式 + 规范化语义 + 多运行时
可嵌入与 JS 互操作 + 独立运行 + 多语言宿主
开放标准W3C 标准 + 多厂商参与 + 多实现

二、WASM 指令集#

2.1 栈机器模型#

WASM 使用栈机器模型:指令从操作数栈取值,结果压回栈。

;; WASM 文本格式(WAT)
;; 计算 (a + b) * c
(module
(func $add_mul (param $a i32) (param $b i32) (param $c i32) (result i32)
local.get $a ;; 将 $a 压栈
local.get $b ;; 将 $b 压栈
i32.add ;; 弹出 a, b → 压入 a+b
local.get $c ;; 将 $c 压栈
i32.mul ;; 弹出 (a+b), c → 压入 (a+b)*c
)
)

2.2 值类型与指令#

类型大小说明
i3232 位整数
i6464 位长整数
f3232 位浮点
f6464 位双精度
funcref函数引用
externref外部引用
指令类别示例说明
算术i32.add, i32.mul, f64.sqrt基本运算
比较i32.eq, i32.lt_s, f64.ge条件判断
转换i32.wrap_i64, f64.convert_i32_s类型转换
内存i32.load, i32.store, memory.size内存操作
控制block, loop, if, br, return结构化控制流
调用call, call_indirect函数调用

2.3 WASM 二进制格式#

WASM 二进制格式:
┌──────────────────┐
│ Magic: 0x00 0x61 0x73 0x6D │ "\0asm"
│ Version: 0x01 0x00 0x00 0x00 │ 版本 1
├──────────────────┤
│ Type Section │ 函数类型签名
│ Function Section │ 函数索引
│ Memory Section │ 内存声明
│ Export Section │ 导出函数
│ Code Section │ 函数体字节码
└──────────────────┘
编码特点:
- LEB128 变长整数:小数值用更少字节
- Section ID + Size + Content:自描述格式
- 验证在加载时完成:运行时无需再验证

三、从 Rust/C 到 WASM#

3.1 Rust → WASM#

# 安装 wasm-pack
cargo install wasm-pack
# 创建项目
cargo new --lib wasm-demo
# 编译为 WASM
wasm-pack build --target web
src/lib.rs
use wasm_bindgen::prelude::*;
// 导出给 JavaScript 调用
#[wasm_bindgen]
pub fn fibonacci(n: u32) -> u32 {
if n <= 1 { return n; }
fibonacci(n - 1) + fibonacci(n - 2)
}
// 导出结构体
#[wasm_bindgen]
pub struct Calculator {
result: f64,
}
#[wasm_bindgen]
impl Calculator {
#[wasm_bindgen(constructor)]
pub fn new() -> Self {
Calculator { result: 0.0 }
}
pub fn add(&mut self, value: f64) {
self.result += value;
}
pub fn get_result(&self) -> f64 {
self.result
}
}
// 从 JavaScript 导入
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = console)]
fn log(s: &str);
}
#[wasm_bindgen]
pub fn greet(name: &str) {
log(&format!("Hello, {}!", name));
}

3.2 JavaScript 互操作#

// 使用 WASM 模块
import init, { fibonacci, Calculator } from './wasm_demo.js';
async function run() {
await init();
// 调用 WASM 函数
console.log(fibonacci(10)); // 55
// 使用 WASM 对象
const calc = new Calculator();
calc.add(1.5);
calc.add(2.5);
console.log(calc.get_result()); // 4.0
calc.free(); // 释放 WASM 内存
}
run();

3.3 C/C++ → WASM#

# 使用 Emscripten 编译 C 到 WASM
emcc hello.c -o hello.html \
-s WASM=1 \
-s EXPORTED_FUNCTIONS='["_main","_add"]' \
-s EXPORTED_RUNTIME_METHODS='["ccall","cwrap"]'
# C 代码
#include <stdio.h>
int add(int a, int b) {
return a + b;
}
int main() {
printf("Hello from WASM!\n");
return 0;
}
// JavaScript 调用 C 函数
const add = Module.cwrap('add', 'number', ['number', 'number']);
console.log(add(1, 2)); // 3

四、Cranelift 代码生成#

4.1 Cranelift 架构#

Cranelift 是 WASM-native 的代码生成后端,也用于 Wasmtime 运行时:

graph TB WASM["WASM 字节码"] --> CLIF["CLIF IR<br/>Cranelift IR"] CLIF --> OPT["优化 Pass<br/>寄存器分配<br/>指令选择"] OPT --> MCODE["机器码<br/>x86/ARM/RISC-V"] style CLIF fill:#e3f2fd,stroke:#1565c0
特性CraneliftLLVM
编译速度极快(~10ms)慢(~1s)
代码质量中等
适用场景JIT、运行时编译AOT、静态编译
语言RustC++
目标架构x86, ARM, RISC-V所有主流架构
Note

Cranelift 的设计目标是编译速度优先——它牺牲了一些代码质量换取极快的编译速度。这对于 JIT 场景(如 Wasmtime 运行时)直接决定用户体验:用户等待 WASM 模块加载的时间应该尽可能短。

五、WASI(WebAssembly System Interface)#

5.1 WASI 标准#

WASI 让 WASM 程序可以安全地访问系统资源:

// WASI 示例:文件操作
use std::fs::File;
use std::io::Write;
fn main() {
// WASI 提供文件系统访问
let mut file = File::create("output.txt").unwrap();
file.write_all(b"Hello from WASM!").unwrap();
// WASI 提供环境变量
let home = std::env::var("HOME").unwrap_or("/tmp".to_string());
println!("Home: {}", home);
// WASI 提供时钟
let now = std::time::SystemTime::now();
println!("Current time: {:?}", now);
}
# 编译为 WASI 目标
rustup target add wasm32-wasi
cargo build --target wasm32-wasi
# 使用 Wasmtime 运行
wasmtime run target/wasm32-wasi/debug/demo.wasm

5.2 WASI 功能集#

功能集描述状态
wasi_snapshot_preview1基础文件/时钟/随机稳定
wasi-unstable完整系统接口开发中
wasi-httpHTTP 客户端/服务器开发中
wasi-nn神经网络推理开发中
wasi-crypto加密操作开发中

六、WASM 运行时#

6.1 运行时对比#

运行时语言场景特点
V8C++浏览器最快 JIT,Liftoff + TurboFan
WasmtimeRust通用Cranelift JIT,WASI 支持
WasmerRust通用多后端(LLVM/Cranelift/Singlepass)
WasmEdgeC++Edge轻量,WASI + 网络
wasm3C嵌入式解释执行,极小体积

6.2 WASM Component Model#

graph TB subgraph "Component Model" COMP1["Component 1<br/>Rust → WASM<br/>接口: calc.add"] COMP2["Component 2<br/>Python → WASM<br/>接口: data.process"] COMP3["Component 3<br/>JS → WASM<br/>接口: ui.render"] HOST["宿主环境<br/>Wasmtime/Wasmer"] COMP1 --> HOST COMP2 --> HOST COMP3 --> HOST COMP1 -.->|"接口调用"| COMP2 COMP2 -.->|"接口调用"| COMP3 end style HOST fill:#fff9c4,stroke:#f9a825

Component Model 是 WASM 的未来方向——不同语言编译的 WASM 模块可以通过标准接口互操作,就像微服务架构中的服务间通信。

七、WASM 在后端的应用#

7.1 Serverless Edge Computing#

# WASM 作为 Serverless 运行时
# Fermyon Spin / Cloudflare Workers
spin.toml:
[[component]]
id = "hello"
source = "hello.wasm"
trigger = { http = { path = "/hello" } }
[component.dependencies]
wasi-http = "0.1"

7.2 WASM + eBPF#

Wasm-eBPF 混合架构:
1. eBPF 程序在内核中运行(高性能)
2. WASM 程序在用户态运行(安全沙箱)
3. WASM 通过共享 Map 与 eBPF 交互
优势:
- WASM 提供安全的用户态处理
- eBPF 提供内核级高性能采集
- 跨平台:WASM 可以在任何运行时执行

七-B、WASM 指令集深入#

7B.1 WASM 指令编码#

WASM 二进制格式使用 LEB128 变长编码,指令设计紧凑:

;; WASM 指令编码示例
;; i32.add 的操作码是 0x6A
;; local.get 0 的编码是 0x20 0x00
;; 函数: (param i32) (result i32) -> local.get 0; i32.const 1; i32.add
;; 二进制编码:
;; 0x20 0x00 ;; local.get 0
;; 0x41 0x01 ;; i32.const 1 (LEB128 编码)
;; 0x6A ;; i32.add
;; 0x0B ;; end
指令类别操作码范围指令数说明
控制流0x00-0x11~12block, loop, if, br, return
参数0x1A-0x1B2drop, select
变量0x20-0x245local.get/set/tee, global.get/set
内存0x28-0x3E~30load, store (各种宽度)
数值0x41-0xC4~120常量、算术、比较、转换
SIMD0xFD prefix~180v128 操作 (WASM SIMD)
原子0xFE prefix~30wait, notify, rmw (线程)

7B.2 Cranelift 代码生成深入#

Cranelift 是专为 JIT 场景设计的代码生成器,其核心 IR 称为 CLIF:

;; CLIF IR 示例
;; 函数: i32 add(i32, i32)
function %add(i32, i32) -> i32 {
block0(v0: i32, v1: i32):
v2 = iadd v0, v1
return v2
}

Cranelift 的编译流水线:

flowchart TB INPUT["WASM 字节码<br/>或 CLIF IR"] --> VERIFY["验证<br/>类型检查+合法性"] VERIFY --> DOM["支配树计算<br/>基本块排序"] DOM --> PREOPT["预优化<br/>常量折叠+死代码"] PREOPT --> LICM["循环不变量外提"] LICM --> RA["寄存器分配<br/>线性扫描"] RA -> ISEL["指令选择<br/>模式匹配"] ISEL -> PROLOG["序言/结语插入"] PROLOG -> EMIT["二进制发射<br/>可执行内存"] style INPUT fill:#e3f2fd,stroke:#1565c0 style EMIT fill:#e8f5e9,stroke:#2e7d32
Cranelift 特性说明与 LLVM 对比
编译速度~10ms/千行 IRLLVM ~1s/千行 IR
代码质量中等(-10-20% vs LLVM)LLVM 高
寄存器分配线性扫描LLVM 图着色
指令选择简单模式匹配LLVM DAG 覆盖
目标架构x86-64, AArch64, RISC-VLLVM 所有主流
语言RustC++

7B.3 WASI 接口深入#

WASI(WebAssembly System Interface)定义了 WASM 访问系统资源的安全接口:

// WASI 的能力安全模型
// WASM 模块只能访问被显式授权的资源
use std::fs::File;
use std::io::Read;
fn read_config() -> String {
// 这段代码编译为 WASM 后,
// 只有在运行时被授予了文件读取权限才能成功
let mut file = File::open("/data/config.json").unwrap();
let mut content = String::new();
file.read_to_string(&mut content).unwrap();
content
}
# Wasmtime 运行时权限控制
wasmtime run --dir=/data::/data \ # 授予 /data 目录的只读访问
--env=API_KEY=xxx \ # 传入环境变量
--allow-network \ # 授予网络访问
app.wasm
WASI 功能能力安全模型
文件系统读写文件基于预打开目录的能力
网络TCP/UDP需要显式授权
时钟系统时间精度可限制
随机数安全随机默认允许
环境变量读取环境需要显式授权
命令行参数读取参数默认允许

7B.4 WASM 运行时详细对比#

运行时编译策略启动时间峰值性能内存模型WASI适用场景
V8 (Liftoff)基线 JIT~1ms线性+GC有限浏览器
V8 (TurboFan)优化 JIT~50ms线性+GC有限浏览器热点
WasmtimeCranelift JIT~5ms中高线性完整服务端
Wasmer (LLVM)LLVM AOT~100ms线性完整高性能
Wasmer (Cranelift)Cranelift JIT~5ms中高线性完整通用
WasmEdgeAOT/JIT~2ms中高线性扩展Edge
wasm3解释~0.1ms线性基本嵌入式
Tip

选择 WASM 运行时的关键因素:1) 启动延迟——Serverless 场景需要 <5ms;2) 峰值性能——计算密集型需要 AOT/JIT;3) WASI 支持——服务端需要完整文件/网络访问;4) 内存开销——嵌入式场景需要 <10MB。没有最好的运行时,只有最适合场景的运行时。

七-C、Rust/C 到 WASM 的完整工作流#

7C.1 Rust → WASM 工具链#

# 完整的 Rust → WASM 开发工作流
# 1. 安装工具链
rustup target add wasm32-unknown-unknown # 浏览器目标
rustup target add wasm32-wasi # WASI 目标
cargo install wasm-pack # 打包工具
cargo install wasm-bindgen-cli # JS 绑定生成
# 2. 创建项目
cargo new --lib wasm-demo
cd wasm-demo
# 3. 添加依赖
cat >> Cargo.toml << 'EOF'
[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "0.2"
EOF
# 4. 编译
wasm-pack build --target web # 浏览器
cargo build --target wasm32-wasi # WASI
# 5. 优化 WASM 二进制
wasm-opt -O3 pkg/wasm_demo_bg.wasm -o pkg/wasm_demo_opt.wasm
# 6. 分析 WASM 大小
wasm-size pkg/wasm_demo_bg.wasm
wasm-objdump -h pkg/wasm_demo_bg.wasm

7C.2 C → WASM 工作流#

# Emscripten 完整工作流
# 1. 安装 Emscripten
git clone https://github.com/emscripten-core/emsdk
cd emsdk && ./emsdk install latest && ./emsdk activate latest
# 2. 编译 C 到 WASM
emcc hello.c -o hello.html -s WASM=1 -s MODULARIZE=1
# 3. 编译为 WASI 目标(使用 Clang)
clang --target=wasm32-wasi --sysroot=/path/to/wasi-sysroot hello.c -o hello.wasm
# 4. 运行
wasmtime run hello.wasm
工具功能适用场景
wasm-packRust WASM 打包+发布浏览器库
wasm-bindgenRust/JS 互操作浏览器应用
wasm-optWASM 二进制优化所有场景
wasm2watWASM → WAT 文本调试
wasm-objdump查看 WASM 结构分析
wasmtimeWASM 运行时服务端

八、总结#

上一章中,Rust 的借用检查器在编译期保证了内存安全,rustc 最终调用 LLVM 后端生成原生机器码。但编译目标不只有 x86 和 ARM——Web 也是一个平台。WebAssembly 让 C、C++、Rust 等语言能编译到浏览器中运行,它正在成为继 JavaScript 之后 Web 平台的第二个原生语言。

维度WASMNativeJVM
安全性沙箱沙箱
性能~90% native100%~70% native
启动速度~1ms~100ms~500ms
内存占用
可移植性极高
GC可选必须
适用场景Edge/嵌入式/插件高性能企业应用
Tip

WASM 的定位不是替代 Native——而是在安全+可移植的场景下提供接近 Native 的性能。Serverless Edge、嵌入式插件、跨平台容器是 WASM 最有前途的应用方向。

支持与分享

如果这篇文章对你有帮助,欢迎支持作者或分享给更多人

WebAssembly 编译
https://blog.souloss.com/posts/compiler/wasm-compilation/
作者
Souloss
发布于
2026-03-04
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时