一个适合放在一条推文中的 WebAssembly 编译器

这是一个将逆波兰表达式编译为 WebAssembly 模块的 JavaScript 代码及其逐步解释:

  • 初始代码与介绍:最初的代码仅 192 字节,能将算术表达式编译为 WebAssembly 模块,通过WebAssembly.instantiate实例化并导出结果,可在stackblitz.com/edit/rpn-to-wasm-js-compiler中体验。
  • 格式化代码:将代码格式化为更易读的形式,虽仍较难理解,但可识别不同部分。
  • 去除赋值表达式:通过将函数体转为块,将赋值放在单独行,避免使用花括号、分号和返回语句,以节省字节。
  • 还原变量技巧:用更具描述性的名字替代单字母变量,引入新的instrs变量代替b,并移除不必要的函数参数l,节省字节。
  • 添加缺失的零:利用稀疏数组语法节省字节,将数组中的逗号后添加零,使数组元素转换为数字时,空项转换为 0。
  • 移除长度定义上的额外 4 字节:避免重复写b.length,通过计算instrs.length + 4得到长度并在不同位置使用,节省字节。
  • 用函数调用替代字符串模板字面量:利用字符串模板字面量的标记模板功能,将code.split改为函数调用,节省括号。
  • 移除三元运算符:将三元运算符替换为if语句,避免使用return语句,使代码更易读。
  • 移除带强制转换的数字检查:显式编写解析和检查逻辑,用parseIntNumber.isFinite检查数字,允许输入负数。
  • 移除indexOf -1技巧:通过创建对象将算术操作符映射到字节码,避免使用indexOf -1技巧,改变了代码的语义。
  • 移除indexOf技巧:完全移除indexOf技巧,使用创建的对象进行映射,保持初始语义。
  • 移除空的导出名称:为导出函数指定名称a,节省字节,并使用新名称调用函数。
  • 隐式设计决策:编译器初始实现仅支持正整数,数字采用 LEB128 编码算法,长度限制为 7 位,超过则模块验证失败。
  • 一个几乎成功的技巧:曾有想法简化代码,但因字符在 UTF-8 和 WebAssembly 字节码中的顺序不同而失败。
  • 解释数组中的数字:详细解释用于构建 WebAssembly 模块的数组中的每个数字的含义,包括魔数、版本、各种节的标识和大小等。
  • 结论:将 192 字节的代码转化为较易读的形式,了解了 WebAssembly 的一些知识,若去掉大小限制,可在编译器中进行更多改进,如处理更大数字、添加语法支持等,可参考WebAssembly from the Ground Up。特别感谢lexi的贡献。
阅读 8
0 条评论