我摸到了隐藏在 V8 背后的编程语言 Torque

介绍

当我在阅读“为什么 JavaScript 中的 Math.Pow 这么快?”这个问题时,我发现 V8 的内置函数使用了一种名为“Torque”的语言的描述。

一种只在程序语言处理系统中使用的程序语言,用于加速……我不禁感到了浪漫.让我们实际运行 Torque。

版本

V8:撰写本文时的主分支 (2022/9/18)

语言概览

Torque 用于描述 V8 内置函数的优化。似乎使用另一种语言而不是 C++ 的目的是提高逻辑的可读性。

V8 Torque 编程语言允许为 V8 项目做出贡献的开发人员专注于意图并表达他们对 VM 的更改,而不会陷入不相关的实现细节中。该语言旨在促进将 ECMAScript 规范直接转换为 V8 实现,同时以稳健的方式应用低级 V8 优化(基于特定对象类型测试的快速通过)。

V8 Torque 是一种语言,它允许开发人员为 V8 项目做出贡献,通过关注他们对 VM 的更改意图来表达 VM 中的更改,而不是专注于无关的实现细节。很容易将 ECMAScript 规范直接翻译成V8 中的一个实现,但功能强大到足以以稳健的方式表达低级 V8 优化技巧,例如基于特定对象形状的测试创建快速路径。

它是一种静态类型语言,语法被设计为更接近 TypeScript。

@export
macro PrintHelloWorld(): void {
  Print('Hello world!');
}

它还具有泛型和联合等丰富的类型操作,可以直接访问V8实现的内置类型。

环境设置

我按照参考。但是,它原本不应该单独运行,所以运行起来花费了很多时间。

既然麻烦,就转成Docker镜像。如果你喜欢,请使用它(小心,因为它是 12GB!)。

以下是设置环境以创建图像的过程。 (如果你想触摸它,请跳过它!)

设置概览

参考资料中的“入门”展示了如何将 HelloWorld 添加到测试用例中,因此请遵循此方法。

步骤大致如下。

克隆 V8,更新依赖模块 将要运行的进程添加到测试用例中,测试Torque源码 构建测试二进制文件 运行测试 克隆 V8

不幸的是,git clone 无法使用,需要专用工具(来自参考)。

第一的安装

git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
PATH=/depot_tools:$PATH
gclient

现在您可以使用 gclient 并克隆 v8。后面我们会用到 Python,所以我们把它放进去。

然后克隆 V8。运行 depot_tools fetch 而不是 git clone 。

cd /v8
fetch v8
cd v8
# 最初はdetached headにいるので、mainに移動してからpull
git checkout main && git pull
# 依存関係をアップデート
gclient sync
补丁测试功能及测试Torque源码

接下来,让我们让这次创建的 Torque 源代码可以从测试中执行。

首先,创建一个测试用例来执行本次添加的源代码。将其添加到test/cctest/torque/test-torque.cc 的测试用例列表的末尾。

测试/cctest/torque/test-torque.cc
// ...

namespace v8 {
namespace internal {
namespace compiler {

// ...

// 末尾のテストケースとして追記。 `Main` という名前の関数を実行するようにする
TEST(Main) {
  Isolate* isolate(CcTest::InitIsolateOnce());
  CodeAssemblerTester asm_tester(isolate, 0);
  TestTorqueAssembler m(asm_tester.state());
  {
    m.Main();
    m.Return(m.UndefinedConstant());
  }
  FunctionTester ft(asm_tester.GenerateCode(), 0);
  ft.Call();
}

}  // namespace compiler
}  // namespace internal
}  // namespace v8

然后将源代码本身添加到test/torque/test-torque.tq。这时候如果删除已有的代码,现有的测试用例会掉下来,构建会失败,所以要小心。

测试/扭矩/测试扭矩.tq
namespace test {

# 関数を追加
@export
macro Main(): void {
  Print('Hello world!');
}

// ... (既存のコード)
}

在Dockerfile中,代码是sed强行插入的。

# テストファイルのパッチ
# 内側のsedでパッチファイルの改行を"\n"に変換して1行表示
# その後外側のsedで "}  // namespace compiler" の行の直前に挿入
cat test-torque.cc | sed "/\}  \/\/ namespace compiler/i $(cat test-torque-patch.cc | sed -z 's/\n/\\n/g')" > test-torque.cc

# 同様に、tqファイルでも "namespace test {" の行の直後に挿入
cat test-torque.original.tq | sed "/namespace test {/a $(cat ${tq_file_path} | sed -z 's/\n/\\n/g')" > test-torque.tq

我第一次了解到 sed 不仅可以替换,还可以插入(sed //i 紧接在前面,sed //a 紧接在紧接之后)。

构建测试二进制文件

我已经修补了测试文件并构建了它(它是用 c++ 编写的,所以我不能按原样运行它)。用于构建的 V8 构建工具使用。参考中介绍了gm x64.debug.check方法,但推荐x64.debug.tests(只构建测试文件),因为这里也执行测试。

alias gm=/v8/v8/tools/dev/gm.py
# デバッグ用ツール一式ビルド
gm x64.debug.tests

(参考) gm 选项

$ gm -h
...
 - all (build all binaries)
 - tests (build test binaries)
 - check (build test binaries, run most tests)
 - checkall (build all binaries, run more tests)

如果构建成功,会在V8目录下生成out/x64.debug/cctest。

测试运行

执行“Patch Test Function and Torque Source Code for Test”中创建的测试用例Main。

# cctestの引数にテストケース名を指定すると単体で実行可能
root@3083bb2e4a26:/v8/v8# out/x64.debug/cctest test-torque/Main
Hello world!

执行成功

尝试运行 FizzBu​​zz

你终于准备好玩 Torque 了。一个人无法掌握HelloWorld的特点,所以写了FizzBu​​zz。

(如果您使用的是 VSCode,建议您通过插入使用语法高亮

// 分かりやすいようにMainと付けたが、エントリーポイントの概念はない
@export
macro Main(): void {
  for (let i: int32 = 1; i <= 30; i++) {
    FizzBuzz(i);
  }
}

macro FizzBuzz(n: int32): void {
  if (n % 15 == 0) {
    Print("fizzbuzz");
    return;
  }
  if (n % 3 == 0) {
    Print("fizz");
    return;
  }
  if (n % 5 == 0) {
    Print("buzz");
    return;
  }
  Print(Convert<Smi>(n));
}

到目前为止还没有特殊的语法,它变成了一个通用的(?)FizzBu​​zz。

但是,在处理数字类型时必须小心。 Print 只能接收 String 或 Object 并且不能传递 int32 或数字文字(constexpr IntegerLiteral 类型)。严格区分常量和变量的类型(其值可以在运行时改变)这一事实是嵌入式系统的语言。

candidates are:
  Cast(implicit class Context)(MaybeObject): Smi labels CastError
  Cast(implicit class Context)(Object): Smi labels CastError
  Cast(class String): Smi labels CastError

在上面,它在被传递之前被转换为Smi(小整数)。Print for Smi(Object) 会产生调试打印,所以 FizzBu​​zz 的外观会稍逊

DebugPrint: Smi: 0x1 (1)

DebugPrint: Smi: 0x2 (2)

fizz
DebugPrint: Smi: 0x4 (4)

buzz
fizz
DebugPrint: Smi: 0x7 (7)
...

顺便提一下,“如果FizzBuzz函数的返回值设置为Union( | ),会不会变成纯函数?”在没有通用基类型的情况下,似乎无法使用 Union(这与 TypeScript 不同)。

macro FizzBuzz(n: Smi): (Smi | constexpr String) {
  if (n % 15 == 0) {
    return "fizzbuzz";
  }
  if (n % 3 == 0) {
    return "fizz";
  }
  if (n % 5 == 0) {
    return "buzz";
  }
  return n;
}
test/torque/test-torque.tq:23:1: Torque Error: types Smi and constexpr String have no common supertype
综上所述

以上是运行在 V8 内部的 Torque 语言的介绍。环境搭建好辛苦,还没来得及探索语言就筋疲力尽了……我想再借一次机会,研究一下语法和语言的特点。

原创声明:本文系作者授权爱码网发表,未经许可,不得转载;

原文地址:https://www.likecs.com/show-308626190.html

48人参与, 0条评论 登录后显示评论回复

你需要登录后才能评论 登录/ 注册