龙空技术网

5分钟看懂的WebAssembly入门指南

阿里开发者 659

前言:

而今咱们对“c语言 web”都比较着重,各位老铁们都想要了解一些“c语言 web”的相关资讯。那么小编也在网上搜集了一些对于“c语言 web””的相关文章,希望你们能喜欢,看官们快快来学习一下吧!

点击链接阅读原文,获取更多技术内容:WebAssembly 入门-阿里云开发者社区

本文是一篇WebAssembly的入门文章,从理论介绍到实战方面有全面的讲述。

作者 | 子肃

来源 | 阿里开发者公众号

历史进程

由于 javascript 的动态类型特性,AOT 并不能为它做出优化,只能选择 JIT 来优化。

而为了让 JIT 效率提高,Mozilla 推出了 asm.js。它和 Typescript 比较相似的是它也是一个强类型语言,不过它的语法是 js 的子集,它专为 JIT 效率提高而打造。

在 Mozilla 推出 asm.js 之后,一些公司都觉得这个思路不错,于是联合起来推出了 WebAssembly。

WebAssembly 是什么

WebAssembly 是一套指令集(字节码)标准,不过它并不是可以被 CPU 直接执行的原生指令集,所以它目前还需要配套一个虚拟机(low-level)来执行。

工具链

目前比较成熟的有

1、(基于 LLVM)

2、(Rust)

我们这里拿 emscripten 举例,它可以让任何使用 LLVM 作为编译器后端的语言都可以编译到 wasm。

这是怎么做到的呢?我们先了解一下 LLVM。

LLVM

Low-Level-Virtual-Machine

它是一个编译器,不过它不直接将语言编译到机器码,而是先用每个语言的前端编译器编译到 IR(intermediate representation),然后再利用后端编译器编译到目标机器码。这样设计的好处是在需要支持一个新架构时,只需要添加一个后端编译器就可以了。

而 WebAssembly 就是这么多编译目标中的一个,所以任何基于 LLVM 的语言都可以编译到 WebAssembly。

既然这样,我们可以试试直接采用 LLVM 来编译 wasm,体验一下原汁原味的过程。

实战

安装 LLVM

brew install llvmbrew link --force llvmllc --version# 确保 wasm32 在 targets 中

编译 C

假设我们有 C 文件 add.c

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,大概长下面这个样子

; 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 optnonedefine 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 wabtwasm-objdump -x add.o

大概长这样

add.o:  file format wasm 0x1Section Details:Type[1]: - type[0] (i32, i32) -> i32Import[2]: - memory[0] pages: initial=0 <- env.__linear_memory - global[0] i32 mutable=1 <- env.__stack_pointerFunction[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 入门-阿里云开发者社区

阿里云开发者社区,千万开发者的选择。百万精品技术内容、千节免费系统课程、丰富的体验场景、活跃的社群活动、行业专家分享交流,尽在:阿里云开发者社区-云计算社区-阿里云

标签: #c语言 web