Skip to content

Latest commit

 

History

History
66 lines (47 loc) · 4.29 KB

FeatureTest.md

File metadata and controls

66 lines (47 loc) · 4.29 KB

有关激励方案,请参见rationale

特征测试

[后 MVP:unicorn:][未来通用],应用程序将能够通过 [ has_feature 或类似的 API:unicorn:][未来功能测试] 查询支持哪些功能。这解释了不同引擎在不同时间以不同顺序交付功能的实际情况。

下面是这样一个特性测试功能的草图。

由于某些 WebAssembly 功能添加了运算符,并且模块中的所有 WebAssembly 代码都是提前验证的,因此通常的 JavaScript 功能检测模式:

if (foo)
    foo();
else
    alternativeToFoo();

在 WebAssembly 中不起作用(如果 foo 不支持, foo() 将无法验证)。

相反,应用程序可以使用以下策略之一:

  1. 编译一个模块的多个版本,每个版本假定不同的功能支持,并使用 has_feature 测试来确定要加载哪个版本。

  2. 在期间“特定”层解码(将在 MVP无论如何的用户代码中发生),使用 has_feature 确定支持哪些功能,然后将不支持的功能使用转换为多边形填充或陷印。

这两个选项都可以由工具链自动提供,并由编译器标志控制。因为 has_feature 是一个常量表达式,所以 WebAssembly 引擎可以对其进行常量折叠。

为了说明,考虑 4 个例子:

  • i32.min_s🦄-策略 2 可用于转换 (i32.min_s lhs rhs) 为等效表达式,该表达式 lhs 将 AND rhs 存储在局部变量中,然后使用 i32.lt_s AND select
  • Threads:unicorn:-如果应用程序广泛使用 #ifdef 以生成启用/禁用线程的构建,则策略 1 将是合适的。但是,如果应用程序能够将线程的使用抽象为几个原语,则可以使用策略 2 来修补正确的原语实现。
  • mprotect🦄-如果引擎不能使用操作系统信号处理来有效实现 mprotect,则 mprotect 可能成为永久可选功能。因为它的使用 mprotect 不是正确性所必需的(而只是捕捉错误), mprotect 可以替换为 nop。如果 mprotect 对于正确性是必要的,但存在不依赖 mprotect 的替代策略, mprotect 则可以替换为 abort() 调用,依赖于要测试 (has_feature "mprotect") 的应用程序以避免调用 abort()has_feature 查询可以通过现有 __builtin_cpu_supports 的向 C++ 代码公开。
  • SIMD-当 SIMD 运算符具有足够好的 polyfill(例如 f32x4.fma via f32x4.mul/ add)时,可以使用策略 2(类似于上面的 i32.min_s 示例)。然而,当 SIMD 特征不具有有效的 polyfill(例如, f64x2 其引入两种运算符类型)时,需要在加载时提供并选择替代算法。

作为填充 SIMD f64x2 特性的一个假设(未实现)示例,C++ 编译器可以提供一个新的 Function 属性,该属性指示一个函数是另一个函数的优化但依赖于特性的版本(类似于 [ ifunc Attribute](https://gcc.gnu.org/onlinedocs/gcc-4.7.2/gcc/function-attributes.html#index-g_t_0040code_007bifunc_007d-attribute-2529),但没有回调):

#include <xmmintrin.h>
void foo(...) {
  __m128 x, y;           // -> f32x4 locals
  ...
  x = _mm_add_ps(x, y);  // -> f32x4.add
  ...
}
void foo_f64x2(...) __attribute__((optimizes("foo","f64x2"))) {
  __m256 x, y;           // -> f64x2 locals
  ...
  x = _m_add_pd(x, y);   // -> f64x2.add
  ...
}
...
foo(...);                 // calls either foo or foo_f64x2

在此示例中,工具链可以发出 foofoo_f64x2 作为“特定层”二进制格式的函数定义。然后,加载时间 polyfill 将替换 foofoo_f64x2 if (has_feature "f64x2")。许多其他策略可以允许更细或更粗的粒度替换。由于这一切都在用户空间中,策略可以随着时间的推移而发展。

另请参阅 [更好的功能测试支持:unicorn:][未来功能测试] 未来功能。