by Vyacheslav Egorov
dynamic static
──────────────────────────── ──────────
2011 2013 2015 2018 2020
──────────────────────────────────────►
0.x 1.0 2.0 2.12
───────JIT────── ──────JIT or AOT───────
╱ ╲ NNBD
development deployment
Dog d = new Cat(); // ignored type annotations
// access to a field
d.f // ─ invocation of getter
// method tear-off
// noSuchMethod invocation
int x = null; // no primitive types
(1 << 100) >> 100; // == 1, int is bignum
/* How to make a JIT */
╔══════════╗ ┌──────────┐
║ Compiler ║ ▶ generates ▶ │ Code │
╚══════════╝ └──────────┘
▲ │
╰──────────────────────────╯
execution feedback
— generate unoptimized code;
— collect type feedback through inline caches;
— generate optimized code using type feedback;
— rinse and repeat.
// call-site specific cache
─── ICData { ... }
╱
dog.bark();
╲ // generic dispatch stub
─── dispatch(icData, receiver, ...) { ... }
d.bark()
(unoptimized)
mov RBX, [PP + 0x7f] ; ICData
call [PP + 0x87] ; dispatch stub
; ╲
; instead of embedding constants
; into the code we access them
; through object pool
─── ICData { }
╱
dog.bark();
─── ICData { Dog: Dog.bark (1) }
╱
dog.bark();
─── ICData { Dog: Dog.bark (2) }
╱
dog.bark();
─── ICData { Dog: Dog.bark (3) }
╱
dog.bark();
─── ICData { Dog: Dog.bark (555) }
╱
dog.bark();
d.bark()
(optimized, monomorphic)
;; Check that RAX contains a Dog
test al, 0x1
jz ->deopt ; deopt if RAX is a Smi
movzx rcx, word ptr [rax + 0x1]
cmp rcx, 0x53a
jnz ->deopt
;; Call Dog.bark directly.
mov r10, qword ptr [PP + 0x5f] ; argdesc
call qword ptr [PP + 0x67]
d.bark()
(optimized, monomorphic)
;; Check that RAX contains a Dog
test al, 0x1
jz ->deopt ; deopt if RAX is a Smi
movzx rcx, word ptr [rax + 0x1]
cmp rcx, 0x53a
jnz ->deopt
;; Call Dog.bark directly.
mov r10, qword ptr [PP + 0x5f] ; argdesc
call qword ptr [PP + 0x67]
d.bark()
(optimized, monomorphic)
;; Check that RAX contains a Dog
test al, 0x1
jz ->deopt ; deopt if RAX is a Smi
movzx rcx, word ptr [rax + 0x1]
cmp rcx, 0x53a
jnz ->deopt
;; Call Dog.bark directly.
mov r10, qword ptr [PP + 0x5f] ; argdesc
call qword ptr [PP + 0x67]
dynamic
──────────────────────────────────────────
2011 2013
─────────────────────────────────────────►
0.x 1.0
───────JIT─────────────────────────────────
dynamic
──────────────────────────────────────────
2011 2013
────────────────────────────────────────►
0.x 1.0 │
───────JIT────────────────────────────────
│
Flutter
(noun) serialized form for heap object graphs, used for faster startup or exchanging data between isolates. Similar to Smalltalk images. Only supported data, not machine code.
instructions pool ┌─────────────────────┐ ┌─┬─┬─┬─┬─┬─┬─ │ object_pool_ ──────────┤ │ │ │ │ ││ ├─────────────────────┤ └─┴─┴─┴─┴─┴┴─ entry │ mov PP, [RIP-0x25] │ │ │ ... │ ┌──┴───┐ │ mov rax, [PP+0x2f] │ │object│ │ ... │ └──────┘ └─────────────────────┘
instructions (RX) pool (RO) ┌─────────────────────┐ ┌─┬─┬─┬─┬─┬─┬─ │ object_pool_ ──────────┤ │ │ │ │ ││ ├─────────────────────┤ └─┴─┴─┴─┴─┴┴─ entry │ mov PP, [RIP-0x25] │ │ │ ... │ ┌──┴───┐ │ mov rax, [PP+0x2f] │ │object│ │ ... │ └──────┘ └─────────────────────┘
╔════════════════════════╗ ╔════════════════════════╗
║ Isolate ┌─────────┐ ║ ║ Isolate ┌─────────┐ ║
║ ┌─────────┐│ ║ ║ ┌─────────┐│ ║
║ ┌─────────┐│┘ ║ ║ ┌─────────┐│┘ ║
║ │ │┘ ║ ║ │ │┘ ║
║ └─────────┘ ║ ║ └─────────┘ ║
╚════════════════════════╝ ╚════════════════════════╝
╔════════════════════════╗ ╔════════════════════════╗
║ Isolate ┌─────────┐ ║ ║ Isolate ┌─────────┐ ║
║ ┌─────────┐│ ║ ║ ┌─────────┐│ ║
║ ┌─────────┐│┘ ║ ║ ┌─────────┐│┘ ║
║ │ Pool │┘ ║ ║ │ Pool │┘ ║
║ └─────────┘ ║ ║ └─────────┘ ║
╚════════════════════════╝ ╚════════════════════════╝
instructions (RX) ╏ pool (RW) ┌─────────────────────┐ ╏ ┌─┬─┬─┬─┬─┬─┬─ │ object_pool_ ──────────┤ │ │ │ │ ││ ├─────────────────────┤ ╏ └─┴─┴─┴─┴─┴┴─ entry │ mov PP, [RIP-0x25] │ ╏ │ │ ... │ ╏ ┌──┴───┐ │ mov rax, [PP+0x2f] │ ╏ │object│ │ ... │ ╏ isolate └──────┘ └─────────────────────┘ ╏ heap
instructions (RX) ╏ ╏ ╏ ┌─────────────────────┐ ╏ entry │ mov PP, [RIP-0x25] │ ╏ ┌──────┐ │ ... │ ╏ │ pool │ │ mov rax, [PP+0x2f] │ ╏ └──────┘ │ ... │ ╏ isolate └─────────────────────┘ ╏ heap
instructions (RX) ╏ ┌──────┐ ╏ │ code │ ┌────────────────┐ └────┘ ┌──────────┴──────────┐ ╏ └─────────┘ │ entry │ mov PP, [RIP-0x25] │ ╏ ┌──────┐ │ │ ... │ ╏ │ pool ├──────┘ │ mov rax, [PP+0x2f] │ ╏ └──────┘ │ ... │ ╏ isolate └─────────────────────┘ ╏ heap
instructions (RX) ╏ ┌──────┐ ╏ │ code │ ┌────────────────┐ └────┘ ┌──────────┴──────────┐ ╏ └─────────┘ │ entry │ mov PP, [CR+pp_ofs] │ ╏ ┌──────┐ │ │ ... │ ╏ │ pool ├──────┘ │ mov rax, [PP+0x2f] │ ╏ └──────┘ │ ... │ ╏ isolate └─────────────────────┘ ╏ heap
; STATIC CALLS
; ═ BEFORE ═══════════════════════════════════════
call [PP + 0x87] ; direct call
mov PP, [RIP - 0x25] ; in callee load PP
; ═ AFTER ════════════════════════════════════════
mov CR, [PP + 0x87] ; load Code object
call [CR + entry_ofs] ; call through Code
mov PP, [CR + pp_ofs] ; in callee load PP
dynamic
──────────────────────────────────────────
2011 2013 2015 2016
───────────────────────────────────────►
0.x 1.0 └ we are here
───────JIT────── ──────JIT or AOT────────
╱ ╲
development deployment
code (rx) pool (rw)
┌───┐
──────────▶│ ────▶ ICData
╱ ├───┤⬉
dog.bark(); │ │ can patch these
╲ ├───┤⬋
──────────▶│ ────▶ DispatchStub
├───┤
code (rx) pool (rw)
┌───┐
──────────▶│ ────▶ class Dog
╱ ├───┤
dog.woof(); │ │
╲ ├───┤ Dog.woof
──────────▶│ ────▶ ┌────────────────────────────────┐
├───┤ │ if (this.class != icData) │
normal │ return monomorphicMiss(...); │
entry⬊├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
int
is nullableint
is arbitrary precision
;; Y <- X + 1
test X, 1
jnz slow──┐
mov Y, X │
add Y, 2 │
jo slow───┤
┌─▶ done: │
│ ... │
│ slow: ◀──────┘ ;; "cold" code
│ ;; ... X.operator+(1) call ...
└────jmp done
Development | Production | |
Native | JIT | JIT |
Web | Dartium† | AOT (closed world) |
† Fork of Chromium with Dart VM embedded
dart2js
too slowPace of Dart language evolution was often hindered by duplication between different language implementations. A project is born to address that.
Dart VM (in C++)
┌─────────────────────────┐
┌─▶│ Parser ─▶ AST ─▶ ... │
│ └─────────────────────────┘
│ dart2js (in Dart)
╭────────╮ ┌─────────────────────────┐
│ Dart ──▶│ Parser ─▶ AST ─▶ ... │
│ Source │ └─────────────────────────┘
╰────────╯│ dartanalyzer (in Dart)
│ ┌─────────────────────────┐
└─▶│ Parser ─▶ AST ─▶ ┌─────┐ some tools use
└────────────────────│┌─────┐ analyzer as a Dart
└│ │ parser
└─────┘
Dart VM
┌──────┐
┌─▶│ ... │
│ └──────┘
CFE Kernel│ dart2js
╭────────╮ ┌────────┐ ╭─────╮ ┌──────┐
│ Dart │──▶│ Parser │─▶│ AST ──▶│ ... │
│ Source | └────────┘ ╰─────╯ └──────┘
╰────────╯ (in Dart) ┊
┊
int
becomes 64-bit signed integer
Dog d = new Cat(); // ignored type annotations
// access to a field
d.f // ─ invocation of getter
// method tear-off
// noSuchMethod invocation
int x = null; // no primitive types
(1 << 100) >> 100; // == 1, int is bignum
Dog d = new Cat(); // compile time error
//
d.f // calls an implementation
// of Dog.f
//
int x = null; // still no primitive types
(1 << 100) >> 100; // == 0, int is 64-bit
global context sensitive analysis over Kernel to annotate it with more accurate type information than what is available in static type annotations†.
† which specify nullable interface types
groom(Animal a) {
a.method();
}
main() {
groom(Dog());
feed(Cat());
}
groom(Animal a) {
a.method(); /* TFA: a is non-nullable Dog */
} /* TFA: invokes Dog.method */
main() {
groom(Dog());
feed(Cat());
}
f(int anInt, double aDouble) {
// ╲ ╱
// conservative *boxed* representation
}
// Dart 1
f(IntegerDog(), FloatingCat()); // ooook
// Dart 2
f(null, 2.0); // ok
f(int anInt, double aDouble) {
// ╲ ╱
// In real code TFA can often prove
// that such variables are non-nullable
// allowing to use *unboxed* representation.
// ... btw this was an intern project!
}
; a lot of code for a static call
mov CR, [PP + 0x87] ; load Code object
call [CR + entry_ofs] ; call through Code
mov PP, [CR + pp_ofs] ; in callee load PP
Code
object
; ═ BEFORE ═══════════════════════════════════════
mov CR, [PP + 0x87] ; load Code object
call [CR + entry_ofs] ; call through Code
mov PP, [CR + pp_ofs] ; in callee load PP
; ═ AFTER ════════════════════════════════════════
call #relative-offset-to-target
a lot of monomorphic call-sites are devirtualized by TFA
Table indexed by a combination of receiver's class id and selector id. Classical idea described in literature as row displacement dispatch tables.
movzx cid, word ptr [obj + 15] ; load class id
call [GDT + cid * 8 + #(sid * 8)]
┌────────────────────────┐ ╱
│ Selectors are numbered ├
│ such that GDT[cid+sid] │
│ gives dispatch target │
└────────────────────────┘
movzx cid, word ptr [obj + 15] ; load class id
call [GDT + cid * 8 + #(sid * 8)]
┌────────────────────────┐ ╱
│ Naive numbering 0, ├
│ NumCids, 2*NumCids, ...│
│ is too sparse │
└────────────────────────┘
movzx cid, word ptr [obj + 15] ; load class id
call [GDT + cid * 8 + #(sid * 8)]
┌────────────────────────┐ ╱
│ Selectors are numbered ├
│ in a way that tries to │
│ minimize GDT size. │
└────────────────────────┘
dynamic static
──────────────────────────── ──────────
2011 2013 2015 2018 2020
─────────────────────────────────────
0.x 1.0 │ 2.0│ 2.12
vm ───────JIT───── ──────────AOT──────────
│ │
Flutter We are here
Dog d = new Cat(); // compile time error
//
d.f // calls an implementation
// of Dog.f
//
int x = null; // still no primitive types
(1 << 100) >> 100; // == 0, int is 64-bit
// types are now non-nullable by default
int x = null; // compile time error
// types are now non-nullable by default
int x = null; // compile time error
int? x = null; // ok
List<Dog>? dogs;
if (dogs != null) {
dogs.add(null); // compile time error
dogs.add(Dog()); // ok
}
† NNBD is incremental and provides an opt-out escape hatch. Compiler can only rely on static non-nullability if none of the libraries are opted out.
Unhandled exception: Error occurred! #0 causeError (test.dart:4) #1 main (test.dart:9)
Unhandled exception: Error occurred! *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** pid: 83265, tid: 4552355264, name Dart_Initialize build_id: 'c0371757e9bbea5ce6a27b1f22e5a25a' isolate_dso_base: 10f396000, vm_dso_base: 10f396000 isolate_instructions: 10f3a0000, vm_instructions: 10f398000 #00 abs 000000010f3efd87 virt 0000000000059d87 _kDartIsolateSnapshotInstructions+0x4fd87 #01 abs 000000010f3efd62 virt 0000000000059d62 _kDartIsolateSnapshotInstructions+0x4fd62
Opens doors for dropping symbolic information and parts of program structure.
╔════════════════════════╗ ╔════════════════════════╗
║ Isolate ┌─────────┐ ║ ║ Isolate ┌─────────┐ ║
║ ┌─────────┐│ ║ ║ ┌─────────┐│ ║
║ ┌───┐ ┌─────────┐│┘ ║ ║ ╌╌╌ ┌─────────┐│┘ ║
║ │MSG│ │ Program │┘ ║ ║ ╎ ╎ │ Program │┘ ║
║ └─┬─┘ └─────────┘ ║ ║ ╌▲╌ └─────────┘ ║
╚═══┆════════════════════╝ ╚═══┆════════════════════╝
└╌╌╌╌╌╌╌╌╌╌╌ copying ╌╌╌╌╌╌╌┘
╔═════════════════════════════════════╗
║ Isolate Isolate ┌─────────┐ ║
║ ┌─────────┐│ ║
║ ┌───┐ ╌╌╌ ┌─────────┐│┘ ║
║ │MSG│ ╎ ╎ │ Program │┘ ║
║ └─┬─┘ ╌▲╌ └─────────┘ ║
╚═══┆══════════┆══════════════════════╝
└╌ copying ┘
╔═════════════════════════════════════╗
║ Isolate Isolate ┌─────────┐ ║
║ ┌─────────┐│ ║
║ ┌───┐ ┌─────────┐│┘ ║
║ │MSG│ │ Program │┘ ║
║ └───┘ └─────────┘ ║
╚═════════════════════════════════════╝
╔═════════════════════════════════════╗
║ Isolate ┌─────────┐ ║
║ ┌─────────┐│ ║
║ ┌───┐ ┌─────────┐│┘ ║
║ │MSG│ │ Program │┘ ║
║ └───┘ └─────────┘ ║
╚═════════════════════════════════════╝
╔═════════════════════════════════════╗
║ Isolate ┌─────────┐ ║
║ ┌─────────┐│ ║
║ ┌───┐ ┌─────────┐│┘ ║
║ │MSG│ │ Program │┘ ║
║ └───┘ └─────────┘ ║
╚═════════════════════════════════════╝
╔═════════════════════════════════════╗
║ Isolate ┌─────────┐ ║
║ ┌─────────┐│ ║
║ ┌───┐ ┌─────────┐│┘ ║
║ │MSG│ │ Program │┘ ║
║ └───┘ └─────────┘ ║
╚═════════════════════════════════════╝
╔═════════════════════════════════════╗
║ Isolate ┌─────────┐ ║
║ ┌─────────┐│ ║
║ ┌───┐ ┌─────────┐│┘ ║
║ │MSG│ │ Program │┘ ║
║ └───┘ └─────────┘ ║
╚═════════════════════════════════════╝
╔═════════════════════════════════════╗
║ Isolate ┌─────────┐ ║
║ ┌─────────┐│ ║
║ ┌───┐ ┌─────────┐│┘ ║
║ │MSG│ │ Program │┘ ║
║ └───┘ └─────────┘ ║
╚═════════════════════════════════════╝