Skip to content

Commit

Permalink
Port the builtin guide to Torque (#69)
Browse files Browse the repository at this point in the history
  • Loading branch information
tebbi authored and mathiasbynens committed Nov 14, 2018
1 parent 51e1298 commit 1a3945d
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 5 deletions.
2 changes: 1 addition & 1 deletion prism-languages.js
Expand Up @@ -42,7 +42,7 @@ const installPrismLanguages = (Prism) => {
}
],
'builtin': /\b(?:UnsafeCast|Convert|Cast|check|assert)\b/,
'keyword': /\b(?:typeswitch|javascript|generates|constexpr|otherwise|continue|implicit|operator|runtime|builtin|extends|labels|return|module|extern|while|macro|const|label|break|type|else|case|let|try|for|if)\b/,
'keyword': /\b(?:typeswitch|javascript|generates|constexpr|otherwise|continue|implicit|operator|runtime|builtin|extends|labels|return|namespace|extern|while|macro|const|label|break|type|else|case|let|try|for|if)\b/,
'boolean': /\b(?:[tT]rue|[fF]alse)\b/,
'number': /\b0x[\da-fA-F]+\b|(?:\b\d+\.?\d*|\B\.\d+)(?:e[+-]?\d+)?/i,
'operator': /--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*|\/|~|\^|%/,
Expand Down
2 changes: 2 additions & 0 deletions src/docs/csa-builtins.md
Expand Up @@ -3,6 +3,8 @@ title: 'CodeStubAssembler builtins'
---
This document is intended as an introduction to writing CodeStubAssembler builtins, and is targeted towards V8 developers.

**Note:** [Torque](/docs/torque) replaces CodeStubAssembler as the recommended way to implement new builtins. See [Torque builtins](/docs/torque-builtins) for the Torque version of this guide.

## Builtins

In V8, builtins can be seen as chunks of code that are executable by the VM at runtime. A common use case is to implement the functions of builtin objects (such as RegExp or Promise), but builtins can also be used to provide other internal functionality (e.g. as part of the IC system).
Expand Down
5 changes: 3 additions & 2 deletions src/docs/index.md
Expand Up @@ -54,8 +54,9 @@ V8 enables any C++ application to expose its own objects and functions to JavaSc
- Under the hood
- [Ignition](/docs/ignition)
- [TurboFan](/docs/turbofan)
- [Torque](/docs/torque)
- [CodeStubAssembler built-ins](/docs/csa-builtins)
- [Torque user manual](/docs/torque)
- [Writing Torque built-ins](/docs/torque-builtins)
- [Writing CSA built-ins](/docs/csa-builtins)
- Writing optimizable JavaScript
- [Using V8’s sample-based profiler](/docs/profile)
- [Profiling Chromium with V8](/docs/profile-chromium)
Expand Down
139 changes: 139 additions & 0 deletions src/docs/torque-builtins.md
@@ -0,0 +1,139 @@
---
title: 'V8 Torque builtins'
---

This document is intended as an introduction to writing Torque builtins, and is targeted towards V8 developers. Torque replaces CodeStubAssembler as the recommended way to implement new builtins. See [CodeStubAssembler builtins](/docs/csa-builtins) for the CSA version of this guide.

## Builtins

In V8, builtins can be seen as chunks of code that are executable by the VM at runtime. A common use case is to implement the functions of builtin objects (such as `RegExp` or `Promise`), but builtins can also be used to provide other internal functionality (e.g. as part of the IC system).

V8’s builtins can be implemented using a number of different methods (each with different trade-offs):

- **Platform-dependent assembly language**: can be highly efficient, but need manual ports to all platforms and are difficult to maintain.
- **C++**: very similar in style to runtime functions and have access to V8’s powerful runtime functionality, but usually not suited to performance-sensitive areas.
- **JavaScript**: concise and readable code, access to fast intrinsics, but frequent usage of slow runtime calls, subject to unpredictable performance through type pollution, and subtle issues around (complicated and non-obvious) JS semantics. Javascript builtins are deprecated and should not be added anymore.
- **CodeStubAssembler**: provides efficient low-level functionality that is very close to assembly language while remaining platform-independent and preserving readability.
- **[V8 Torque](/docs/torque)**: is a V8-specific domain-specific language that is translated to CodeStubAssembler. As such, it extends upon CodeStubAssembler and offers static typing as well as readable and expressive syntax.

The remaining document focuses on the latter and give a brief tutorial for developing a simple Torque builtin exposed to JavaScript. For more complete information about Torque, see the [V8 Torque user manual](/docs/torque).

## Writing a Torque builtin

In this section, we will write a simple CSA builtin that takes a single argument, and returns whether it represents the number `42`. The builtin is exposed to JS by installing it on the `Math` object (because we can).

This example demonstrates:

- Creating a Torque builtin with JavaScript linkage, which can be called like a JS function.
- Using Torque to implement simple logic: type distinction, Smi and heap-number handling, conditionals.
- Installation of the CSA builtin on the `Math` object.

In case you’d like to follow along locally, the following code is based off revision [589af9f2](https://chromium.googlesource.com/v8/v8/+/589af9f257166f66774b4fb3008cd09f192c2614).

## Defining `MathIs42`

Torque code is located in `src/builtins/*.tq` files, roughly organized by topic. Since we will be writing a `Math` builtin, we’ll put our definition into `src/builtins/math.tq`. Since this file doesn't exist yet, we have to add it to [`torque_files`](https://cs.chromium.org/chromium/src/v8/BUILD.gn?l=914&rcl=589af9f257166f66774b4fb3008cd09f192c2614) in [`BUILD.gn`](https://cs.chromium.org/chromium/src/v8/BUILD.gn).

```torque
namespace math {
javascript builtin MathIs42(
context: Context, receiver: Object, x: Object): Boolean {
// At this point, x can be basically anything - a Smi, a HeapNumber,
// undefined, or any other arbitrary JS object. ToNumber_Inline is defined
// in CodeStubAssembler. It inlines a fast-path (if the argument is a number
// already) and calls the ToNumber builtin otherwise.
const number: Number = ToNumber_Inline(x);
// A typeswitch allows us to switch on the dynamic type of a value. The type
// system knows that a Number can only be a Smi or a HeapNumber, so this
// switch is exhaustive.
typeswitch (number) {
case (smi: Smi): {
// The result of smi == 42 is not a Javascript boolean, so we use a
// conditional to create a Javascript boolean value.
return smi == 42 ? True : False;
}
case (heapNumber: HeapNumber): {
return Convert<float64>(heapNumber) == 42 ? True : False;
}
}
}
}
```

We put the definition in the Torque namespace `math`. Since this namespace didn't exist before, we have to add it to [`torque_namespaces`](https://cs.chromium.org/chromium/src/v8/BUILD.gn?l=933&rcl=589af9f257166f66774b4fb3008cd09f192c2614) in [`BUILD.gn`](https://cs.chromium.org/chromium/src/v8/BUILD.gn).

## Attaching `Math.is42`

Builtin objects such as `Math` are set up mostly in [`src/bootstrapper.cc`](https://cs.chromium.org/chromium/src/v8/src/bootstrapper.cc?q=src/bootstrapper.cc+package:%5Echromium$&l=1) (with some setup occurring in `.js` files). Attaching our new builtin is simple:

```cpp
// Existing code to set up Math, included here for clarity.
Handle<JSObject> math = factory->NewJSObject(cons, TENURED);
JSObject::AddProperty(global, name, math, DONT_ENUM);
// […snip…]
SimpleInstallFunction(isolate_, math, "is42", Builtins::kMathIs42, 1, true);
```
Now that `is42` is attached, it can be called from JS:
```bash
$ out/debug/d8
d8> Math.is42(42);
true
d8> Math.is42('42.0');
true
d8> Math.is42(true);
false
d8> Math.is42({ valueOf: () => 42 });
true
```

## Defining and calling a builtin with stub linkage

Builtins can also be created with stub linkage (instead of JS linkage as we used above in `MathIs42`). Such builtins can be useful to extract commonly-used code into a separate code object that can be used by multiple callers, while the code is only produced once. Let’s extract the code that handles heap numbers into a separate builtin called `HeapNumberIs42`, and call it from `MathIs42`.

The definition is also straightforward. The only difference to our builtin with Javascript linkage is that we omit the keyword `javascript` and there is no receiver argument.

```torque
namespace math {
builtin HeapNumberIs42(implicit context: Context)(heapNumber: HeapNumber):
Boolean {
return Convert<float64>(heapNumber) == 42 ? True : False;
}
javascript builtin MathIs42(implicit context: Context)(
receiver: Object, x: Object): Boolean {
const number: Number = ToNumber_Inline(x);
typeswitch (number) {
case (smi: Smi): {
return smi == 42 ? True : False;
}
case (heapNumber: HeapNumber): {
// Instead of handling heap numbers inline, we now call our new builtin.
return HeapNumberIs42(heapNumber);
}
}
}
}
````

Why should you care about builtins at all? Why not leave the code inline (or extracted into macros for better readability)?

An important reason is code space: builtins are generated at compile-time and included in the V8 snapshot or embedded into the binary. Extracting large chunks of commonly used code to separate builtins can quickly lead to space savings in the 10s to 100s of KBs.

## Testing stub-linkage builtins

Even though our new builtin uses a non-standard (at least non-C++) calling convention, it’s possible to write test cases for it. The following code can be added to [`test/cctest/compiler/test-run-stubs.cc`](https://cs.chromium.org/chromium/src/v8/test/cctest/compiler/test-run-stubs.cc) to test the builtin on all platforms:

```cpp
TEST(MathIsHeapNumber42) {
HandleAndZoneScope scope;
Isolate* isolate = scope.main_isolate();
Heap* heap = isolate->heap();
Zone* zone = scope.main_zone();

StubTester tester(isolate, zone, Builtins::kMathIs42);
Handle<Object> result1 = tester.Call(Handle<Smi>(Smi::FromInt(0), isolate));
CHECK(result1->BooleanValue());
}
```
4 changes: 2 additions & 2 deletions src/docs/torque.md
@@ -1,7 +1,7 @@
---
title: 'Torque user manual'
title: 'V8 Torque user manual'
---
Torque is a language that allows developers contributing to the V8 project to express changes in the VM by focusing on their _intent_ of their changes to the VM, rather than preoccupying themselves with unrelated implementation details. The language was designed to be simple enough to make it easy to directly translate the [ECMAScript specification](https://tc39.github.io/ecma262/) into an implementation in V8, but powerful enough to express the low-level V8 optimization tricks in a robust way, like creating fast-paths based on tests for specific object-shapes.
V8 Torque is a language that allows developers contributing to the V8 project to express changes in the VM by focusing on their _intent_ of their changes to the VM, rather than preoccupying themselves with unrelated implementation details. The language was designed to be simple enough to make it easy to directly translate the [ECMAScript specification](https://tc39.github.io/ecma262/) into an implementation in V8, but powerful enough to express the low-level V8 optimization tricks in a robust way, like creating fast-paths based on tests for specific object-shapes.

Torque will be familiar to V8 engineers and JavaScript developers, combining a TypeScript-like syntax that eases both writing and understanding V8 code with syntax and types that reflects concepts that are already common in the [`CodeStubAssembler`](/blog/csa). With a strong type system and structured control flow, Torque ensures correctness by construction. Torque’s expressiveness is sufficient to express almost all of the functionality that is [currently found in V8’s builtins](/docs/builtin-functions). It also is very interoperable with `CodeStubAssembler` builtins and `macro`s written in C++, allowing Torque code to use hand-written CSA functionality and vice-versa.

Expand Down

0 comments on commit 1a3945d

Please sign in to comment.