Torque. Язык для V8.

Torque. Язык для V8.

Yuriy Karpov

Я периодически читаю исходники V8, чтобы понять, как реализован тот или иной метод. Исходно V8 написан на C++, с которым у меня есть небольшой опыт, поэтому на уровне читателя я справляюсь.

Однажды я полез посмотреть реализацию методов Array — и неожиданно столкнулся с файлами с расширением .tq. Код в них был похож на внебрачного сына C++ и TypeScript. Это оказался язык Torque.

В этой статье я расскажу, что это за язык, зачем его придумали разработчики V8 и как он влияет на производительность и читаемость движка.

Torque

Torque — это домен-специфический язык программирования (DSL), созданный специально для работы внутри движка V8. Он предназначен для реализации встроенных функций JavaScript, таких как mapreducesplitisArray, и многих других.

Torque пришёл на смену части кода, ранее написанного на C++ и JavaScript, и делает его более:

  • читаемым,
  • безопасным,
  • и оптимизированным для исполнения внутри V8.

Почему V8 использует Torque

1. Интеграция с V8

Torque тесно связан с внутренними структурами движка:

  • управлением памятью (кучей),
  • типами объектов (массивы, строки, функции),
  • структурой элементов массивов и т.д.

2. Высокоуровневый синтаксис

Синтаксис Torque схож с JavaScript, а точнее с TypeScript, что делает его более доступным для разработчиков. Он позволяет описывать функции на высоком уровне с обеспечением строгой проверки типов. Это упрощает разработку встроенных функций.

Примеры Torque-кода

HelloWorld на Torque:

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

Подсчёт длины массива:

@builtin
function ArrayLength(array: JSArray): Smi {
    return %_GetProperty(array, "length");
}
  • @builtin — аннотация, указывающая, что функция встроена в движок.
  • array: JSArray — строгая типизация, указывающая, что аргумент — это JavaScript-массив.
  • Smi — представляет собой низкоуровневый тип для маленьких целых чисел.

Реальный пример: Array.isArray

Функция Array.isArray() реализована на Torque вот так:
Исходник на GitHub

// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

namespace runtime {
extern runtime ArrayIsArray(implicit context: Context)(JSAny): JSAny;
}  // namespace runtime

namespace array {
// ES #sec-array.isarray
javascript builtin ArrayIsArray(
    js-implicit context: NativeContext)(arg: JSAny): JSAny {
  // 1. Return ? IsArray(arg).
  typeswitch (arg) {
    case (JSArray): {
      return True;
    }
    case (JSProxy): {
      // TODO(verwaest): Handle proxies in-place
      return runtime::ArrayIsArray(arg);
    }
    case (JSAny): {
      return False;
    }
  }
}
} // namespace array

Что здесь происходит:

Определяет встроенную функцию ArrayIsArray, соответствующую Array.isArray() в JavaScript.

Принимает аргумент любого типа (JSAny) и неявно получает текущий JS-контекст.

Проверяет тип аргумента с помощью typeswitch:

  • Проверяется, является ли аргумент массивом (JSArray) — тогда возвращается true.
  • Если аргумент — Proxy, вызывается внешняя runtime-функция.
  • Всё остальное возвращает false.

Оборачивает реализацию в namespace array, чтобы отделить встроенные функции.

Подключает runtime-функцию через namespace runtime, чтобы использовать её при необходимости.

Процесс выполнения:

Например, вызов reduce в JavaScript может быть перенаправлен в реализацию на Torque, если этот метод реализован в Torque. Torque-функция выполняет операции (например, итерацию по массиву) с использованием внутренних структур V8. Результат возвращается обратно в JavaScript-код. Torque-код транслируется в C++, а затем компилируется стандартными инструментами V8 в машинный код, который эффективно исполняется внутри движка.

Читатель, обрати внимание, что трансляция происходит не рантайм, а перед компиляцией V8, далее будет подробнее об этом.

Безопасность и производительность:

  • Строгая типизация предотвращает ошибки, связанные с неправильными типами.
  • Интеграция с оптимизирующим компилятором V8 обеспечивает быструю генерацию машинного кода.
  • Проверки на уровне компиляции упрощают написание корректного и оптимизированного кода.

Как Torque улучшает работу V8:

  • Читаемость: Код на Torque проще и понятнее, что важно для команды разработчиков V8 и сторонних участников.
  • Легкость разработки: Встроенные методы и примитивы легче писать и обновлять, без необходимости управления низкоуровневыми аспектами.
  • Оптимизация производительности: Код, написанный на Torque, автоматически генерируется с учетом особенностей V8, что делает его более эффективным.
  • Совместимость: Torque встроен в процесс разработки V8, что позволяет работать с внутренними структурами, типами и оптимизациями движка.

Torque в контексте разработки V8:

Torque заменяет часть старого C++ кода, улучшая читаемость и упрощая разработку. Его основной вклад заключается в оптимизации встроенных методов, снижении количества багов благодаря строгой типизации и проверкам на этапе компиляции, а также в упрощении написания и поддержки встроенных функций. Это делает внутреннюю работу V8 более мощной, гибкой и доступной для будущих улучшений.
Можете посмотреть, что уже заменили на Torque https://chromium.googlesource.com/v8/v8/+/refs/heads/main/src/builtins/

Как Torque компилируется?

  1. Превращение Torque в C++

Torque-компилятор берет написанный код и генерирует из него C++ код. Этот процесс называется транспиляцией(перевод из одного языка в другой). Сгенерированный C++ код затем становится частью движка V8.

2. Компиляция C++ в машинный код

Сгенерированный C++ вызывает существующий интерфейс CodeStubAssembler в V8. CodeStubAssembler, в свою очередь, использует бэкенд компилятора TurboFan для генерации эффективного машинного кода.

Таким образом, компиляция с помощью Torque включает несколько этапов:

  1. Сборка через GN запускает компилятор Torque, который обрабатывает все файлы с расширением *.tq. Каждый файл Torque path/to/file.tq приводит к генерации нескольких файлов.
  2. GN-сборка компилирует сгенерированные -csa.cc файлы (из шага 1) в исполняемый файл mksnapshot.
  3. Во время выполнения mksnapshot все встроенные функции V8 (builtins) генерируются и упаковываются в snapshot-файл, включая те, что были определены через Torque, а также другие builtins, использующие функциональность, определенную в Torque.
  4. Оставшаяся часть V8 собирается. Все встроенные функции, созданные с помощью Torque, становятся доступными через snapshot-файл, который линкуется в V8. Эти функции можно вызывать как любые другие builtins. Кроме того, исполняемые файлы d8 или chrome также включают напрямую сгенерированные единицы компиляции, связанные с определениями классов.
Моя схема (сверху)
схема из документации (сверху)


Вывод

Torque – важный шаг в эволюции движка V8. Он упростил реализацию встроенных методов JavaScript, сделал код более читаемым, надёжным и одновременно повысил производительность.

Хотя Torque – это внутренний язык V8 и не предназначен для использования в прикладных проектах, его появление значительно улучшило архитектуру движка и упростило поддержку встроенных функций.

А зачем мне это?

Изучение исходников V8 даёт возможность лучше понять, как работает JavaScript в Chrome и Node.js на самом низком уровне. Благодаря Torque это стало заметно проще.

Ранее мы разобрали bytecode в статье "Как Ignition оптимизирует forEach() в bytecode v8" и подготовили Benchmark-платформу для доклада "На чем сегодня писать для WebAssembly?" (HolyJS), теперь же мы закрыли третий слой –исходный код движка.

В результате, у нас появляется полноценная трёхуровневая модель анализа JavaScript:

  • Исходный код в V8
  • Байткод, генерируемый при исполнении
  • Измерения производительности в Benchmark

Эта связка даёт глубокое понимание того, как работает JavaScript под капотом. И мы будем использовать все эти знания для дальнейших исследований и статей.


Если вы хотите глубже изучить Torque или попробовать себя в разработке V8, читайте официальную документацию: https://v8.dev/docs/torque


Всем, кто прочитал спасибо, присоединяйтесь к моему каналу https://t.me/frontend_bookmark


Report Page