├── .gitignore ├── Contributing.md ├── MVP.md ├── HighLevelGoals.md ├── UseCases.md ├── Tooling.md ├── Portability.md ├── README.md ├── Polyfill.md ├── FeatureTest.md └── BinaryEncoding.md /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *# 3 | .#* 4 | out 5 | -------------------------------------------------------------------------------- /Contributing.md: -------------------------------------------------------------------------------- 1 | # 为WebAssembly作贡献 2 | 3 | WebAssembly最初是由对一些[使用案例](UseCases.md)有兴趣的浏览器厂商设计和实现的,在他们设计和实现的过程中,需要来自对WebAssembly感兴趣的开发者的投入和反馈。 4 | 5 | 对参与充满兴趣?我们建议您从如下做起: 6 | 7 | 1. 熟悉[合乎道德和专业行为的代码](CodeOfConduct.md)文档。 8 | 2. 阅读[WebAssembly的设计文档][]。 9 | 3. 参与到IRC频道中:`irc://irc.w3.org:6667/#webassembly`。 10 | 11 | 在背景知识和交流渠道建立起来之后,您可以对WebAssembly的设计仓库提交任何[issue][]。请在提交pull request之前加入[W3C社区组][]:它提供了法律体制来保护设计仓库中的工作,并请确认在设计组中您已关联了您的公司和组织(如果有的话)。 12 | 13 | 随着WebAssembly不断向前发展,我们希望能建立官方的标准机构,它对规范制定的贡献有自己的流程。 14 | 15 | 祝您汇编愉快! 16 | 17 | [WebAssembly的设计文档]: https://github.com/WebAssembly/design 18 | [issue]: https://github.com/WebAssembly/design/issues 19 | [W3C社区组]: https://www.w3.org/community/webassembly/ 20 | -------------------------------------------------------------------------------- /MVP.md: -------------------------------------------------------------------------------- 1 | # 最小可行性产品 2 | 3 | 正如在[高层设计目标](HighLevelGoals.md)中提到的,WebAssembly第一个发布版本的目标是实现最小可行性产品(MVP)。这意味着某些我们*已知的*功能需求将在MVP之后出现,对于这部分功能,我们单列了一个[post-MVP](PostMVP.md)功能的计划文档。MVP将包含现有web浏览器已经具有并且在移动设备上运行良好的功能,这些大致和[asm.js](http://asmjs.org)所具有的功能相同。 4 | 5 | MVP主要部件的设计被拆分为如下几个文档: 6 | 7 | * 在WebAssembly中,可分发、加载和执行的代码单元被称为[模块](Modules.md)。 8 | * 一个模块中WebAssembly代码的行为,按照其[抽象语法树](AstSemantics.md)进行规范。 9 | * 被设计为能被WebAssembly原生地解码,WebAssembly的二进制格式,被规范化为模块的抽象语法树的[二进制序列化](BinaryEncoding.md)。 10 | * 能够使用工具(如汇编器,调试器,分析器)进行读和写操作的WebAssembly文本格式,被规范化为模块抽象语法树的[文本化映射](TextFormat.md)。 11 | * 在设计上,WebAssembly将在[Web浏览器](Web.md)和[非浏览器执行环境](NonWeb.md)中同时实现。 12 | * 为了简化迁移到WebAssembly的同时保持对现有平台的支持,WebAssembly的MVP版本还将实现一个高效的[polyfill库](Polyfill.md)。 13 | 14 | -------------------------------------------------------------------------------- /HighLevelGoals.md: -------------------------------------------------------------------------------- 1 | # WebAssembly高层设计目标 2 | 3 | 1. 定义一种[可移植](Portability.md), 占用体积小和加载速度快的[二进制格式](MVP.md)作为编译的目标格式,并利用现有平台(包括[移动设备](https://en.wikipedia.org/wiki/Mobile_device)和[IoT设备](https://en.wikipedia.org/wiki/Internet_of_Things))普遍具有的通用硬件能力,使其达到原生代码的执行速度。 4 | 2. 逐步规范和实现: 5 | * 一个大致与[asm.js](http://asmjs.org)具有相同功能的[最小可行性产品(MVP)](MVP.md),其主要的目标语言是[C/C++](CAndC++.md); 6 | * 针对MVP,实现一个将WebAssembly转换成JavaScript的高效[polyfill](Polyfill.md)库,以便于WebAssembly的MVP版本能在现有的浏览器中运行; 7 | * [MVP后续版本](PostMVP.md)将在MVP基础上增加一些必不可少的功能; 8 | * 在此基础上逐步细化,以及根据反馈和经验附加[新功能](FutureFeatures.md),包括支持除C/C++之外的其它语言。 9 | 3. WebAssembly能在*现有的*[Web平台](Web.md)执行,并与这些平台良好的集成: 10 | * 保持无需更新、[功能已测试](FeatureTest.md)和 11 | [向后兼容](BinaryEncoding.md)等Web发展的成果; 12 | * 与JavaScript在相同的语义领域下执行; 13 | * 允许异步地与JavaScript互调用; 14 | * 强制与浏览器同源并执行相同的权限安全策略; 15 | * 通过与JavaScript使用相同的Web API来访问浏览器功能;并定义了一种可以与二进制格式相互转换的易编辑文本格式以支持查看源代码功能。 16 | 4. 在设计上,WebAssembly同样支持[非浏览器嵌入](NonWeb.md)。 17 | 5. 构建一个优秀的平台: 18 | * 为WebAssembly构建一个新的LLVM后端,同时伴随一个clang的移植版本([为什么首先是LLVM?](FAQ.md#which-compilers-can-i-use-to-build-webassembly-programs)); 19 | * 鼓励发展其它针对于WebAseembly的编译器和工具; 并且 20 | * 实现其它实用[工具](Tooling.md). 21 | -------------------------------------------------------------------------------- /UseCases.md: -------------------------------------------------------------------------------- 1 | # 使用案例 2 | 3 | 在WebAssembly的[高层目标](HighLevelGoals.md)文档中定义了在[Web](Web.md)和[非Web](NonWeb.md)平台上,WebAssembly的目标是*什么*,以*什么顺序*以及*如何实现*这些目标。下面的内容是可能从WebAssembly中收益的应用/领域/计算的一个不完全和未排序的列表,同时在WebAssembly的设计阶段,它们也作为其使用的案例。 4 | 5 | ## 浏览器内 6 | 7 | * 已经可以交叉编译到Web的语言和工具集(C/C++,GWT......),可获得更好的执行效率 8 | * 图像和视频编辑 9 | * 游戏: 10 | - 需要快速启动的休闲游戏 11 | - 需要加载很多资源的[AAA游戏](https://zh.wikipedia.org/wiki/AAA_(%E7%94%B5%E5%AD%90%E6%B8%B8%E6%88%8F%E4%BA%A7%E4%B8%9A)) 12 | - 游戏门户(混合团体或自产内容) 13 | * P2P应用(游戏,协同编辑,去中心化和中心化) 14 | * 音乐应用(流式,缓冲) 15 | * 图像识别 16 | * 在线视频增强(例如,给头上加一顶帽子) 17 | * 虚拟现实和增强现实(非常低的延迟) 18 | * CAD应用 19 | * 科学可视化和仿真 20 | * 交互性教育软件和新闻文章 21 | * 平台模拟/仿真(ARC,DOSBox,QEMU,MAME等) 22 | * 编程语言解释器和虚拟机 23 | * 允许移植现有POSIX应用到Web的POSIX用户空间环境 24 | * 开发者工具(编辑器,编译器,调试器......) 25 | * 远程桌面 26 | * VPN 27 | * 加密 28 | * 本地web服务器 29 | * 在Web安全的模型和API下的普通[NPAPI](https://zh.wikipedia.org/wiki/NPAPI)使用者 30 | * 企业应用的胖客户端(例如数据库) 31 | 32 | # 浏览器外 33 | 34 | * 游戏分发服务(可移植和安全的) 35 | * 不可信代码在服务端的运行 36 | * 服务端应用 37 | * 在移动设备上的混合本地应用 38 | 39 | # WebAssembly的使用方式 40 | 41 | * 整个以Web Assembly作为代码基 42 | * 以Web Assembly为主要框架,但UI部分是JavaScript/HTML代码 43 | * 以Web Assembly为编译目标格式重用现有代码,嵌入到更大的JavaScript/HTML应用中;现有代码可以是简单的帮助库到计算密集型任务等任意代码 44 | -------------------------------------------------------------------------------- /Tooling.md: -------------------------------------------------------------------------------- 1 | # 支持工具 2 | 3 | 在浏览器内执行的工具,(与原生工具相比,)常常具有不相称的软件质量。而WebAssembly的目标是是通过暴露[底层能力][]接口给开发者,而不是规定哪些工具可以被构建来支持构建真正优秀的工具。底层能力可以: 4 | * 移植现有或相似的工具到WebAssembly; 5 | * 构建特别适合WebAssembly的新工具。 6 | 7 | [底层能力]: https://extensiblewebmanifesto.org 8 | 9 | WebAssembly的开发要求能达到自托管(self-hosting)状态,成为一个愉开发者积极追求的愉快平台而不仅仅只是好看的黑客技术, 因为那些他们期望的开发工具*能够刚好满足期望*。 开发者具有很高的期望而满足这些工具上的期望意味着,WebAssembly具有为非开发者构建丰富应用的能力。 10 | 11 | 我们打算支持包括如下的工具: 12 | * 编辑器: 13 | - 像vim和emacs这样的编辑器,要求达到*刚刚够用*。 14 | * 编译器和语言级虚拟机: 15 | - 对于语言(C/C++,Rust,Go,C#)的编译目标是WebAssembly的编译器,要求能够在WebAssembly环境中运行,编译产生的WebAssembly模块能够在此环境立即运行。 16 | - 诸如bash,Python,Ruby这些语言的虚拟机,要求能够工作。 17 | - 具有just-in-time编译器的虚拟机(如JavaScript虚拟机,luajut,pypy),要求能提供以WebAssembly作为后端的新just-in-time运行环境。 18 | * 调试器: 19 | - 通过对source map的支持,实现基本的浏览器集成。 20 | - 对于如C++等编程语言的完全集成,需要在调试信息格式,中断程序的权限,内省状态,修改状态等上进行更多的标准化工作。 21 | - 调试信息最好是能够按需提供,而不是内建于WebAssembly模块中。 22 | * 对内存不安全语言的sanitizer支持:包括asan,tsan,msan,ubsan。对sanitizer的高效支持需要如下的改进: 23 | - 支持陷入(trap); 24 | - shadow stack技术 (通常通过`mmap`的`MAP_FIXED`来实现)。 25 | * 对开发者的代码具有可选的安全增强措施:WebAssembly开发者会希望他们的代码能被沙箱化,而这比WebAssembly的具体平台实现所要求提供的保护用户措施走的更远。 26 | * 分析器: 27 | - 基于采样; 28 | - 基于测量。 29 | * 进程dump:包括变量,调用栈,堆,全局变量,线程列表。 30 | * JavaScript+WebAssembly大小优化工具:通过调用JavaScript来于Web平台的其他部分进行通信的庞大的WebAssembly+JavaScript混合应用程序,需要工具来进行dead code stripping*(译者注:删除目标文件中不需要加载的符号和代码)*和跨API边界的全局优化。 31 | 32 | 在许多的案例中,工具将会完全由无需任何特定WebAssembly工具支持的WebAssmbly实现;虽然这对调试器来讲是不可能的,但是对sanitizer来讲完全可行。 33 | -------------------------------------------------------------------------------- /Portability.md: -------------------------------------------------------------------------------- 1 | # 可移植性 2 | 3 | WebAssembly的[二进制格式](BinaryEncoding.md)被设计成可以在多种操作系统和指令集体系结构,以及[Web环境](Web.md)和[非Web环境](Web.md)下高效运行。 4 | 5 | ## 高效执行的(环境)假设 6 | 7 | [受限,局部和非确定性](Nondeterminism.md)的执行环境无法提供下面列出的特性,但是仍然可以运行WebAssembly模块。尽管这些环境不得不模拟主机硬件或操作系统不提供的行为,使得WebAssembly模块看起来*好像*环境支持它们顺利执行。这种方式有时会导致性能低下。 8 | 9 | 随着WebAssembly标准不断深入,我们期望能下面这些要求,和使WebAssembly适应那些在最初设计时并不存在的新平台的方式能够形式化。 10 | 11 | 具体来讲,WebAssembly的可移植性需假定执行环境提供如下的特征: 12 | 13 | * 一个字节(byte)有8比特(bit)。 14 | * 支持以1个字节为粒度进行访存。 15 | * 支持非对齐内存访问,或者通过可靠的陷入方式以软件模拟来实现。 16 | * 二进制补码的带符号32位整型,以及可选的64位整型。 17 | * IEEE754-2008规范的32位和64位浮点数,但不包括[一些例外](AstSemantics.md#floating-point-operators)。 18 | * 小端序(Little-endian)。 19 | * 能被32位指针和索引高效访问的内存区域。 20 | * wasm64(64位wasm)额外需要支持64位指针和索引访问[超过4GB](FutureFeatures.md#linear-memory-bigger-than-4-gib)的线性内存空间。 21 | * 强制WebAssembly模块与同一主机上其他进程和模块(包括WebAssembly模块和非WebAssembly模块)之间进行安全隔离。 22 | * 执行环境必须为所有线程(即使在非并行方式中),提供forward progress保证*(译者注:[forward progress guarantees](https://msdn.microsoft.com/en-us/library/windows/hardware/ff543244(v=vs.85).aspx)是在低内存场景下防止丢失关键数据的一种技术)*。 23 | * 当8位,16位或32位自然对齐时,必须支持lock-free原子内存操作,实现该支持的最小代价是具有原子compare-and-exchange操作(或等价的load-linked/store-conditional操作)。 24 | * wasm64额外需要支持64位自然对齐时的lock-free原子内存操作。 25 | 26 | ## API 27 | 28 | WebAssembly没有对任何API或系统调用作规范, 并且已规范的[导入(import)机制](Modules.md)中涉及的具体可用导入项集合也是在主机环境定义的。在[Web](Web.md)环境中, 开发者通过[开放Web平台](https://en.wikipedia.org/wiki/Open_Web_Platform)定义的Web API访问来功能,而[非Web](NonWeb.md)环境可以选择实现标准Web API、标准非Web API(如POSIX)或实现一套自己的接口等方式自由实现。 29 | 30 | ## 源码层 31 | 32 | 在C/C++源码层上的可移植性可以通过在标准API(如POSIX接口)上编程,同时依靠编译器和/或库在编译时(通过`#ifdef`)或运行时(通过[特征检测](FeatureTest.md)和动态[加载](Modules.md)/[链接](DynamicLinking.md)),将标准接口映射到主机环境的导入项的方式实现。 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WebAssembly设计文档,简体中文 2 | 3 | > 本仓库包含了描述有关WebAssembly设计及其高层概述的文档。 4 | 5 | > 本仓库的文档和讨论是[WebAssembly社区组](https://www.w3.org/community/webassembly/)内容的一部分。 6 | 7 | ## 概述 8 | 9 | WebAssembly或者wasm,是一种新型可移植,具有占用存储小,加载速度快等特点的面向web应用的编译格式。 10 | 11 | 当前,WebAssembly是一项由[W3C社区组](https://www.w3.org/community/webassembly/)主导的开放标准,设计目标包括能被所有主流浏览器支持的格式等。*该仓库的内容处于不断变化:所有的内容仍然处于讨论之中* 12 | 13 | - **WebAssembly是执行高效的**: wasm的[抽象语法树](AstSemantics.md)被设计为编码成占用存储小,加载时间短的[二进制格式](BinaryEncoding.md)。在设计目标上,通过利用现有平台普遍具有的[通用硬件能力](Portability.md#高效执行的环境假设),WebAssembly可以达到接近原生二进制代码的执行速度。 14 | 15 | - **WebAssembly是安全的**: WebAssembly描述了一个内存安全,沙箱式的[执行环境](AstSemantics.md#linear-memory),该执行环境甚至可以利用现有的JavaScript虚拟机加以实现。当其内嵌于[Web](Web.md)环境时,WebAssembly将强制与浏览器同源并执行相同的权限安全策略以保证安全。 16 | 17 | - **WebAssembly是开放和可调试的**: WebAssembly设计了美化的[文本格式](TextFormat.md)用于调试、测试、试验、优化、学习、传授以及直接使用该格式进行编程。该文本格式同时也在[查看wasm模块的代码](FAQ.md#will-webassembly-support-view-source-on-the-web)的功能中被使用。 18 | 19 | - **WebAssembly是开放web平台体系中的一部分**: WebAssembly在设计上继续保持了无需更新、特性测试和向后兼容等[web应用的本质属性](Web.md)。WebAssembly模块可以与Javascript上下文互调用,并且与JavaScript使用相同的Web API来访问浏览器所有支持的功能。除此之外,WebAssembly同时支持[非web](NonWeb.md)的运行环境。 20 | 21 | ## 更多信息 22 | 23 | | 资源 | 位置 | 24 | |------------------|--------------------------| 25 | | 高层设计目标 | [HighLevelGoals.md](HighLevelGoals.md) | 26 | | Frequently Asked Questions | [FAQ.md](FAQ.md) | 27 | | 语言规范(进行中......) | [spec/README.md](https://github.com/WebAssembly/spec) | 28 | | 端到端的原型(从C++到浏览器虚拟机) | [wasm-e2e/README.md](https://github.com/WebAssembly/wasm-e2e) | 29 | 30 | ## 设计流程和贡献 31 | 32 | 具有实际意义的WebAssembly规范是在[spec仓库](https://github.com/WebAssembly/spec/)中开展的。但目前为止,高层设计讨论将在继续在本设计仓库中,通过issue和pull request的方式进行,从而使得规范的制定工作更加聚焦。 33 | 34 | 我们计划wasm分步提供如下的功能: 35 | 36 | 1. [MVP](MVP.md); 37 | 2. [MVP之后版本(PostMVP)](PostMVP.md); 38 | 3. [远期版本](FutureFeatures.md)中。 39 | 40 | 加入我们: 41 | 42 | * [W3C社区组](https://www.w3.org/community/webassembly/) 43 | * IRC频道: `irc://irc.w3.org:6667/#webassembly` 44 | * 通过[如何贡献](Contributing.md)页面! 45 | 46 | 如果您想贡献一份力,请查看[合乎道德和专业行为的代码](CodeOfConduct.md)页面。 47 | -------------------------------------------------------------------------------- /Polyfill.md: -------------------------------------------------------------------------------- 1 | # Polyfill到JavaScript 2 | 3 | 即使在浏览器提供WebAssembly的原生支持以前,开发者同样可以在Web上,使用将WebAssembly转换到JavaScript的[polyfill][]库来交付应用。除了将应用打包成前瞻性样式*(译者注:即二进制格式)*,以便于当浏览器原生地支持WebAssembly时,应用将立刻用上之外,polyfill同时提供了以下额外的好处: 4 | 5 | * 采用[二进制编码](BinaryEncoding.md)传输,所以下载文件较小; 6 | * 对启动性能影响较小(将二进制格式解码成JavaScript速度很快); 7 | * 与现有[Emscripten][],[asm.js][]和[PNaCl][]等的将C/C++应用编译到浏览器的方法相比,具有相同的高吞吐率。 8 | 9 | 进一步的,polyfill将允许我们在早期的二进制编码上进行试验,同时在格式定稿之前获取开发者的反馈,然后作为[MVP版本](MVP.md)的一部分提供原生地支持。 10 | 11 | 在[polyfill仓库][]中,制定中的原型polyfill将拆开试验性的二进制格式到JavaScript,同时转换[asm.js][]到WebAssembly(对于现有的Web应用将会有用),我们同时也保留存在多个不同的polyfill以满足不同开发者的需求的可能性。 12 | 13 | 正如在浏览器嵌入的[实现细节](Web.md#implementation-details)中详细描述的一样,**高效**的polyfill会重用大量的Web平台现有的功能。 14 | 15 | [polyfill]: https://remysharp.com/2010/10/08/what-is-a-polyfill 16 | [Emscripten]: http://emscripten.org 17 | [asm.js]: http://asmjs.org 18 | [PNaCl]: http://gonacl.com 19 | [polyfill仓库]: https://github.com/WebAssembly/polyfill-prototype-1 20 | 21 | ## Polyfill的偏离 22 | 23 | **高效**的polyfill可能会有意识地偏离具体的WebAssembly语义:这是为了在具体实践中可用,polyfill不必同WebAssembly规范保持100%的相同。在一些个别案例(通常是C/C++中未定义的行为)中,JavaScript和asm.js都没有理想的语义以高效地维护正确性。 24 | 25 | 如果有必要的话,polyfill可以在牺牲性能的条件下,提供选项用于保证语义的完全正确性,尽管对于可移植的C/C++代码来讲,这样的做法估计是不必的。 26 | 27 | 下面是一些我们已识别出的潜在且可取的语义偏离: 28 | 29 | * **[非对齐的heap访问](AstSemantics.md#alignment)**:由于对非对齐的load/store操作需要保证返回正确结果但ams.js强制使用对齐(例如,`HEAP32[i>>2]`屏蔽了最末两位),所以asm.js实现的polyfill需要将*所有的*load/store操作转换到字节访问上(无论是否已经对齐)以保证正确。但为了达到有竞争力的性能,polyfill的原型将以全尺寸(full-size)的存取(看起来索引好像是对齐的)作为默认(但可能出错)方式。所以一般情况下,提供正确的对齐信息对于可移植WebAssembly的性能很重要;同时对齐信息也保证了polyfill正确和高效。 30 | * **[heap访问越界](AstSemantics.md#out-of-bounds)**:无论WebAssembly行为如何,asm.js实现的polyfill将会遵循以下的标准asm.js行为: 31 | - 越界store操作将会被忽略(当作no-op操作); 32 | - 对于整型的越界load操作,返回0;对于浮点型的越界load操作,返回NaN。 33 | * **[32位整型操作](AstSemantics.md#32-bit-integer-operators)**:无论WebAssembly行为如何,asm.js实现的polyfill将会遵循以下的标准行为: 34 | - 除零返回0; 35 | - `INT32_MIN / -1`返回`INT32_MIN`; 36 | - 移位计数(shift count)将会被隐式屏蔽掉。 37 | * **[数据类型的变换](AstSemantics.md#datatype-conversions-truncations-reinterpretations-promotions-and-demotions)**:无论WebAssembly行为如何,asm.js实现的polyfill将会遵循以下的标准行为: 38 | - 在将浮点数变换为整型失败时,返回0; 39 | - 可任意指定NaN的值。 40 | * **[NaN的bit传播](AstSemantics.md#floating-point-operators)**:无论WebAssembly行为如何,asm.js实现的polyfill将会遵循以下的标准行为: 41 | - 可任意指定NaN的值。 42 | 43 | ## Polyfill的演变 44 | 45 | 整个MVP版本功能集将实现完全高效可polyfill化;在WebAssembly进化到MVP版本之后,工作组将会遵循如下原则: 46 | 47 | * 标准化可以被polyfill的功能点; 48 | * 其他的Web标准体协同进化, 以保证将来出现的WebAssembly功能点可被polyfill。 49 | 50 | 但是在MVP版本之后,并没有硬性的要求WebAssembly功能一定能被polyfill。如果出现某个非常可取但无法高效polyfill的功能,那么WebAssembly还是会采纳该功能。但这并不是一个可掉以轻心的决定,WebAssembly工作组将会试图使得开发者避免使用这些功能或者通过[功能检测](FeatureTest.md)机制提供fallback。 51 | -------------------------------------------------------------------------------- /FeatureTest.md: -------------------------------------------------------------------------------- 1 | *(译者注:有些地方真的不知如何翻译,呼唤各路大神的帮助)* 2 | 关于推动该功能的场景请查看[设计理念](Rationale.md#feature-testing---motivating-scenarios)文档 3 | 4 | # 特性测试 5 | 6 | 在[PostMVP](PostMVP.md)版本中, 应用可以通过[`has_feature`或相似的API](PostMVP#Feature-Testing)来查询个特性是否被平台支持。该功能的初衷基于现实情况中,各个特性在不同的引擎里,会在不同的时间以不同的顺序被实现完成。 7 | 8 | 下面是描述特性测试功能的示例。 9 | 10 | 由于一些WebAssembly特性增加了操作,而所有的WebAssembly模块的代码都采用事先(ahead-of-time)的验证方式。常见的JavaScript的特性检测是如下模式: 11 | ``` 12 | if (foo) 13 | foo(); 14 | else 15 | alternativeToFoo(); 16 | ``` 17 | 而在WebAssembly中该代码将无法正常工作(如果 `foo` 函数不支持,那么`foo()` 将验证失败)。 18 | 19 | 在这种情况下,应用可以选择如下的两种策略之一作为替代方法: 20 | 21 | 1. 将模块编译成多个不同的版本,对应于多个不同平台所支持的特性,并使用`has_feature`方法测试决定将要加载的版本。 22 | 23 | 2. 在["特定"层解码](BinaryEncoding.md)阶段, which will happen 24 | in user code in the MVP *anyway*,使用`has_feature`来决定哪些特性已被支持, 并将不支持的功能映射到polyfill或陷入(trap)方法上实现。 25 | 26 | 上面两种做法都可使用工具链和编译flag来自动实现,并且`has_feature`是一个常量表达式,因此它可以被WebAssembly引擎常量合并。 27 | 28 | 为了说明这个问题,考虑下面四个例子: 29 | 30 | * [`i32.min_s`](FutureFeatures.md#additional-integer-operators) —— 策略2可以被用来替换 31 | `(i32.min_s lhs rhs)`到等价的表达式 32 | ,即存储 `lhs` 和`rhs` 到局部变量中,然后使用`i32.lt_s` 和 `select`操作实现相同的功能。 33 | * [线程](PostMVP.md#threads) - 如果应用大量地使用`#ifdef`来进行线程开启/关闭的构建(build),那么策略1是适用的方法。但是,如果应用能够将线程的使用抽象成一些原语,策略2可以用来为正确的原语实现打补丁。 34 | * [`mprotect`](FutureFeatures.md#finer-grained-control-over-memory) —— 如果引擎 35 | 无法使用操作系统提供的signal handling机制来高效实现`mprotect`,那么`mprotect`可能永久地变成了一个可选特性。但如果`mprotect`的使用不是为了保证正确性(而仅仅是为了捕捉bug),即可用`nop`来替换。相反,为了保证正确性而使用`mprotect`但存在不依赖`mprotect`的替代方法,那么`mprotect`可以依靠应用测试`(has_feature "mprotect")` 方法来避免调用`abort()`,用`abort()`进行替换。`has_feature`查询操作可以通过现有的`__builtin_cpu_supports`函数暴露给C++代码。 36 | * [SIMD](PostMVP.md#fixed-width-simd)(单指令多数据流)—— 当SIMD操作有足够优秀的polyfill实现时,例如通过`f32x4.mul`/`add`实现`f32x4.fma`指令, 那么策略2适用(与上面`i32.min_s`的例子相似)。相反的,当SIMD功能没有足够好的polyfill(例如,`f64x2`同时引入了操作符*和*类型)时,需要提供替代的算法并在加载时对其进行选择。 37 | 38 | 下面是一个假定(非真正实现)的对SIMD的`f64x2`功能进行polyfill的例子,C++编译器可以提供新的功能属性,来标识一个函数是另一个函数的优化,但特性依赖的版本(与 39 | [`ifunc`属性](https://gcc.gnu.org/onlinedocs/gcc-4.7.2/gcc/Function-Attributes.html#index-g_t_0040code_007bifunc_007d-attribute-2529)相似,但不具有callback回调): 40 | ``` 41 | #include 42 | void foo(...) { 43 | __m128 x, y; // -> f32x4 locals 44 | ... 45 | x = _mm_add_ps(x, y); // -> f32x4.add 46 | ... 47 | } 48 | void foo_f64x2(...) __attribute__((optimizes("foo","f64x2"))) { 49 | __m256 x, y; // -> f64x2 locals 50 | ... 51 | x = _m_add_pd(x, y); // -> f64x2.add 52 | ... 53 | } 54 | ... 55 | foo(...); // calls either foo or foo_f64x2 56 | ``` 57 | 在上面的例子中,工具链可以同时在“规范层”的二进制格式中,产生`foo`和`foo_f64x2`作为函数定义。如果`(has_feature "f64x2")`测试通过,那么polyfill会在加载时将`foo`替换成`foo_f64x2`。许多其他的策略也允许进行细粒度和粗粒度的替换,并由于这些替换都是发生在用户空间,所以策略可以随着时间不断演化。 58 | 59 | 请在[更好的特性测试支持](FutureFeatures.md#better-feature-testing-support)文档中查看进一步的功能。 60 | -------------------------------------------------------------------------------- /BinaryEncoding.md: -------------------------------------------------------------------------------- 1 | # 二进制编码 2 | 3 | 本文档描述了WebAssembly模块的[可移植](Portability.md)二进制格式。 4 | 5 | 二进制编码是模块信息的密集表示方式,并同时达到减小文件体积,快速解码以及减少内存使用量的目的。更多信息请查看[设计理念](Rationale.md#why-a-binary-encoding)部分。 6 | 7 | 编码的过程被分为如下三层: 8 | 9 | * **第0层(Layer 0)**是抽象语法树(AST)以及相关的数据结构的一个简单的前序编码。编码的结果是密集且容易交互的,使得其适合于JIT,测量工具,调试等场景。 10 | * **第1层(Layer 1)**利用了语法树和节点本身的特定知识,对第0层结果进行结构性压缩。结构压缩方法引入了更多高效的值编码、模块内部的重排序以及剔除结构相同树节点等方法。 11 | * **第2层(Layer 2)**进一步使用了多种通用压缩算法,例如[gzip](http://www.gzip.org/)和[Brotli](https://datatracker.ietf.org/doc/draft-alakuijala-brotli/)等已经在现有浏览器和其它工具中实现的算法。 12 | 13 | 更重要的是,这种分层方法使得开发和标准制定可以增量地进行。例如,通过在应用层上解压缩到更低层的方式,可以试验第1层和第2层编码技术。压缩技术一旦稳定下来以后,那么将成为标准并转移到原生的实现上。 14 | 15 | # 数据类型 16 | 17 | ### uint8 18 | 单字节无符号整型 19 | 20 | ### uint32 21 | 四字节[小端序](https://zh.wikipedia.org/wiki/%E5%AD%97%E8%8A%82%E5%BA%8F#.E5.B0.8F.E7.AB.AF.E5.BA.8F)(little endian)无符号整型 22 | 23 | ### varint32 24 | [有符号LEB128格式](https://en.wikipedia.org/wiki/LEB128#Signed_LEB128)32位可变长整型. 25 | 26 | ### varuint32 27 | [无符号LEB128格式](https://en.wikipedia.org/wiki/LEB128)的32位可变长整型, `varuint32`的值可能包含前导0。 28 | 29 | ### varint64 30 | 有符号LEB128格式64位可变长整型。 31 | 32 | ### 值类型 33 | 使用一个单字节无符号整型表示[值类型](AstSemantics.md#types)。这些类型具体编码如下: 34 | * `1`表示`i32`类型 35 | * `2`表示`i64`类型 36 | * `3`表示`f32`类型 37 | * `4`表示`f64`类型 38 | 39 | 40 | # 定义 41 | 42 | ### 前序编码 43 | 依据语法树的编码方式,每个节点以可识别二进制序列开始,接着是所有子节点的循环编码。 44 | 45 | * 示例 46 | * 给定一个简单的AST节点:`I32Add(left: AstNode, right: AstNode)` 47 | * 首先对`I32Add`的编码(uint8类型) 48 | * 然后依次循环编码左节点和右节点 49 | 50 | * 给定一个调用AST节点:`Call(callee_index: uint32_t, args: AstNode[])` 51 | * 首先对`Call`的编码(uint8类型) 52 | * 然后写入(可变长)整型`callee_index`的值(varuint32类型) 53 | * 然后依次循环编码每个参数变量节点, 其中参数列表长度通过在签名列表中查询`callee_index`得到 54 | 55 | # 模块结构 56 | 57 | 本节记录的是当前模块结构的原型格式。该格式是以[原生v8上的wasm原型格式](https://docs.google.com/document/d/1-G11CnMA0My20KI9D7dBR6ZCPOBCRD0oCH6SHCPFGx0/edit?usp=sharing)为基础,并将在未来取代之。 58 | 59 | ## 高层结构 60 | 61 | 模块以如下形式的魔术数字(magic number)和版本号开头 62 | 63 | | 字段 | 类型 | 描述 | 64 | | ----- | ----- | ----- | 65 | | magic number | `uint32` | `0x6d736100` (也就是'\0asm') | 66 | | version | `uint32` | 版本号。当前是10,MVP将会重置版本为1 | 67 | 68 | 接着是一系列的段。通常情况下段是可重复的, 但是某些类型的段只能出现一次,或某些段所依赖的其它段必须出现在其之前,但又不必挨着该段出现,所以可以以任意先后顺序出现。每一段以一个立即字符串作为标识(ID),而对于某些段,如果它的标识符(ID)对于某个具体的WebAseembly实现是未知的,那么这些段将会被忽略。所有的段的以如下的内容作为编码开始: 69 | 70 | | 字段 | 类型 | 描述 | 71 | | ----- | ----- | ----- | 72 | | size | `varuint32` | 该段(除了本字段)的大小,以字节为单位 | 73 | | id_len | `varuint32` | 该段的标识符长度 | 74 | | id_str | `bytes` | 长度为id_len字节的标识符字符串 | 75 | 76 | ### 内存段 77 | 78 | ID: `memory` 79 | 80 | 内存段声明了与该模块相关联的内存的大小和特征。一个模块最多只有一个内存段。 81 | 82 | | 字段 | 类型 | 描述 | 83 | | ----- | ----- | ----- | 84 | | min_mem_pages | `varuint32` | 最小可能内存大小,以64KiB页为单位 | 85 | | max_mem_pages | `varuint32` | 最小可能内存大小,以64KiB页为单位 | 86 | | exported | `uint8` | 如果内存对对模块外可见,则为`1` | 87 | 88 | ### 签名段 89 | 90 | ID: `signatures` 91 | 92 | 签名段声明了该模块会用到的所有函数的签名。一个模块最多只有一个签名段。 93 | 94 | | 字段 | 类型 | 描述 | 95 | | ----- | ----- | ----- | 96 | | count | `varuint32` | 签名项的数量 | 97 | | entries | `signature_entry*` | 签名项列表,具体描述如下 | 98 | 99 | #### 签名项 100 | | 字段 | 类型 | 描述 | 101 | | ----- | ----- | ----- | 102 | | param_count | `varuint32` | 函数的参数数量 | 103 | | return_type | `value_type?` | 函数的返回类型,如果该项是`0`则表示无返回值 | 104 | | param_types | `value_type*` | 函数参数的类型 | 105 | 106 | ### 导入表段 107 | 108 | ID: `import_table` 109 | 110 | 导入表段声明了该模块会用到的所有导入项。一个模块最多只有一个导入表段。 111 | 112 | | 字段 | 类型 | 描述 | 113 | | ----- | ----- | ----- | 114 | | count | `varuint32` | 导入项的数量 | 115 | | entries | `import_entry*` | 导入项列表,具体描述如下 | 116 | 117 | #### 导入项 118 | | 字段 | 类型 | 描述 | 119 | | ----- | ----- | ----- | 120 | | sig_index | `varuint32` | 该导入项的在函数表的索引 | 121 | | module_len | `varuint32` | 被导入的模块名称的长度 | 122 | | module_str | `bytes` | 长度为`module_len`的模块名称字符串 | 123 | | function_len | `varuint32` | 被导入的函数名称长度 | 124 | | function_str | `bytes` | 长度为`function_len`的函数名称字符串 | 125 | 126 | ### 函数签名段 127 | 128 | ID: `function_signatures` 129 | 130 | 函数签名段声明了该模块所有函数的签名,并且该段必须位于[签名段](#签名段)之后。一个模块最多只有一个函数签名段。 131 | 132 | | 字段 | 类型 | 描述 | 133 | | ----- | ----- | ----- | 134 | | count | `varuint32` | 函数签名项的数量 | 135 | | signatures | `varuint32*` | 该函数签名项出现在签名表的位置的下标组成的列表 | 136 | 137 | ### 函数体段 138 | 139 | ID: `function_bodies` 140 | 141 | 函数体段为模块中的每个函数指定了其主体部分的内容,因此该段必须出现在[函数签名段](#函数签名段)之后。 142 | 函数签名项的数量和函数体项的数量必须相同,并且第`i`个签名项必须对应于第`i`个函数主体。 143 | 144 | | Field | Type | Description | 145 | | ----- | ----- | ----- | ----- | 146 | | count | `varuint32` | 函数体项的数量 | 147 | | bodies | `function_body*` | [函数体](#函数体)的列表 | 148 | 149 | ### 导出表段 150 | 151 | ID: `export_table` 152 | 153 | 导出表段声明了该模块所有导出项。一个模块最多只有一个导出表段,且该段必须位于[函数签名段](#函数签名段)之后。 154 | 155 | | 字段 | 类型 | 描述 | 156 | | ----- | ----- | ----- | 157 | | count | `varuint32` | 导出项的数量 | 158 | | entries | `import_entry*` | 导出项列表,具体描述如下 | 159 | 160 | #### 导出项 161 | | 字段 | 类型 | 描述 | 162 | | ----- | ----- | ----- | 163 | | func_index | `varuint32` | 该项在函数表出现的位置索引 | 164 | | function_len | `varuint32` | 函数名长度 | 165 | | function_str | `bytes` | 长度为`function_len`的函数名称字符串编码 | 166 | 167 | ### 开始函数段 168 | 169 | ID: `start_function` 170 | 171 | 一个模块至多有一个函数开始段,并且该段必须出现在[函数签名段](#函数签名段)之后。 172 | 173 | | 字段 | 类型 | 描述 | 174 | | ----- | ----- | ----- | 175 | | index | `varuint32` | 开始函数在函数表的索引 | 176 | 177 | ### 数据段段 178 | 179 | ID: `data_segments` 180 | 181 | 数据段段声明了已初始化并将加载到线性内存的数据。一个模块最多可能有一个数据段段。 182 | 183 | | 字段 | 类型 | 描述 | 184 | | ----- | ----- | ----- | 185 | | count | `varuint32` | 数据段项的数量 | 186 | | entries | `data_segment*` | 数据段项列表,具体描述如下 | 187 | 188 | `data_segment`内容如下: 189 | 190 | | 字段 | 类型 | 描述 | 191 | | ----- | ----- | ----- | 192 | | offset | `varuint32` | 存储该数据的位置,以在线性存储中的位移量作为表示 | 193 | | size | `varuint32` | `data`的大小,以字节为单位 | 194 | | data | `bytes` | `size`个字节的具体数据 | 195 | 196 | ### 间接函数表段 197 | 198 | ID: `function_table` 199 | 200 | 间接函数表段声明了间接函数表的大小和每个表项的值,该值是函数在[函数签名段](#函数签名段)所在位置的索引(因此该段也必须出现在函数签名段之后)。 201 | 202 | | 字段 | 类型 | 描述 | 203 | | ----- | ----- | ----- | 204 | | count | `varuint32` | 间接函数表项的数量 | 205 | | entries | `varuint32*` | 间接函数表项列表,以在在函数表位置索引作为每一项 | 206 | 207 | ### 名字段 208 | 209 | ID: `names` 210 | 211 | 名字段在模块中可能出现0或1次,并且不会改变执行时语义。该段在验证时出现错误不会导致模块验证失败,而是判定该段在模块中不存在。名字段的期望使用场景是,当二进制的WebAssembly模块在浏览器或其它开发环境中被查看时,该段的内容将被作为函数名和局部名,出现在[文本格式](TextFormat.md)中。 212 | 213 | | 字段 | 类型 | 描述 | 214 | | ----- | ----- | ----- | 215 | | count | `varuint32` | 名字项的数量 | 216 | | entries | `function_names*` | 名字项列表 | 217 | 218 | `function_name`列表中每一项对在函数项列表中同一索引的函数项指派名字。其数量可能会不等于实际函数的数量。 219 | 220 | #### 函数名 221 | 222 | | 字段 | 类型 | 描述 | 223 | | ----- | ----- | ----- | 224 | | fun_name_len | `varuint32` | 字符串长度,以字节为单位 | 225 | | fun_name_str | `bytes` | 合法的utf8字符串编码 | 226 | | local_count | `varuint32` | 局部名项数量 | 227 | | local_names | `local_name*` | 局部名项列表 | 228 | 229 | `local_name`列表每一项对在局部项列表中同一索引的局部项指派名字为。其数量可能会不等于实际的局部值数量 230 | 231 | #### 局部名 232 | 233 | | 字段 | 类型 | 描述 | 234 | | ----- | ----- | ----- | 235 | | local_name_len | `varuint32` | 字符串长度,以字节为单位 | 236 | | local_name_str | `bytes` | 合法的utf8字符串编码 | 237 | 238 | ### 结束段 239 | 240 | ID: `end` 241 | 242 | 该段表示模块的结束,任何在本段之后出现的多余数据都不会被解码器解释。(正是由于不会被解释,结束段之后部分)可以用来,例如,存储函数名或数据段主体内容。 243 | 244 | | 字段 | 类型 | 描述 | 245 | | ----- | ----- | ----- | 246 | 247 | ### 未知段 248 | 249 | | 字段 | 类型 | 描述 | 250 | | ----- | ----- | ----- | 251 | | body | `bytes` | 该段的内容 | 252 | 253 | 如果某段的ID对于WebAssembly的具体实现是未知的,那么该段(是未知段)并会被忽略。 254 | 255 | # 函数体 256 | 257 | 函数体包含了局部变量声明序列,然后接着是[抽象语法树](AstSemantics.md)的密集前序编码结果。抽象语法树的每个节点对应于一个操作,例如`i32.add`、`if`或`block`。每个操作节点被编码成操作码,接着是立即数(如果有),再接着子节点(如果有)的形式。 258 | 259 | | 名字 | 操作码 | 描述 | 260 | | ----- | ----- | ----- | 261 | | body size | `varuint32` | 函数体的大小,以字节为单位 | 262 | | local count | `varuint32` | 局部项的数量 | 263 | | locals | `local_entry*` | 局部变量的列表 | 264 | | ast | `byte*` | 前序编码后的AST | 265 | 266 | #### 局部项 267 | 268 | 每一个局部项声明了数个同一类型的局部变量,并允许多个局部项具有相同的变量类型。 269 | 270 | | 字段 | 类型 | 描述 | 271 | | ----- | ----- | ----- | 272 | | count | `varuint32` | 变量的数量 | 273 | | type | `value_type` | 变量的类型 | 274 | 275 | 276 | ## 控制流操作([具体描述](AstSemantics.md#control-flow-structures)) 277 | 278 | | 操作名 | 操作码 | 立即数 | 描述 | 279 | | ---- | ---- | ---- | ---- | 280 | | `nop` | `0x00` | | 空操作 | 281 | | `block` | `0x01` | count = `varuint32` | 表达式序列,该序列最后一个表达式产生一个返回值 | 282 | | `loop` | `0x02` | count = `varuint32` | 产生控制流回路的块 | 283 | | `if` | `0x03` | | 高层单臂if(即只有if,没有else) | 284 | | `if_else` | `0x04` | | 高层双臂if(即if-else) | 285 | | `select` | `0x05` | | 依照条件选择两个值中的一个 | 286 | | `br` | `0x06` | relative_depth = `varuint32` | 以嵌套该块的外部块为目标进行跳转 | 287 | | `br_if` | `0x07` | relative_depth = `varuint32` | 以嵌套该块的外部块为目标进行条件跳转 | 288 | | `br_table` | `0x08` | 解释如下 | 分支表控制流结构 | 289 | | `return` | `0x14` | | 从函数中返回值或返回0(表示无返回值) | 290 | | `unreachable` | `0x15` | | 立即陷入(trap) | 291 | 292 | `br_table`操作有一个立即操作数,该操作数采用如下编码方式: 293 | 294 | | 字段 | 类型 | 描述 | 295 | | ---- | ---- | ---- | 296 | | target_count | `varuint32` | target_table中目标项个数 | 297 | | target_table | `uint32*` | 目标项列表,每一项表示可以跳转到外部块或环路的目标位置 | 298 | | default_target | `uint32` | 默认跳转到外部块或环路的目标位置 | 299 | 300 | `br_table`操作码实现了间接分支功能,它接受一个`i32`表达式值作为输入的位移量,在`target_table`范围内跳转到目标块或环路。如果输入值超出了范围,那么`br_table`将跳转到默认的目标位置。 301 | 302 | ## 基本操作([详细描述](AstSemantics.md#constants)) 303 | | 名称 | 操作码 | 立即数 | 描述 | 304 | | ---- | ---- | ---- | ---- | 305 | | `i32.const` | `0x0a` | value = `varint32` | 作为`i32`类型的常量 | 306 | | `i64.const` | `0x0b` | value = `varint64` | 作为`i32`类型的常量 | 307 | | `f64.const` | `0x0c` | value = `uint64` | 作为`u64`类型的常量 | 308 | | `f32.const` | `0x0d` | value = `uint32` | 作为`u32`类型的常量 | 309 | | `get_local` | `0x0e` | local_index = `varuint32` | 读局部变量或参数的值 | 310 | | `set_local` | `0x0f` | local_index = `varuint32` | 为局部变量或参数赋值 | 311 | | `call` | `0x12` | function_index = `varuint32` | 根据索引(index)调用函数 | 312 | | `call_indirect` | `0x13` | signature_index = `varuint32` | 根据期望的签名值间接调用函数 | 313 | | `call_import` | `0x1f` | import_index = `varuint32` | 根据索引(index)调用被导入的函数 | 314 | 315 | ## 内存相关的操作([详细描述](AstSemantics.md#linear-memory-accesses)) 316 | 317 | | 名称 | 操作码 | 立即数 | 描述 | 318 | | ---- | ---- | ---- | ---- | 319 | | `i32.load8_s` | `0x20` | `memory_immediate` | 从内存加载 | 320 | | `i32.load8_u` | `0x21` | `memory_immediate` | 从内存加载 | 321 | | `i32.load16_s` | `0x22` | `memory_immediate` | 从内存加载 | 322 | | `i32.load16_u` | `0x23` | `memory_immediate` | 从内存加载 | 323 | | `i64.load8_s` | `0x24` | `memory_immediate` | 从内存加载 | 324 | | `i64.load8_u` | `0x25` | `memory_immediate` | 从内存加载 | 325 | | `i64.load16_s` | `0x26` | `memory_immediate` | 从内存加载 | 326 | | `i64.load16_u` | `0x27` | `memory_immediate` | 从内存加载 | 327 | | `i64.load32_s` | `0x28` | `memory_immediate` | 从内存加载 | 328 | | `i64.load32_u` | `0x29` | `memory_immediate` | 从内存加载 | 329 | | `i32.load` | `0x2a` | `memory_immediate` | 从内存加载 | 330 | | `i64.load` | `0x2b` | `memory_immediate` | 从内存加载 | 331 | | `f32.load` | `0x2c` | `memory_immediate` | 从内存加载 | 332 | | `f64.load` | `0x2d` | `memory_immediate` | 从内存加载 | 333 | | `i32.store8` | `0x2e` | `memory_immediate` | 存入内存 | 334 | | `i32.store16` | `0x2f` | `memory_immediate` | 存入内存 | 335 | | `i64.store8` | `0x30` | `memory_immediate` | 存入内存 | 336 | | `i64.store16` | `0x31` | `memory_immediate` | 存入内存 | 337 | | `i64.store32` | `0x32` | `memory_immediate` | 存入内存 | 338 | | `i32.store` | `0x33` | `memory_immediate` | 存入内存 | 339 | | `i64.store` | `0x34` | `memory_immediate` | 存入内存 | 340 | | `f32.store` | `0x35` | `memory_immediate` | 存入内存 | 341 | | `f64.store` | `0x36` | `memory_immediate` | 存入内存 | 342 | | `memory_size` | `0x3b` | | 查询内存大小(*译者注:应该是分配的主存空间大小*) | 343 | | `grow_memory` | `0x39` | | 增加内存大小 | 344 | 345 | `memory_immediate`类型的编码方式如下: 346 | 347 | | 名称 | 类型 | 描述 | 348 | | ---- | ---- | ---- | 349 | | flags | `varuint32` | bit字段,当前是在最低有效位上包含对齐信息(alignment), 以`log2(alignment)`作为编码 | 350 | | offset | `varuint32` | 位移值 | 351 | 352 | 正如`log2(alignment)`所隐式表达的,对齐长度必须是2的次幂。并且作为额外的验证要求,对齐长度不得大于自然对齐长度。`log(memory-access-size)`最低有效位之后所有位必须置0。而这些位被保留以便未来使用(例如,留作共享内存定序用)。 353 | 354 | ## 简单操作 ([详细描述](AstSemantics.md#32-bit-integer-operators)) 355 | 356 | | 名称 | 操作码 | 立即数 | 描述 | 357 | | ---- | ---- | ---- | ---- | 358 | | `i32.add` | `0x40` | | | 359 | | `i32.sub` | `0x41` | | | 360 | | `i32.mul` | `0x42` | | | 361 | | `i32.div_s` | `0x43` | | | 362 | | `i32.div_u` | `0x44` | | | 363 | | `i32.rem_s` | `0x45` | | | 364 | | `i32.rem_u` | `0x46` | | | 365 | | `i32.and` | `0x47` | | | 366 | | `i32.or` | `0x48` | | | 367 | | `i32.xor` | `0x49` | | | 368 | | `i32.shl` | `0x4a` | | | 369 | | `i32.shr_u` | `0x4b` | | | 370 | | `i32.shr_s` | `0x4c` | | | 371 | | `i32.rotr` | `0xb6` | | | 372 | | `i32.rotl` | `0xb7` | | | 373 | | `i32.eq` | `0x4d` | | | 374 | | `i32.ne` | `0x4e` | | | 375 | | `i32.lt_s` | `0x4f` | | | 376 | | `i32.le_s` | `0x50` | | | 377 | | `i32.lt_u` | `0x51` | | | 378 | | `i32.le_u` | `0x52` | | | 379 | | `i32.gt_s` | `0x53` | | | 380 | | `i32.ge_s` | `0x54` | | | 381 | | `i32.gt_u` | `0x55` | | | 382 | | `i32.ge_u` | `0x56` | | | 383 | | `i32.clz` | `0x57` | | | 384 | | `i32.ctz` | `0x58` | | | 385 | | `i32.popcnt` | `0x59` | | | 386 | | `i32.eqz` | `0x5a` | | | 387 | | `i64.add` | `0x5b` | | | 388 | | `i64.sub` | `0x5c` | | | 389 | | `i64.mul` | `0x5d` | | | 390 | | `i64.div_s` | `0x5e` | | | 391 | | `i64.div_u` | `0x5f` | | | 392 | | `i64.rem_s` | `0x60` | | | 393 | | `i64.rem_u` | `0x61` | | | 394 | | `i64.and` | `0x62` | | | 395 | | `i64.or` | `0x63` | | | 396 | | `i64.xor` | `0x64` | | | 397 | | `i64.shl` | `0x65` | | | 398 | | `i64.shr_u` | `0x66` | | | 399 | | `i64.shr_s` | `0x67` | | | 400 | | `i64.rotr` | `0xb8` | | | 401 | | `i64.rotl` | `0xb9` | | | 402 | | `i64.eq` | `0x68` | | | 403 | | `i64.ne` | `0x69` | | | 404 | | `i64.lt_s` | `0x6a` | | | 405 | | `i64.le_s` | `0x6b` | | | 406 | | `i64.lt_u` | `0x6c` | | | 407 | | `i64.le_u` | `0x6d` | | | 408 | | `i64.gt_s` | `0x6e` | | | 409 | | `i64.ge_s` | `0x6f` | | | 410 | | `i64.gt_u` | `0x70` | | | 411 | | `i64.ge_u` | `0x71` | | | 412 | | `i64.clz` | `0x72` | | | 413 | | `i64.ctz` | `0x73` | | | 414 | | `i64.popcnt` | `0x74` | | | 415 | | `i64.eqz` | `0xba` | | | 416 | | `f32.add` | `0x75` | | | 417 | | `f32.sub` | `0x76` | | | 418 | | `f32.mul` | `0x77` | | | 419 | | `f32.div` | `0x78` | | | 420 | | `f32.min` | `0x79` | | | 421 | | `f32.max` | `0x7a` | | | 422 | | `f32.abs` | `0x7b` | | | 423 | | `f32.neg` | `0x7c` | | | 424 | | `f32.copysign` | `0x7d` | | | 425 | | `f32.ceil` | `0x7e` | | | 426 | | `f32.floor` | `0x7f` | | | 427 | | `f32.trunc` | `0x80` | | | 428 | | `f32.nearest` | `0x81` | | | 429 | | `f32.sqrt` | `0x82` | | | 430 | | `f32.eq` | `0x83` | | | 431 | | `f32.ne` | `0x84` | | | 432 | | `f32.lt` | `0x85` | | | 433 | | `f32.le` | `0x86` | | | 434 | | `f32.gt` | `0x87` | | | 435 | | `f32.ge` | `0x88` | | | 436 | | `f64.add` | `0x89` | | | 437 | | `f64.sub` | `0x8a` | | | 438 | | `f64.mul` | `0x8b` | | | 439 | | `f64.div` | `0x8c` | | | 440 | | `f64.min` | `0x8d` | | | 441 | | `f64.max` | `0x8e` | | | 442 | | `f64.abs` | `0x8f` | | | 443 | | `f64.neg` | `0x90` | | | 444 | | `f64.copysign` | `0x91` | | | 445 | | `f64.ceil` | `0x92` | | | 446 | | `f64.floor` | `0x93` | | | 447 | | `f64.trunc` | `0x94` | | | 448 | | `f64.nearest` | `0x95` | | | 449 | | `f64.sqrt` | `0x96` | | | 450 | | `f64.eq` | `0x97` | | | 451 | | `f64.ne` | `0x98` | | | 452 | | `f64.lt` | `0x99` | | | 453 | | `f64.le` | `0x9a` | | | 454 | | `f64.gt` | `0x9b` | | | 455 | | `f64.ge` | `0x9c` | | | 456 | | `i32.trunc_s/f32` | `0x9d` | | | 457 | | `i32.trunc_s/f64` | `0x9e` | | | 458 | | `i32.trunc_u/f32` | `0x9f` | | | 459 | | `i32.trunc_u/f64` | `0xa0` | | | 460 | | `i32.wrap/i64` | `0xa1` | | | 461 | | `i64.trunc_s/f32` | `0xa2` | | | 462 | | `i64.trunc_s/f64` | `0xa3` | | | 463 | | `i64.trunc_u/f32` | `0xa4` | | | 464 | | `i64.trunc_u/f64` | `0xa5` | | | 465 | | `i64.extend_s/i32` | `0xa6` | | | 466 | | `i64.extend_u/i32` | `0xa7` | | | 467 | | `f32.convert_s/i32` | `0xa8` | | | 468 | | `f32.convert_u/i32` | `0xa9` | | | 469 | | `f32.convert_s/i64` | `0xaa` | | | 470 | | `f32.convert_u/i64` | `0xab` | | | 471 | | `f32.demote/f64` | `0xac` | | | 472 | | `f32.reinterpret/i32` | `0xad` | | | 473 | | `f64.convert_s/i32` | `0xae` | | | 474 | | `f64.convert_u/i32` | `0xaf` | | | 475 | | `f64.convert_s/i64` | `0xb0` | | | 476 | | `f64.convert_u/i64` | `0xb1` | | | 477 | | `f64.promote/f32` | `0xb2` | | | 478 | | `f64.reinterpret/i64` | `0xb3` | | | 479 | | `i32.reinterpret/f32` | `0xb4` | | | 480 | | `i64.reinterpret/f64` | `0xb5` | | | 481 | --------------------------------------------------------------------------------