欢迎大家来到IT世界,在知识的湖畔探索吧!
点击链接阅读原文,获取更多技术内容:WebAssembly 入门-阿里云开发者社区
本文是一篇WebAssembly的入门文章,从理论介绍到实战方面有全面的讲述。
作者 | 子肃
来源 | 阿里开发者公众号
历史进程
由于 javascript 的动态类型特性,AOT 并不能为它做出优化,只能选择 JIT 来优化。
而为了让 JIT 效率提高,Mozilla 推出了 asm.js。它和 Typescript 比较相似的是它也是一个强类型语言,不过它的语法是 js 的子集,它专为 JIT 效率提高而打造。
在 Mozilla 推出 asm.js 之后,一些公司都觉得这个思路不错,于是联合起来推出了 WebAssembly。
WebAssembly 是什么
WebAssembly 是一套指令集(字节码)标准,不过它并不是可以被 CPU 直接执行的原生指令集,所以它目前还需要配套一个虚拟机(low-level)来执行。
工具链
目前比较成熟的有
1、https://emscripten.org/(基于 LLVM)
2、https://github.com/rustwasm/wasm-bindgen(Rust)
我们这里拿 emscripten 举例,它可以让任何使用 LLVM 作为编译器后端的语言都可以编译到 wasm。
这是怎么做到的呢?我们先了解一下 LLVM。
LLVM
Low-Level-Virtual-Machine
它是一个编译器,不过它不直接将语言编译到机器码,而是先用每个语言的前端编译器编译到 IR(intermediate representation),然后再利用后端编译器编译到目标机器码。这样设计的好处是在需要支持一个新架构时,只需要添加一个后端编译器就可以了。
而 WebAssembly 就是这么多编译目标中的一个,所以任何基于 LLVM 的语言都可以编译到 WebAssembly。
既然这样,我们可以试试直接采用 LLVM 来编译 wasm,体验一下原汁原味的过程。
实战
安装 LLVM
brew install llvm brew link --force llvm llc --version # 确保 wasm32 在 targets 中
欢迎大家来到IT世界,在知识的湖畔探索吧!
编译 C
假设我们有 C 文件 add.c
欢迎大家来到IT世界,在知识的湖畔探索吧!int add(int a, int b) { return a * a + b; }
第一步:我们先采用 clang 将 C 文件编译到 LLVM IR
clang \ --target=wasm32 \ -emit-llvm # 生成 LLVM IR, 而不是直接生成机器码 -c \ # 仅编译(no linking) -S \ # 生成可读的 wat 格式, 而不是二进制格式 add.c
我们会得到一个 add.ll 文件,这就是 LLVM IR,大概长下面这个样子
欢迎大家来到IT世界,在知识的湖畔探索吧!; ModuleID = 'add.c' source_filename = "add.c" target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20" target triple = "wasm32" ; Function Attrs: noinline nounwind optnone define hidden i32 @add(i32 noundef %0, i32 noundef %1) #0 { %3 = alloca i32, align 4 %4 = alloca i32, align 4 store i32 %0, ptr %3, align 4 store i32 %1, ptr %4, align 4 %5 = load i32, ptr %3, align 4 %6 = load i32, ptr %3, align 4 %7 = mul nsw i32 %5, %6 %8 = load i32, ptr %4, align 4 %9 = add nsw i32 %7, %8 ret i32 %9 } attributes #0 = { noinline nounwind optnone "frame-pointer"="none" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic" } !llvm.module.flags = !{!0} !llvm.ident = !{!1} !0 = !{i32 1, !"wchar_size", i32 4} !1 = !{!"Homebrew clang version 15.0.7"}
第二步:将 LLVM IR 编译到 object 文件
llc -march=wasm32 -filetype=obj add.ll
我们会得到一个 add.o,它是一个含有这个 C 文件所有编译代码的 wasm 模块,不过现在还不能够运行它。这个模块中其实是一个可以被阅读的格式,我们可以用一些工具来解析它,比如 WebAssembly Binary Toolkit(wabt)
brew install wabt wasm-objdump -x add.o
大概长这样
add.o: file format wasm 0x1 Section Details: Type[1]: - type[0] (i32, i32) -> i32 Import[2]: - memory[0] pages: initial=0 <- env.__linear_memory - global[0] i32 mutable=1 <- env.__stack_pointer Function[1]: - func[0] sig=0 <add> Code[1]: - func[0] size=44 <add> Custom: - name: "linking" - symbol table [count=2] - 0: F <add> func=0 [ binding=global vis=hidden ] - 1: G <env.__stack_pointer> global=0 [ undefined binding=global vis=default ] Custom: - name: "reloc.CODE" - relocations for section: 3 (Code) [1] - R_WASM_GLOBAL_INDEX_LEB offset=0x000006(file=0x00005e) symbol=1 <env.__stack_pointer> Custom: - name: "producers"
这里定义了 add 方法,不过除此以外还包含了很多其他信息,比如 imports,这其实是要被下一个环节(linking)所消费的
第三步:Linking
一般来说,连接器的作用是将多个 object 文件连接成一个可执行的文件。
LLVM 中的连接器叫做 lld,根据编译目标不同存在多个 lld,我们这里要用的是 wasm-ld
wasm-ld \ --no-entry \ # add.c 并没有一个入口文件, 它是一个 lib --export-all \ # 导出所有方法 -o add.wasm \ add.o
这里我们会得到最终的 wasm 产物,add.wasm
剩余60%,完整内容请点击下方链接查看:WebAssembly 入门-阿里云开发者社区
阿里云开发者社区,千万开发者的选择。百万精品技术内容、千节免费系统课程、丰富的体验场景、活跃的社群活动、行业专家分享交流,尽在:阿里云开发者社区-云计算社区-阿里云
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://itzsg.com/57749.html