这些功能在 WebAssembly 的上下文中高层次目标是有意义的,但不是初始最小可行产品版本的一部分。
**注意:**我们正在将所有后 MVP 功能迁移到跟踪问题。
特征 | Tracking issue | Status |
---|---|---|
规格 | 1077 | in progress |
螺纹 | 1073 | in progress |
固定宽度 SIMD | 1075 | in progress |
异常处理 | 1078 | in progress |
垃圾收集 | 1079 | in progress |
ECMAScript 模块集成 | 1087 | no started |
**注意:**这些将很快转移到跟踪问题。
⭐ = 我们希望在之后MVP不久优先添加的基本功能。
这将在tooling一节中介绍。
在 MVP 之后,需要进行某种形式的功能测试。我们还没有编写多边形填充的经验,不知道它是否 has_feature
是正确的原始构建块,所以在我们获得这种经验之前,我们不会定义它(或其他东西)。在此期间,只需 eval
输入 WebAssembly 代码并捕捉验证错误,就可以进行粗略的功能测试(就像人们在 JavaScript 中所做的那样)。
有关更详细的草图,请参见特征测试。
提供对安全操作系统提供的功能的访问,包括:
map_file(addr, length, Blob, file-offset)
:语义上,此运算符 将指定的范围从Blob
复制到范围[addr, addr+length)
(其中addr+length <= memory_size
),但鼓励mmap(addr, length, MAP_FIXED | MAP_PRIVATE, fd)
实现discard(addr, length)
:从语义上讲,此运算符将给定范围归零 但是鼓励实现从进程的工作集中删除被置零的物理页面(例如,通过调用madvise(MADV_DONTNEED)
POSIX)。shmem_create(length)
:创建可在多个线性内存之间同时共享的内存对象map_shmem(addr, length, shmem, shmem-offset)
:LIKEmap_file
EXCEPTMAP_SHARED
,它在只读 BLOB 上无效mprotect(addr, length, prot-flags)
:更改范围[addr, addr+length)
上的保护(其中addr+length <= memory_size
)decommit(addr, length)
:相当于mprotect(addr, length, PROT_NONE)
后跟discard(addr, length)
,并且可能比按顺序执行这些运算符更有效。
上面的 addr
和 length
参数需要是 [ page_size
] 的倍数(semantics.MD#resizing)。
mprotect
操作员将需要硬件存储器保护来有效地执行,并且因此可以作为“可选”特征(需要特征测试使用)来添加。即使在没有硬件内存保护可用的情况下,为了支持高效执行,也可以添加受限形式 mprotect
的,它是静态声明的,并且仅保护低内存(提供本机 C/C++ 应用程序的预期低内存故障行为)。
上面的功能列表主要涵盖了 mmap
OS 原语提供的功能集。一个显著的例外是 mmap
可以分配不连续的虚拟地址范围。有关基本原理,请参见FAQ。
一些平台提供了对 16GiB 内存页面的支持,这可以在某些情况下提高内存管理的效率。WebAssembly 可以为程序提供选项,以指定比更大的default页面大小。
某些类型的控制流(尤其是不可简化的和间接的)无法在 WebAssembly 中以最大效率表达,除非 Relooper 进行模式化输出并跳线在引擎中进行优化。更具表现力的控制流的目标用途是:
- 语言解释器,通常使用计算机-
goto
。 - 函数式语言支持,其中保证了尾部调用优化的正确性和性能。
正在考虑的备选方案:
- 没有动作,
while
switch
结合跳线就足够了。 - 只需添加
goto
(直接和间接)。 - 添加处理常见模式的新控制流原语。
- 添加受签名限制的正确尾调用。
- 添加适当的尾部调用,扩展受签名限制的适当尾部调用,并使其更容易支持其他语言,特别是函数式编程语言。
看吧GC.md。
WebAssembly MVP 将支持 WebAssembly 的 WASM32 模式,使用 32 位线性内存索引,线性内存大小高达 4 GiB.为了支持更大的大小,将来将添加 WebAssembly 的 WASM64 模式,使用 64 位线性内存索引支持更大的线性内存大小。WASM32 和 WASM64 都只是 WebAssembly 的模式,由模块头中的标志选择,并不意味着线性内存处理方式之外的任何语义差异。平台还将具有用于查询支持 WASM32 和 WASM64 中的哪一个的 API.
当然,实际分配这么多内存的能力始终取决于动态资源可用性。
WASM64 最初可能只支持 64 位线性内存索引,而 WASM32 将不支持 64 位线性内存索引,因此实现不必在同一实例中支持多个索引大小。然而,具有 32 位索引的操作符和具有 64 位索引的操作符将被赋予单独的名称,以保留将来在同一实例中支持这两种操作符的可能性。
- 添加新的源映射模块部分类型。
- 要么直接嵌入源地图,要么只嵌入一个可以下载源地图的 URL.
- 即使对于中等大小的编译代码,文本源映射也会变得非常大,因此可能需要为源映射定义新的二进制格式。
- 孕育想法并开始讨论源映射 RFC 存储库
协程将[eventually be part of C++][]在 WebAssembly 支持的其他编程语言中流行。
[最终成为 C++ 的一部分]:http://wg21.link/n4499
有关签名限制的正确尾调用(PTC)的完整说明,请参见[asm.js RFC][]。
签名受限 PTC 的有用属性:
-
在大多数情况下,可以编译为单个跳转。
-
可以通过函数指针调用间接
goto
表示。 -
可以用作具有不受限制的 PTC 的语言的编译目标;代码生成器可以使用线性存储器中的堆栈来在签名受限的 PTC 之上有效地实现定制调用 ABI.
-
希望执行积极优化的引擎可以将 PTC 的图形融合到单个函数中。
-
为了减少编译时间,代码生成器可以使用 PTC 以较低的开销将超大函数分解为较小的函数。
-
编译器可以通过对 PTC 签名中的参数进行排序来对寄存器分配施加一定程度的控制。
[ASM.JS RFC]:http://discourse.specifiction.org/t/request-for-comments-add-a-restricted-subset-of-proper-tail-calls-to-asm-js
通用适当的尾调用将没有签名限制,因此比签名限制的正确尾调用更广泛可用,尽管会有一些不同的性能特征。
托多
初始 SIMD API 将是一个“短 SIMD ”API,以固定宽度的 128 位类型和显式 SIMD 运算符为中心。这是相当便携和有用的,但它不能提供当今一些流行硬件的全部性能。有[a proposal in the SIMD.js repository][]一个“长 SIMD ”模型,它可以推广到更宽的硬件矢量长度,更自然地使用高级功能,如矢量车道预测、聚集/分散等。对这种模型提出的有趣问题包括:
-
这个模型将如何映射到流行的现代 SIMD 硬件架构?
-
此模型与其他硬件并行功能(如 GPU 和具有共享内存的线程)的关系是什么?
-
如何在高级编程语言中使用此模型?例如,C++ 委员会正在考虑各种可能的方法;模型可能支持它们中的哪一个?
-
与“短 SIMD ”API 有什么关系?“没有”可能是一个可以接受的答案,但这是需要考虑的事情。
-
该模型在整个平台中引入了哪些不确定性?
-
当代码在不支持它的硬件平台上使用长 SIMD 时,会发生什么?合理的选择可能包括在没有硬件加速优势的情况下模拟它,或者通过功能测试表明缺乏支持。
[SIMD.JS 仓库中的提议]:tc39/ecmascript_simd#180
WebAssembly 是一个新的虚拟 ISA,因此应用程序不能简单地重用其现有的 JIT 编译器后端。相反,应用程序必须与 WebAssembly 的指令接口,就像它们是一个新的 ISA 一样。
应用程序需要各种各样的 JIT 编译功能。WebAssembly 应支持:
- 生成动态库并加载到当前 WebAssembly 模块中。
- 定义轻量级机制,例如向现有模块添加函数的能力。
- 支持函数中的显式可修补构造,以允许非常细粒度的 JIT 编译。这包括:
- 多态内联缓存的代码修补
- 调用 Patching 将 JIT 编译的函数链接在一起;
- 函数中的临时停止插入,用于在函数启动时进行捕获 在 JIT 编译器的运行时执行对该函数有危险的运算符时执行。
- 为其 JIT 编译的代码提供对配置文件反馈的 JIT 访问。
- 代码卸载功能,特别是在代码垃圾收集和碎片整理的上下文中。
WebAssembly 的 JIT 接口可能相当低级。但是,也有高级功能和优化的用例。解决这些使用情形的一个途径是JIT 和优化库。
vfork
。- 进程间通信。
- 进程间
mmap
。
目前,当指令捕获时,程序立即终止。这适用于 C/C++ 代码,其中捕获条件指示源代码级别的未定义行为,并且它也适用于手写代码,其中捕获条件通常指示被要求在其支持范围之外执行的指令。但是,当前的工具没有涵盖一些有趣的用例:
- 并非所有可能的错误条件都包括在内。例如,如果有一个在溢出时捕获的有符号整数 ADD,那就太好了。这样的构造会给当今流行的硬件体系结构增加太多的开销,但在某些情况下仍然有用。
- 一些高级语言为被零除等条件定义了自己的语义。编译器可以添加显式检查并手动处理此类情况,但平台提供的更直接的支持可能具有以下优势:
- 某些运算符的非陷印版本(如返回零而不是在被零除时陷印的整数除法指令)在某些平台上可能运行得更快。
- 以某种方式从陷阱中优雅地恢复的能力可以使许多事情成为可能。这可能涉及抛出,或者可能通过在执行状态改变的情况下在捕获指令处恢复执行,如果有合理的方法来指定如何工作的话。
-
下面的操作符可以从已经存在的其他操作符构建,但是在这样做的时候,它们多次读取至少一个非常量输入,打破了单一使用的表达式树结构。
i32.min_s
:有符号最小值i32.max_s
:有符号最大值i32.min_u
:无符号最小值i32.max_u
:无符号最大值i32.sext
:符号不可知sext(x, y)
的是shr_s(shl(x,y),y)
i32.abs_s
:有符号绝对值(陷印开启INT32_MIN
)i32.bswap
:与符号无关的反向字节(字节序转换)i32.bswap16
:符号不可知,bswap16(x)
IS((x>>8)&255)|((x&255)<<8)
-
下面的操作符可能很有趣。
i32.clrs
:与符号无关的计数前导冗余符号位(为所有值定义,包括 0)i32.floor_div_s
:带符号的除法(结果为floored)
-
下面的 64 位操作符可能也很有趣。
i64.mor
:符号不可知8x8 位矩阵乘以或i64.mxor
:符号不可知8x8 位矩阵乘以 XOR
f32.minnum
:最小;如果正好有一个操作数是 Nan,则返回另一个操作数f32.maxnum
:最大;如果正好有一个操作数是 Nan,则返回另一个操作数f32.fma
:融合乘法-加法(结果始终符合 IEEE 754-2008)f64.minnum
:最小;如果正好有一个操作数是 Nan,则返回另一个操作数f64.maxnum
:最大;如果正好有一个操作数是 Nan,则返回另一个操作数f64.fma
:融合乘法-加法(结果始终符合 IEEE 754-2008)
minnum
并且 maxnum
操作符将被视为 -0.0
有效地小于 0.0
。此外,建议遵循 IEEE 754-2018 草案,该草案删除了 IEEE 754-2008 的 minNum
AND maxNum
(当任一操作数为 SNaN 时返回 QNaN),并将其替换为 minimumNumber
AND maximumNumber
,即使一个操作数为 SNaN,AND 也更倾向于返回一个数字。
请注意,某些操作符(如 fma
)可能不可用,或者可能无法在所有平台上很好地执行。这些应由功能测试保护,以便在可用的情况下,它们的行为保持一致。
f32.reciprocal_approximation
:倒数近似f64.reciprocal_approximation
:倒数近似f32.reciprocal_sqrt_approximation
:倒数 SQRT 近似f64.reciprocal_sqrt_approximation
:倒数 SQRT 近似
这些运营商不需要完全精确,但具体细节需要澄清。
对于 16 位浮点支持,将该功能分为两部分可能是有意义的:仅支持 16 位和 32 位或 64 位格式之间的转换(可能合并到加载和存储操作符中),以及完全支持实际的 16 位算术。
128 位是一个有趣的问题,因为硬件对它的支持非常罕见,所以它通常会通过软件模拟来实现,所以没有什么可以阻止 WebAssembly 应用程序链接到适当的模拟库并获得类似的性能结果。仿真库将具有更大的灵活性来提供近似技术,如双倍算术。如果我们在 WebAssembly 中标准化 128 位浮点,它可能是标准的 IEEE 754-2008 四倍精度。
WebAssembly 浮点在大多数方面符合 IEEE 754-2008,但也有一些方面符合尚未覆盖。
要支持异常和备用舍入模式,一个选项是为 add
、 sub
、 mul
、 div
、 sqrt
和 fma
定义备用形式。这些替代形式将具有用于舍入模式、掩码陷阱和旧标志的额外操作数,以及用于新标志值的额外结果。这些操作符相当冗长,但预计它们的用例将是专门化的。这种方法的优点是不向应用程序公开全局(即使只是每个线程)控制和状态寄存器,并避免给公共操作符带来副作用的可能性。
调试技术也很重要,但它们不一定需要在规范本身中。欢迎(并鼓励)实现支持仅从开发人员工具启用的非标准执行模式,例如具有交替舍入的模式,或以更高精度计算浮点运算符,以支持检测数值不稳定性的技术,或使用交替 NaN 位模式规则的模式,以携带诊断信息并帮助开发人员跟踪 Nan 的来源。
为了帮助开发人员找到浮点异常的来源,实现可能希望提供一种模式,其中使用包含标识符的有效负载来生成 NaN 值,以帮助程序员定位 Nan 首次出现的位置。另一种选择是提供另一种非标准执行模式,仅从开发人员工具启用,这将在选定的浮点异常上启用陷阱,但应小心,因为并非所有浮点异常都表示错误。
许多流行的 CPU 在处理低于正常值时具有显著的停顿,并且支持将低于正常值刷新为零的模式,以避免这些停顿。而且,ARMv7 Neon 不支持低于正常值,并且总是刷新它们。在 WebAssembly 中,浮点计算将次法线刷新为零的模式将解决这两个问题。
这里有两种不同的用例,一种是应用程序希望在本地处理溢出,另一种则不希望。
当应用程序准备在本地处理溢出时,使用算术运算符来指示何时发生溢出将非常有用。这方面的一个例子是编译器(如clang和GCC)中可用的检查算术内置。如果 WebAssembly 支持具有多个返回值的节点,则可以使用它来代替传递指针。
还有一些应用程序不希望在本地处理溢出的用例。一系列示例包括实现优化的 BigNum 算法,或优化 JavaScript 数字以使用 Int32 运算符。另一类包括编译代码,这些代码不希望发生溢出,但希望检测到溢出并在发生溢出时进行报告。理想情况下,这些用例希望有溢出陷阱,并允许它们handle trap specially。遵循显式有符号和无符号运算符在结果值无法在结果类型中表示时进行捕获的规则,可以添加整数 add
、 sub
和 mul
的显式有符号和无符号版本,这将在溢出时进行捕获。我们还没有添加它们的主要原因是,它们在当今几种流行的硬件架构上的通用使用效率不高。
MVP 功能测试情况可以通过允许未知/不支持的指令进行解码和验证来改进。这些未知指令的运行时语义可以是捕获或调用相同签名模块定义的 polyfill 函数。此功能可以提供加载时多边形填充(中的FeatureTest.MD方法 2)的轻量级替代方案,特别是如果特定层要标准化并在本机执行,则不需要用户空间转换过程。
如果允许数组类型的全局变量,则可以将内存的重要部分移出线性内存,这可以减少碎片问题。像 Fortran 这样限制别名的语言将是一个用例。C/C++ 编译器还可以确定某些全局变量从未使用过它们的地址。
WebAssembly 基于堆栈的特性使其能够支持来自块/函数的多个返回值。
MVP 将模块限制为最多一个存储器和最多一个表(缺省表),并且只有用于访问缺省表和存储器的操作符。
在添加 MVP 和 AFTER 之后GC 引用类型,可以放宽默认限制,以便可以导入或内部定义任意数量的表和内存,并且内存/表可以作为参数、返回值和局部变量传递。然后将添加、 store
和 call_indirect
的 load
新变体,这些变体采用额外的内存/表引用操作数。
要访问导入的或内部定义的非默认表或内存,可以添加一个新的 address_of
操作符,给定一个索引 IMMEDIATE,它将返回一级引用。除了表和内存之外,这还可以用于函数定义,以获得对函数的引用(由于不透明,可以实现为原始函数指针)。
在 MVP 中,WebAssembly 具有有限的操作tables功能,而主机环境可以执行更多操作(例如,请参阅 [JavaScript 的 WebAssembly.Table
API](JS.MD#WebAssemblyTable-Objects))。如果能够在 WebAssembly 中执行所有操作,则将非常有用,例如,可以在 WebAssembly 中编写 webassembly 动态加载器。作为先决条件,WebAssembly 需要对堆栈和局部变量的一流支持GC 参考文献。鉴于此,可增加以下内容:
get_table
/set_table
:获取或设置给定动态索引处的表元素;GOT/SET 值将具有 GC 引用类型grow_table
:增大当前表(直到可选的最大值),类似于grow_memory
current_table_length
:喜欢current_memory
。
此外,在 MVP 中,表的唯一允许的元素类型是通用的“AnyFunc ”类型,这仅仅意味着可以调用该元素,但没有静态签名验证检查。这可以通过允许以下操作来改进:
- 具有特定签名的函数,允许 WASM 生成器使用多个同构类型的函数表(而不是单个异构函数表),这消除了对异构表调用的隐含动态签名检查;
- 任何其他特定的 GC 引用类型,有效地允许 WebAssembly 代码实现各种根 API 方案。
复制和清除大内存区域是非常常见的,并且使这些操作快速依赖于体系结构。虽然这可以通过 i32.load
和 i32.store
在 MVP 中完成,但这需要更多的代码字节,并强制 VM 识别循环。可以添加以下运算符来提高性能:
- 将
move_memory
数据从源存储器区域复制到目标区域; 这些区域可以重叠:执行复制,就好像源区域首先被复制到临时缓冲区,然后临时缓冲区被复制到目标区域。 set_memory
:将内存区域中的所有字节设置为给定字节
我们预计 WebAssembly 生产者将在已知区域大小较大时使用这些操作,否则将使用加载/存储。
TODO:确定这些操作如何与共享内存交互。