Skip to content

Latest commit

 

History

History
733 lines (545 loc) · 30.3 KB

BinaryEncoding.md

File metadata and controls

733 lines (545 loc) · 30.3 KB

二进制编码

本文档描述了portableWebAssembly 模块的二进制编码。

二进制编码是模块信息的密集表示,支持小文件、快速解码和减少内存使用。有关详细信息,请参见理由文件

[:独角兽:][未来通用]= 计划的 [未来][未来通用] 功能

编码分为三层:

  • 第 0 层是字节码指令和相关数据结构的简单二进制编码。这种编码很密集,交互起来很简单,因此适用于 JIT、插装工具和调试等场景。
  • 第一层:unicorn: 在第 0 层之上提供结构压缩,利用关于语法树及其节点的性质的特定知识。结构压缩引入了更有效的值编码,重新排列模块中的值,并修剪结构相同的树节点。
  • 第二层:unicorn: 第 2 层应用通用压缩算法,如gzipBrotli,这些算法已在浏览器和其他工具中可用。

最重要的是,分层方法允许以增量方式进行开发和标准化。例如,第 1 层和第 2 层编码技术可以通过应用程序级解压缩到下面的层来试验。随着压缩技术的稳定,它们可以被标准化并转移到本机实现中。

有关第 1 层结构压缩的建议,请参见 [建议的第 1 层压缩:unicorn:][未来压缩]。

数据类型

数字

UINTN

位的_名词_无符号整数,按小端顺序以_名词_/8 字节表示。_名词_为 8、16 或 32。

VARUINTN

LEB128可变长度整数,限于_名词_位(即,值 [0,2^n-1]),由_最多_可能包含填充 0x80 字节的 ceil(n/7)字节表示。

注意:目前,唯一使用的大小是 varuint1varuint7varuint32,其中前两个用于与潜在的未来扩展兼容。

varintn

长度签名 LEB128可变的整数,限于_名词_位(即,值 [-2^(n-1),+2^(n-1)-1]),由_最多_可能包含填充 0x800xFF 字节的 ceil(n/7)字节表示。

注意:目前使用的尺寸只有 varint7varint32varint64

指令操作码

在 MVP 中,的操作码指示全部编码在单个字节中,因为操作码少于 256 个。未来的功能(如SIMDatomics)将使总数超过 256,因此需要扩展方案,将一个或多个单字节值指定为多字节操作码的前缀。

语言类型

所有类型都通过其编码的第一个字节(表示类型构造函数)的负值 varint7 来区分:

Opcode 类型构造函数
-0x01(即字节 0x7f i32
-0x02(即字节 0x7e i64
-0x03(即字节 0x7d f32
-0x04(即字节 0x7c f64
-0x10(即字节 0x70 anyfunc
-0x20(即字节 0x60 func
-0x40 (i.e., the byte 0x40) 表示空 block_type 的伪类型

其中一些字段后面会有其他字段,请参阅下文。

注意:间隙是为 Future:unicorn: 扩展保留的。有符号方案的使用是为了使类型可以与类型部分中的(正)索引共存于单个空间中,这可能与类型系统的未来扩展有关。

值 _ 类型

A varint7 表示 Avalue type。其中之一:

  • I32
  • I64
  • F32
  • F64

如上面所编码的。

阻止 _ 类型

a varint7 表示区块签名。这些类型编码为:

  • 指示具有单个结果的签名的 [ value_type](# 值 _ 类型)
  • 或者 -0x40(即,字节 0x40)指示具有 0 结果的签名。

元素 _ 类型

A varint7 表示 Atable中元素的类型。在 MVP 中,只有一种类型可用:

  • [ anyfunc](semantics.MD# 表)

注意:在 Future:unicorn: 中,可能允许其他元素类型。

FUNC_ 类型

函数签名的描述。它的类型构造函数后面有一个附加说明:

Field Type 描述
form varint7 上面定义的 func 类型构造函数的值
param_count varuint32 函数的参数数目
param_types value_type* 函数的参数类型
return_count varuint1 函数的结果数
return_type value_type? 函数的结果类型(如果返回 _ 计数为 1)

注意:在 Future:unicorn: 中, return_count return_type 可以泛化为允许多个值。

其他类型

全局 _ 类型

全局变量的说明。

Field Type 描述
content_type value_type 值的类型
mutability varuint1 0 如果不可变, 1 如果可变

表格 _ 类型

表的说明。

Field Type 描述
element_type elem_type 元素的类型
limits resizable_limits 看到below

内存 _ 类型

对记忆的描述。

Field Type 描述
limits resizable_limits 看到below

外部 _ 类型

指示要导入或定义的定义类型的单字节无符号整数:

可调整大小的 _ 限制

描述或memory限制的table压缩元组:

Field Type 描述
flags varuint1 1 如果存在最大值字段, 0 则为
initial varuint32 初始长度(以表元素或 WASM 页面为单位)
maximum varuint32? 仅在指定 flags 时存在

注意:在 Future:unicorn: 中,“Flags”字段可以被改变为 varuint32 例如包括用于在线程之间共享的标志。

初始化 _ 表达式

初始化表达式编码是表达式的正常编码,后面是作为分隔符的 end 操作码。

请注意 get_global,在初始值设定项中,表达式只能引用不可变的导入全局变量,并且的 init_expr 所有使用只能出现在导入部分之后。

模块结构

以下文档记录了当前的原型格式。此格式基于并取代了 V8 原生原型格式,该格式最初位于公共设计文件

高层结构

该模块以两个字段的前导码开始:

Field Type 描述
magic number uint32 幻数 0x6d736100(即“\0asm”)
version uint32 版本号 0x1

模块前导码之后是一系列部分。每个部分由一个 1 字节区段代码标识,该字节对已知部分或自定义部分进行编码。然后是部分长度和有效载荷数据。已知部分具有非零 ID,而自定义部分具有一个 0 ID,后跟一个标识字符串,作为有效负载的一部分。

Field Type 描述
id varuint7 区段代码
payload_len varuint32 此部分的大小(以字节为单位)
name_len varuint32 ? 长度( name 以字节为单位),如果 id == 0
name bytes ? 节名:有效的 UTF-8 字节序列,如果 id == 0
payload_data bytes 本节内容,长度 payload_len - sizeof(name) - sizeof(name_len)

每个已知部分都是可选的,最多只能出现一次。自定义部分都具有相同 id 的(0),并且可以不唯一地命名(组成其名称的所有字节可以相同)。

自定义部分旨在用于调试信息、未来发展或第三方扩展。对于 MVP,我们使用特定的自定义部分(名称节)来显示调试信息。

如果 WebAssembly 实现在模块验证或编译期间解释任何自定义节的有效负载,则该有效负载中的错误不得使模块无效。

下面列表中的已知部分不能无序显示,而自定义部分可以以任何顺序散布在列表的任何元素之前、之间以及之后。某些自定义节可能有自己的排序和基数要求。例如,名称节最多出现一次,紧跟在数据节之后。违反此类要求最多可能导致实现忽略该部分,而不会使模块无效。

每个部分的内容都在其 payload_data 中编码。

节名称 Code Description
Type 1 函数签名声明
Import 2 Import declarations
Function 3 Function declarations
Table 4 间接函数表和其他表
Memory 5 Memory attributes
Global 6 Global declarations
Export 7 Exports
Start 8 开始函数声明
Element 9 Elements section
Code 10 函数主体(代码)
Data 11 Data segments

最后一个当前部分的结尾必须与模块的最后一个字节一致。最短的有效模块是 8 个字节( magic numberversion,后面是零节)。

类型部分

type 部分声明将在模块中使用的所有函数签名。

Field Type 描述
count varuint32 要跟随的类型条目的计数
entries func_type* 重复的类型项,如所述above

注意:在 Future:unicorn: 中,此部分也可能包含其他形式的类型条目,可以通过 form 类型编码的字段进行区分。

进口科

import 部分声明将在模块中使用的所有导入。

Field Type 描述
count varuint32 要跟随的导入条目的计数
entries import_entry* 如下所述的重复导入条目

导入条目

Field Type 描述
module_len varuint32 长度( module_str 单位:字节)
module_str bytes 模块名称:有效的 UTF-8 字节序列
field_len varuint32 长度( field_str 单位:字节)
field_str bytes 字段名:有效的 UTF-8 字节序列
kind external_kind 要导入的定义的种类

后跟,如果 kindFunction

Field Type 描述
type varuint32 函数签名的类型索引

或者,如果 kindTable

Field Type 描述
type table_type 导入表的类型

或者,如果 kindMemory

Field Type 描述
type memory_type 导入内存的类型

或者,如果 kindGlobal

Field Type 描述
type global_type 导入的全局变量的类型

请注意,在 MVP 中,只能导入不可变全局变量。

功能部分

函数部分_声明_是模块中所有函数的签名(它们的定义显示在中代码部分)。

Field Type 描述
count varuint32 要跟随的签名索引的计数
types varuint32* 类型部分中的索引序列

表节

A表节的编码:

Field Type 描述
count varuint32 指示模块定义的表数
entries table_type* 所述above的重复 table_type 条目

在 MVP 中,表的数量必须不超过 1。

存储器部分

身份证: memory

A存储器部分的编码:

Field Type 描述
count varuint32 表示模块定义的存储器数量
entries memory_type* 所述above的重复 memory_type 条目

请注意,初始/最大值字段以单位WebAssembly 页面指定。

在 MVP 中,记忆数量必须不超过 1。

全球部分

的编码全球部分

Field Type 描述
count varuint32 全局变量条目计数
globals global_variable* 全局变量,如下所述

全局条目

每个 global_variable 声明一个给定类型、可变性和给定初始值设定项的单个全局变量。

Field Type 描述
type global_type 变量的类型
init init_expr 全局的初始值

请注意,在 MVP 中,只能导出不可变的全局变量。

导出部分

的编码导出部分

Field Type 描述
count varuint32 要跟随的导出条目的计数
entries export_entry* 如下所述的重复导出条目

出口进口

Field Type 描述
field_len varuint32 长度( field_str 单位:字节)
field_str bytes 字段名:有效的 UTF-8 字节序列
kind external_kind 要导出的定义的种类
index varuint32 索引到相应索引空间

例如,如果“种类”是 Function,则“索引”是功能索引。请注意,在 MVP 中,内存或表导出的唯一有效索引值是 0。

开始部分

开始部分声明启动功能

Field Type 描述
index varuint32 开始功能索引

元素部分

的编码元素部分

Field Type 描述
count varuint32 要跟随的元素段数
entries elem_segment* 重复的元素片段如下所述

A elem_segment 为:

Field Type 描述
index varuint32 表索引(MVP 中的 0)
offset init_expr i32 初始化器表达式,用于计算放置元素的偏移量
num_elem varuint32 要遵循的元素数量
elems varuint32* 功能指标顺序

代码部分

身份证: code

代码部分包含模块中每个函数的主体。本节中定义的功能部分和函数体中声明的函数计数必须相同,并且 i 第个声明与 i 第个函数体相对应。

Field Type 描述
count varuint32 要跟随的函数体的计数
bodies function_body* 函数体顺序

数据节

数据段声明加载到线性内存中的初始化数据。

Field Type 描述
count varuint32 要跟随的数据段的计数
entries data_segment* 重复的数据段如下所述

A data_segment 为:

Field Type 描述
index varuint32 线性记忆指数(MVP 中的 0)
offset init_expr i32 初始化器表达式,用于计算放置数据的偏移量
size varuint32 大小 data(以字节为单位)
data bytes 字节序列 size

名称节

自定义部分 name 字段: "name"

名称部分是一个自定义部分。因此,它使用 ID 0 后跟名称字符串 "name" 进行编码。与所有自定义节一样,此节格式错误不会导致模块验证失败。这取决于实现如何处理格式错误或部分格式错误的名称节。在模块实例化之后,如果需要调试,WebAssembly 实现也可以自由选择延迟读取和处理此部分。

名称部分可能只出现一次,并且只出现在数据节。期望在浏览器或其他开发环境中查看二进制 WebAssembly 模块时,此部分中的数据将用作中文本格式的函数和局部变量的名称。

“名称”部分包含一系列名称子部分:

Field Type 描述
name_type varuint7 识别本小节中包含的名称类型的代码
name_payload_len varuint32 此子部分的大小(字节)
name_payload_data bytes 本节内容,长度 name_payload_len

由于名称子部分具有给定的长度,因此引擎可以跳过未知或不需要的子部分。当前有效 name_type 代码列表为:

Name Type Code 描述
Module 0 为模块指定名称
Function 1 为函数指定名称
Local 2 为函数中的局部变量指定名称

出现时,子节必须按此顺序出现,且最多出现一次。最后一个子段的结尾必须与名称段的最后一个字节一致,才能成为格式良好的名称段。

模块名称

模块名称子部分为模块本身指定一个名称。它只包含一个字符串:

Field Type 描述
name_len varuint32 长度( name_str 单位:字节)
name_str bytes 名称的 UTF-8 编码

名称映射

在以下小节中,A name_map 编码为:

Field Type 描述
count varuint32 IN 名称的 naming 数量
names naming* 按索引排序的 naming 序列

其中 A naming 编码为:

Field Type 描述
index varuint32 正在命名的索引
name_len varuint32 长度( name_str 单位:字节)
name_str bytes 名称的 UTF-8 编码

函数名称

函数名称子部分是 name_map 将名称分配给的函数索引空间子集(导入和模块定义)。

每个函数最多只能命名一次。多次命名函数会导致节的格式不正确。

但是,名称不必是唯一的。可以为多个函数指定相同的名称。这在 C++ 程序中很常见,其中组成二进制文件的多个编译单元可以包含具有相同名称的本地函数。

本地名称

局部名称子部分 name_map 将 s 分配给中函数索引空间的函数子集(导入和模块定义)。 name_map 对于给定的函数,将名称分配给局部变量索引的子集。

Field Type 描述
count varuint32 函数中的 local_names 计数
funcs local_names* 按索引排序的 local_names 序列

其中 A local_name 编码为:

Field Type 描述
index varuint32 正在命名其局部变量的函数的索引
local_map name_map 将名称分配给本地索引

函数体

函数体由一系列局部变量声明组成,后面跟有字节码指令。根据下表的定义,指令编码为opcode后跟零或更多立即。每个函数体必须以 end 操作码结束。

Field Type 描述
body_size varuint32 要跟随的函数体的大小(以字节为单位)
local_count varuint32 本地条目数
locals local_entry* 局部变量
code byte* 函数的字节码
end byte 0x0b,表示主体的末端

本地条目

每个局部条目声明给定类型的多个局部变量。拥有多个相同类型的条目是合法的。

Field Type 描述
count varuint32 以下类型的局部变量的数目
type value_type 变量的类型

控制流操作符(在这里描述

Name Opcode Immediates 描述
unreachable 0x00 立即诱捕
nop 0x01 无操作
block 0x02 sig : block_type 开始一个表达式序列,产生 0 或 1 值
loop 0x03 sig : block_type 开始也可以形成控制流循环的块
if 0x04 sig : block_type 开始 If 表达式
else 0x05 开始 If 的 Else 表达式
end 0x0b 结束块、循环或 if
br 0x0c relative_depth : varuint32 以外部嵌套块为目标的打断
br_if 0x0d relative_depth : varuint32 以外部嵌套块为目标的条件中断
br_table 0x0e see below 分支表控制流构造
return 0x0f 从该函数返回零或一个值

AND if 运算符的 block _西格_字段指定函数签名,这些签名描述它们对操作数堆栈的使用。

br_table 运算符有一个立即数操作数,其编码如下:

Field Type 描述
target_count varuint32 目标 _ 表中的条目数
target_table varuint32* 指示要中断到的外部块或循环的目标条目
default_target varuint32 在默认情况下要中断到的外部块或循环

br_table 操作符实现了一个间接分支。它接受一个可选的值参数(像其他分支一样)和一个附加 i32 的表达式作为输入,并在给定的偏移量处分支到块或循环 target_table。如果输入值超出范围,则 br_table 分支到默认目标。

注意:这里和其他地方的操作码空间中的间隙是为 Future:unicorn: 扩展保留的。

调用操作员(在这里描述

Name Opcode Immediates 描述
call 0x10 function_index : varuint32 通过其index调用函数
call_indirect 0x11 type_index : varuint32, reserved : varuint1 使用预期的签名间接调用函数

call_indirect 运算符接受一系列函数参数,并将表中的索引作为最后一个操作数。其 reserved 立即值供 Future:unicorn: 使用,并且必须 0 在 MVP 中。

参数运算符(在这里描述

Name Opcode Immediates 描述
drop 0x1a 忽略值
select 0x1b 根据条件选择两个值之一

变量访问(在这里描述

Name Opcode Immediates 描述
get_local 0x20 local_index : varuint32 读取局部变量或参数
set_local 0x21 local_index : varuint32 编写局部变量或参数
tee_local 0x22 local_index : varuint32 编写局部变量或参数并返回相同的值
get_global 0x23 全球 _ 指数: varuint32 read a global variable
set_global 0x24 全球 _ 指数: varuint32 write a global variable

与内存相关的运算符(在这里描述

Name Opcode Immediate 描述
i32.load 0x28 memory_immediate load from memory
i64.load 0x29 memory_immediate load from memory
f32.load 0x2a memory_immediate load from memory
f64.load 0x2b memory_immediate load from memory
i32.load8_s 0x2c memory_immediate load from memory
i32.load8_u 0x2d memory_immediate load from memory
i32.load16_s 0x2e memory_immediate load from memory
i32.load16_u 0x2f memory_immediate load from memory
i64.load8_s 0x30 memory_immediate load from memory
i64.load8_u 0x31 memory_immediate load from memory
i64.load16_s 0x32 memory_immediate load from memory
i64.load16_u 0x33 memory_immediate load from memory
i64.load32_s 0x34 memory_immediate load from memory
i64.load32_u 0x35 memory_immediate load from memory
i32.store 0x36 memory_immediate store to memory
i64.store 0x37 memory_immediate store to memory
f32.store 0x38 memory_immediate store to memory
f64.store 0x39 memory_immediate store to memory
i32.store8 0x3a memory_immediate store to memory
i32.store16 0x3b memory_immediate store to memory
i64.store8 0x3c memory_immediate store to memory
i64.store16 0x3d memory_immediate store to memory
i64.store32 0x3e memory_immediate store to memory
current_memory 0x3f reserved : varuint1 查询内存大小
grow_memory 0x40 reserved : varuint1 增加内存大小

memory_immediate 类型编码如下:

Name Type 描述
flags varuint32 当前包含最低有效位对齐的位域,编码为 log2(alignment)
offset varuint32 偏移的值

正如编码所暗示的 log2(alignment),比对必须是 2 的幂。作为额外的验证标准,比对必须小于或等于自然比对。最低有效位之后 log(memory-access-size) 的位必须设置为 0. 这些位保留给 [未来:unicorn:][未来线程] 使用(例如,用于共享内存排序要求)。

current_memory AND grow_memory 运算符的 reserved 立即值供 Future:unicorn: 使用,并且在 MVP 中必须为 0。

常量(在这里描述

Name Opcode Immediates 描述
i32.const 0x41 value : varint32 常数值解释为 i32
i64.const 0x42 value : varint64 常数值解释为 i64
f32.const 0x43 value : uint32 常数值解释为 f32
f64.const 0x44 value : uint64 常数值解释为 f64

比较运算符(在这里描述

Name Opcode Immediate 描述
i32.eqz 0x45
i32.eq 0x46
i32.ne 0x47
i32.lt_s 0x48
i32.lt_u 0x49
i32.gt_s 0x4a
i32.gt_u 0x4b
i32.le_s 0x4c
i32.le_u 0x4d
i32.ge_s 0x4e
i32.ge_u 0x4f
i64.eqz 0x50
i64.eq 0x51
i64.ne 0x52
i64.lt_s 0x53
i64.lt_u 0x54
i64.gt_s 0x55
i64.gt_u 0x56
i64.le_s 0x57
i64.le_u 0x58
i64.ge_s 0x59
i64.ge_u 0x5a
f32.eq 0x5b
f32.ne 0x5c
f32.lt 0x5d
f32.gt 0x5e
f32.le 0x5f
f32.ge 0x60
f64.eq 0x61
f64.ne 0x62
f64.lt 0x63
f64.gt 0x64
f64.le 0x65
f64.ge 0x66

数值运算符(在这里描述

Name Opcode Immediate 描述
i32.clz 0x67
i32.ctz 0x68
i32.popcnt 0x69
i32.add 0x6a
i32.sub 0x6b
i32.mul 0x6c
i32.div_s 0x6d
i32.div_u 0x6e
i32.rem_s 0x6f
i32.rem_u 0x70
i32.and 0x71
i32.or 0x72
i32.xor 0x73
i32.shl 0x74
i32.shr_s 0x75
i32.shr_u 0x76
i32.rotl 0x77
i32.rotr 0x78
i64.clz 0x79
i64.ctz 0x7a
i64.popcnt 0x7b
i64.add 0x7c
i64.sub 0x7d
i64.mul 0x7e
i64.div_s 0x7f
i64.div_u 0x80
i64.rem_s 0x81
i64.rem_u 0x82
i64.and 0x83
i64.or 0x84
i64.xor 0x85
i64.shl 0x86
i64.shr_s 0x87
i64.shr_u 0x88
i64.rotl 0x89
i64.rotr 0x8a
f32.abs 0x8b
f32.neg 0x8c
f32.ceil 0x8d
f32.floor 0x8e
f32.trunc 0x8f
f32.nearest 0x90
f32.sqrt 0x91
f32.add 0x92
f32.sub 0x93
f32.mul 0x94
f32.div 0x95
f32.min 0x96
f32.max 0x97
f32.copysign 0x98
f64.abs 0x99
f64.neg 0x9a
f64.ceil 0x9b
f64.floor 0x9c
f64.trunc 0x9d
f64.nearest 0x9e
f64.sqrt 0x9f
f64.add 0xa0
f64.sub 0xa1
f64.mul 0xa2
f64.div 0xa3
f64.min 0xa4
f64.max 0xa5
f64.copysign 0xa6

转换(在这里描述

Name Opcode Immediate 描述
i32.wrap/i64 0xa7
i32.trunc_s/f32 0xa8
i32.trunc_u/f32 0xa9
i32.trunc_s/f64 0xaa
i32.trunc_u/f64 0xab
i64.extend_s/i32 0xac
i64.extend_u/i32 0xad
i64.trunc_s/f32 0xae
i64.trunc_u/f32 0xaf
i64.trunc_s/f64 0xb0
i64.trunc_u/f64 0xb1
f32.convert_s/i32 0xb2
f32.convert_u/i32 0xb3
f32.convert_s/i64 0xb4
f32.convert_u/i64 0xb5
f32.demote/f64 0xb6
f64.convert_s/i32 0xb7
f64.convert_u/i32 0xb8
f64.convert_s/i64 0xb9
f64.convert_u/i64 0xba
f64.promote/f32 0xbb

重新解释(在这里描述

Name Opcode Immediate 描述
i32.reinterpret/f32 0xbc
i64.reinterpret/f64 0xbd
f32.reinterpret/i32 0xbe
f64.reinterpret/i64 0xbf