Дмитрий Кашии́цын
Вывод типов в динамических языках
и не только…
Часть I
HOLYWAR
Часть I
Smalltalk, VM, JIT
4
Мир объектов
5
Образ
6
Иерархия базовых объектов
instanceOf subclassOf
Object MetaObject nil
7
Иерархия базовых объектов
instanceOf subclassOf
Object MetaObject nil
Class MetaClass Object
8
Иерархия базовых объектов
instanceOf subclassOf
Object MetaObject nil
Class MetaClass Object
MetaObject Class Class
9
Иерархия базовых объектов
instanceOf subclassOf
Object MetaObject nil
Class MetaClass Object
MetaObject Class Class
MetaClass Class MetaObject
10
Граф базовых объектов
Object
MetaObject nil
Class
MetaClass
instanceOf
subclassOf
11
Представление указателей в памяти
«Сырой» указатель
Raw value S
12
Представление указателей в памяти
«Сырой» указатель
Raw value S
Указатель на объект
Object* 0
13
Представление указателей в памяти
«Сырой» указатель
Raw value S
Указатель на объект
Object* 0
Целочисленная константа
31 bit integer 1
14
Представление объектов в памяти
Любой объект
Size 1
Class* R I
data[]
15
Представление объектов в памяти
Любой объект
Size 1
Class* R I
data[]
Нормальный объект
Size (n) 1
Class* R 0
Field 1
Field 2
...
Field n
16
Представление объектов в памяти
Любой объект
Size 1
Class* R I
data[]
Нормальный объект
Size (n) 1
Class* R 0
Field 1
Field 2
...
Field n
Двоичный объект
Size (n) 1
Class* R 1
Byte 1 Byte 2 ...
...
Byte n-1 Byte n Stuffing
17
Посылка сообщения
●
Базовая операция VM
18
Посылка сообщения
●
Базовая операция VM
●
Единственный способ взаимодействия
с объектами и объектов между собой
19
Посылка сообщения
●
Базовая операция VM
●
Единственный способ взаимодействия
с объектами и объектов между собой
●
Все языковые средства выражаются
посредством сообщений
20
Примеры сообщений
●
2 + 3
21
Примеры сообщений
●
2 + 3 → 5
22
Примеры сообщений
●
2 + 3
●
'Hello world' size
23
Примеры сообщений
●
2 + 3
●
'Hello world' size → 11
24
Примеры сообщений
●
2 + 3
●
'Hello world' size
●
42 class parent methods at: #absolute
25
Примеры сообщений
●
2 + 3
●
'Hello world' size
●
42 class parent methods at: #absolute
→ SmallInt
26
Примеры сообщений
●
2 + 3
●
'Hello world' size
●
42 class parent methods at: #absolute
→ Number
27
Примеры сообщений
●
2 + 3
●
'Hello world' size
●
42 class parent methods at: #absolute
→ Dictionary (
#absolute → Method,
#asChar → Method,
…
)
28
Примеры сообщений
●
2 + 3
●
'Hello world' size
●
42 class parent methods at: #absolute
→ Method
29
Примеры сообщений
●
2 + 3
●
'Hello world' size
●
(42 class parent methods at: #absolute) text
30
Примеры сообщений
●
2 + 3
●
'Hello world' size
●
(42 class parent methods at: #absolute) text
→ 'absolute
self negative
ifTrue: [ ^self negated ].
^self
'
31
Примеры сообщений
●
2 + 3
●
'Hello world' size
●
42 class parent methods at: #absolute
●
Array with: 42 with: true
32
Примеры сообщений
●
2 + 3
●
'Hello world' size
●
42 class parent methods at: #absolute
●
Array with: 42 with: true
→ Array(42, true)
33
Примеры сообщений
●
2 + 3
●
'Hello world' size
●
42 class parent methods at: #absolute
●
Array with: 42 with: true
●
(Array new: 5) at: 2 put: 42
34
Примеры сообщений
●
2 + 3
●
'Hello world' size
●
42 class parent methods at: #absolute
●
Array with: 42 with: true
●
(Array new: 5) at: 2 put: 42
→ Array(nil, nil, nil, nil, nil)
35
Примеры сообщений
●
2 + 3
●
'Hello world' size
●
42 class parent methods at: #absolute
●
Array with: 42 with: true
●
(Array new: 5) at: 2 put: 42
→ Array(nil, 42, nil, nil, nil)
36
Порядок поиска метода
●
Кэш методов виртуальной машины
●
Словарь методов текущего класса
●
Словари методов классов-предков
37
Примеры поиска
●
SmallInt » absolute
SmallInt → Number
38
Примеры поиска
●
SmallInt » absolute
SmallInt → Number
●
String » isNil
String → Array → Collection → Magnitude → Object
39
Примеры поиска
●
SmallInt » absolute
SmallInt → Number
●
String » isNil
String → Array → Collection → Magnitude → Object
●
Object » whatever
Object → nil
40
Object » doesNotUnderstand:
●
Посылается получателю, если метод для
данного селектора так и не был найден
41
Object » doesNotUnderstand:
●
Посылается получателю, если метод для
данного селектора так и не был найден
●
Может быть переопределен пользователем
42
Object » doesNotUnderstand:
●
Посылается получателю, если метод для
данного селектора так и не был найден
●
Может быть переопределен пользователем
●
Поведение зависит от реализации:
- вывод сообщения об ошибке,
- отображение отладчика и т. п.
43
Пример метода Undefined » test:
test: arg | temp |
temp ← ( arg < 100 )
ifTrue: [ arg + 1 ]
ifFalse: [ 0 ].
^temp
44
Контекст выполнения
Context
method*
arguments[]
temporaries[]
stack[]
stackTop
prevContext*
45
Контекст выполнения
Context
method*
arguments[]
temporaries[]
stack[]
stackTop
prevContext*
Array
...
Array
...
Array
...
46
Контекст выполнения
Context
method*
arguments[]
temporaries[]
stack[]
stackTop
prevContext*
Method
name*
byteCodes[]
literals[]
stackSize
tempSize
class*
text*
package*
Array
...
Array
...
Array
...
47
Контекст выполнения
Context
method*
arguments[]
temporaries[]
stack[]
stackTop
prevContext*
Method
name*
byteCodes[]
literals[]
stackSize
tempSize
class*
text*
package*
Array
...
ByteArray
...
Array
...
Array
...
Array
...
48
Контекст выполнения
Context
method*
arguments[]
temporaries[]
stack[]
stackTop
prevContext*
Method
name*
byteCodes[]
literals[]
stackSize
tempSize
class*
text*
package*
Array
...
ByteArray
...
Array
...
Array
...
Array
...
Context
...
49
Пример вызова Undefined » test:
test: arg | temp |
temp ← ( arg < 100 )
ifTrue: [ arg + 1 ]
ifFalse: [ 0 ].
^temp
nil test: 42
50
Интерпретация Undefined>>test:
Context
bytePtr nil
stackTop nil
[] Arguments
0 nil
1 42
[] Stack
0 nil
1 nil
[] Temps
0 nil
[] Literals
0 100
51
Интерпретация Undefined>>test:
Context
bytePtr nil
stackTop nil
[] Arguments
0 nil
1 42
[] ByteCodes
0 PushArgument 1
1 PushLiteral 0
2 SendBinary <
3 BranchIfFalse 8
4 PushArgument 1
5 PushConstant 1
6 SendBinary +
7 Branch 9
8 PushConstant 0
9 AssignTemporary 0
10 PopTop
11 PushTemporary 0
12 StackReturn
[] Stack
0 nil
1 nil
[] Temps
0 nil
[] Literals
0 100
52
Интерпретация Undefined>>test:
Context
bytePtr 0
stackTop nil
[] Arguments
0 nil
1 42
[] ByteCodes
0 PushArgument 1
1 PushLiteral 0
2 SendBinary <
3 BranchIfFalse 8
4 PushArgument 1
5 PushConstant 1
6 SendBinary +
7 Branch 9
8 PushConstant 0
9 AssignTemporary 0
10 PopTop
11 PushTemporary 0
12 StackReturn
[] Stack
0 nil
1 nil
[] Temps
0 nil
[] Literals
0 100
53
Шаг 0
Context
bytePtr 0
stackTop 0
[] Arguments
0 nil
1 42
[] ByteCodes
0 PushArgument 1
1 PushLiteral 0
2 SendBinary <
3 BranchIfFalse 8
4 PushArgument 1
5 PushConstant 1
6 SendBinary +
7 Branch 9
8 PushConstant 0
9 AssignTemporary 0
10 PopTop
11 PushTemporary 0
12 StackReturn
[] Stack
0 nil
1 nil
[] Temps
0 nil
[] Literals
0 100
54
Шаг 1
Context
bytePtr 1
stackTop 1
[] Arguments
0 nil
1 42
[] ByteCodes
0 PushArgument 1
1 PushLiteral 0
2 SendBinary <
3 BranchIfFalse 8
4 PushArgument 1
5 PushConstant 1
6 SendBinary +
7 Branch 9
8 PushConstant 0
9 AssignTemporary 0
10 PopTop
11 PushTemporary 0
12 StackReturn
[] Stack
0 42
1 nil
[] Temps
0 nil
[] Literals
0 100
55
Шаг 2
Context
bytePtr 2
stackTop 2
[] Arguments
0 nil
1 42
[] ByteCodes
0 PushArgument 1
1 PushLiteral 0
2 SendBinary <
3 BranchIfFalse 8
4 PushArgument 1
5 PushConstant 1
6 SendBinary +
7 Branch 9
8 PushConstant 0
9 AssignTemporary 0
10 PopTop
11 PushTemporary 0
12 StackReturn
[] Stack
0 42
1 100
[] Temps
0 nil
[] Literals
0 100
56
Шаг 3
Context
bytePtr 3
stackTop 1
[] Arguments
0 nil
1 42
[] ByteCodes
0 PushArgument 1
1 PushLiteral 0
2 SendBinary <
3 BranchIfFalse 8
4 PushArgument 1
5 PushConstant 1
6 SendBinary +
7 Branch 9
8 PushConstant 0
9 AssignTemporary 0
10 PopTop
11 PushTemporary 0
12 StackReturn
[] Stack
0 true
1 100
[] Temps
0 nil
[] Literals
0 100
57
Шаг 4
Context
bytePtr 4
stackTop 0
[] Arguments
0 nil
1 42
[] ByteCodes
0 PushArgument 1
1 PushLiteral 0
2 SendBinary <
3 BranchIfFalse 8
4 PushArgument 1
5 PushConstant 1
6 SendBinary +
7 Branch 9
8 PushConstant 0
9 AssignTemporary 0
10 PopTop
11 PushTemporary 0
12 StackReturn
[] Stack
0 true
1 100
[] Temps
0 nil
[] Literals
0 100
58
Шаг 5
Context
bytePtr 5
stackTop 1
[] Arguments
0 nil
1 42
[] ByteCodes
0 PushArgument 1
1 PushLiteral 0
2 SendBinary <
3 BranchIfFalse 8
4 PushArgument 1
5 PushConstant 1
6 SendBinary +
7 Branch 9
8 PushConstant 0
9 AssignTemporary 0
10 PopTop
11 PushTemporary 0
12 StackReturn
[] Stack
0 42
1 100
[] Temps
0 nil
[] Literals
0 100
59
Шаг 6
Context
bytePtr 6
stackTop 2
[] Arguments
0 nil
1 42
[] ByteCodes
0 PushArgument 1
1 PushLiteral 0
2 SendBinary <
3 BranchIfFalse 8
4 PushArgument 1
5 PushConstant 1
6 SendBinary +
7 Branch 9
8 PushConstant 0
9 AssignTemporary 0
10 PopTop
11 PushTemporary 0
12 StackReturn
[] Stack
0 42
1 1
[] Temps
0 nil
[] Literals
0 100
60
Шаг 7
Context
bytePtr 7
stackTop 1
[] Arguments
0 nil
1 42
[] ByteCodes
0 PushArgument 1
1 PushLiteral 0
2 SendBinary <
3 BranchIfFalse 8
4 PushArgument 1
5 PushConstant 1
6 SendBinary +
7 Branch 9
8 PushConstant 0
9 AssignTemporary 0
10 PopTop
11 PushTemporary 0
12 StackReturn
[] Stack
0 43
1 1
[] Temps
0 nil
[] Literals
0 100
61
Шаг 8
Context
bytePtr 9
stackTop 1
[] Arguments
0 nil
1 42
[] ByteCodes
0 PushArgument 1
1 PushLiteral 0
2 SendBinary <
3 BranchIfFalse 8
4 PushArgument 1
5 PushConstant 1
6 SendBinary +
7 Branch 9
8 PushConstant 0
9 AssignTemporary 0
10 PopTop
11 PushTemporary 0
12 StackReturn
[] Stack
0 43
1 1
[] Temps
0 nil
[] Literals
0 100
62
Шаг 9
Context
bytePtr 10
stackTop 1
[] Arguments
0 nil
1 42
[] ByteCodes
0 PushArgument 1
1 PushLiteral 0
2 SendBinary <
3 BranchIfFalse 8
4 PushArgument 1
5 PushConstant 1
6 SendBinary +
7 Branch 9
8 PushConstant 0
9 AssignTemporary 0
10 PopTop
11 PushTemporary 0
12 StackReturn
[] Stack
0 43
1 1
[] Temps
0 43
[] Literals
0 100
63
Шаг 10
Context
bytePtr 11
stackTop 0
[] Arguments
0 nil
1 42
[] ByteCodes
0 PushArgument 1
1 PushLiteral 0
2 SendBinary <
3 BranchIfFalse 8
4 PushArgument 1
5 PushConstant 1
6 SendBinary +
7 Branch 9
8 PushConstant 0
9 AssignTemporary 0
10 PopTop
11 PushTemporary 0
12 StackReturn
[] Stack
0 43
1 1
[] Temps
0 43
[] Literals
0 100
64
Шаг 11
Context
bytePtr 12
stackTop 1
[] Arguments
0 nil
1 42
[] ByteCodes
0 PushArgument 1
1 PushLiteral 0
2 SendBinary <
3 BranchIfFalse 8
4 PushArgument 1
5 PushConstant 1
6 SendBinary +
7 Branch 9
8 PushConstant 0
9 AssignTemporary 0
10 PopTop
11 PushTemporary 0
12 StackReturn
[] Stack
0 43
1 1
[] Temps
0 43
[] Literals
0 100
65
Мотивация
66
Правда*
*Ложь
67
Наивный JIT
●
Оформляем методы и блоки как функции
●
Транслируем простые инструкции Smalltalk
в IR код LLVM (и в инструкции процессора)
●
Не забываем про обработку SmallInt
●
Посылку сообщений оформляем
в виде обращений к VM
●
Компилируем методы по требованию
68
Базовые определения в IR
%TSize = type { i32 } ; data
%TObject = type {
%TSize, ; size
%TClass*, ; class
[0 x %TObject*] ; fields
}
%TByteObject = type { %TObject }
%TSymbol = type { %TByteObject }
%TString = type { %TByteObject }
%TChar = type {
%TObject,
i32 ; value
}
%TArray = type { %TObject }
%TObjectArray = type { %TObject }
%TSymbolArray = type { %TObject }
%TClass = type {
%TObject,
%TSymbol*, ; name
%TClass*, ; parentClass
%TDictionary*, ; methods
i32, ; instanceSize
%TSymbolArray*, ; variables
%TObject* ; package
}
%TDictionary = type {
%TObject,
%TSymbolArray*, ; keys
%TObjectArray* ; values
}
%TMethod = type {
%TObject,
%TSymbol*, ; name
%TByteObject*, ; byteCodes
%TSymbolArray*, ; literals
i32, ; stackSize
i32, ; temporarySize
%TClass*, ; class
%TString*, ; text
%TObject* ; package
}
%TContext = type {
%TObject,
%TMethod*,
%TObjectArray*, ; arguments
%TObjectArray*, ; temporaries
%TObjectArray*, ; stack
i32, ; bytePointer
i32, ; stackTop
%TContext* ; previousContext
}
69
Примеры базовых функций в IR
define %TObject** @getObjectFieldPtr(%TObject* %object, i32 %index) {
%fields = getelementptr inbounds %TObject* %object, i32 0, i32 2
%fieldPointer = getelementptr inbounds [0 x %TObject*]* %fields, i32 0, i32
%index
ret %TObject** %fieldPointer
}
define %TObject** @setObjectField(%TObject* %object, i32 %index, %TObject* %value) {
%fieldPointer = call %TObject** @getObjectFieldPtr(%TObject* %object, i32 %index)
store %TObject* %value, %TObject** %fieldPointer
ret %TObject** %fieldPointer
}
define void @pushValueToStack(%TContext* %context, i32 %index, %TObject* %value) {
%stackPointer = getelementptr inbounds %TContext* %context, i32 0, i32 4
%stack = load %TObjectArray** %stackPointer
%stackObject = bitcast %TObjectArray* %stack to %TObject*
call %TObject** @setObjectField(%TObject* %stackObject, i32 %index, %TObject*
%value)
ret void
}
70
Анализ причин
●
Большое количество обращений
к памяти (на каждую инструкцию VM)
●
Кэш данных неэффективен
●
Предсказатель переходов не работает
●
Постоянный сброс конвейера
71
Простой цикл
A Loop B
72
Конструкция switch
A
PushConstant
PushLiteral
PushTemporary
PushArgument
PushBlock
SendBinary
SendUnary
SendMessage
B
73
Связь значений на стеке
●
Инструкции, снимающие значения со
стека косвенно зависят от инструкций,
кладущих значения на стек
●
Этот факт можно использовать
●
Все портят инструкции перехода
74
Зависимость по данным
[] ByteCodes
0 PushArgument 1
1 PushLiteral 0
2 SendBinary <
75
Зависимость по данным
[] ByteCodes
0 PushArgument 1
1 PushLiteral 0
2 SendBinary <
76
Исходный байткод
[] ByteCodes
0 PushArgument 1
1 PushLiteral 0
2 SendBinary <
3 BranchIfFalse 8
4 PushArgument 1
5 PushConstant 1
6 SendBinary +
7 Branch 9
8 PushConstant 0
9 AssignTemporary 0
10 PopTop
11 PushTemporary 0
12 StackReturn
77
Разделяем блоки
[] ByteCodes
0 PushArgument 1
1 PushLiteral 0
2 SendBinary <
3 BranchIfFalse 8
4 PushArgument 1
5 PushConstant 1
6 SendBinary +
7 Branch 9
8 PushConstant 0
9 AssignTemporary 0
10 PopTop
11 PushTemporary 0
12 StackReturn
[] Basic block 0
0 PushArgument 1
1 PushLiteral 0
2 SendBinary <
3 BranchIfFalse 8
[] Basic block 4
4 PushArgument 1
5 PushConstant 1
6 SendBinary +
7 Branch 9
[] Basic block 8
8 PushConstant 0
9 AssignTemporary 0
10 PopTop
11 PushTemporary 0
12 StackReturn
78
Инструкции перехода
[] Basic block 0
0 PushArgument 1
1 PushLiteral 0
2 SendBinary <
3 BranchIfFalse 8
[] Basic block 4
4 PushArgument 1
5 PushConstant 1
6 SendBinary +
7 Branch 9
[] Basic block 8
8 PushConstant 0
9 AssignTemporary 0
10 PopTop
11 PushTemporary 0
12 StackReturn
79
Разделяем еще раз
[] Basic block 0
0 PushArgument 1
1 PushLiteral 0
2 SendBinary <
3 BranchIfFalse 8
[] Basic block 4
4 PushArgument 1
5 PushConstant 1
6 SendBinary +
7 Branch 9
[] Basic block 9
9 AssignTemporary 0
10 PopTop
11 PushTemporary 0
12 StackReturn
[] Basic block 8
8 PushConstant 0
Branch 9
80
Корректные базовые блоки
[] Basic block 0
0 PushArgument 1
1 PushLiteral 0
2 SendBinary <
3 BranchIfFalse 8
[] Basic block 4
4 PushArgument 1
5 PushConstant 1
6 SendBinary +
7 Branch 9
[] Basic block 9
9 AssignTemporary 0
10 PopTop
11 PushTemporary 0
12 StackReturn
[] Basic block 8
8 PushConstant 0
Branch 9
81
Корректные базовые блоки
[] Basic block 0
0 PushArgument 1
1 PushLiteral 0
2 SendBinary <
3 BranchIfFalse 8
[] Basic block 4
4 PushArgument 1
5 PushConstant 1
6 SendBinary +
7 Branch 9
[] Basic block 9
9 AssignTemporary 0
10 PopTop
11 PushTemporary 0
12 StackReturn
[] Basic block 8
8 PushConstant 0
Branch 9
82
Граф потока
управления
0.0 : PushArgument 1
0.1 : PushLiteral 0
0.2 : SendBinary <
0
1
0.3 : Special branchIfFalse 12
12.8 : PushConstant 0
target
6.4 : PushArgument 1
skip
12.9 : Special branch 13
14
6.5 : PushConstant 1
6.6 : SendBinary +
0
1
6.7 : Special branch 13
13.10 : AssignTemporary 0
13.11 : Special popTop
13.12 : PushTemporary 0
13.13 : Special stackReturn
Normal flow
Argument
Branching
Entry point
Terminator
test: arg | temp |
temp ← arg < 100
ifTrue: [ arg + 1 ]
ifFalse: [ 0 ].
^temp
83
Граф потока
управления
0.0 : PushArgument 1
0.1 : PushLiteral 0
0.2 : SendBinary <
0
1
0.3 : Special branchIfFalse 12
12.8 : PushConstant 0
target
6.4 : PushArgument 1
skip
12.9 : Special branch 13
6.5 : PushConstant 1
6.6 : SendBinary +
0
1
6.7 : Special branch 13
Normal flow
Argument
Branching
Entry point
Terminator
test: arg | temp |
temp ← arg < 100
ifTrue: [ arg + 1 ]
ifFalse: [ 0 ].
^temp
84
Граф потока
управления
12.8 : PushConstant 0
target
6.4 : PushArgument 1
12.9 : Special branch 13
14
6.5 : PushConstant 1
6.6 : SendBinary +
0
1
6.7 : Special branch 13
13.10 : AssignTemporary 0
13.11 : Special popTop
13.12 : PushTemporary 0
13.13 : Special stackReturn
Normal flow
Argument
Branching
Entry point
Terminator
test: arg | temp |
temp ← arg < 100
ifTrue: [ arg + 1 ]
ifFalse: [ 0 ].
^temp
85
Локальный вывод типов
●
Сообщения литеральным объектам могут
быть посчитаны на этапе компиляции
●
Свертка арифметических выражений
●
Вывод опирается на граф управления
86
Примеры выражений, выводимых локально
●
2 + 3
●
'Hello world' size
●
42 class parent methods at: #absolute
●
Array with: 42 with: true
●
(Array new: 5) at: 2 put: 42
87
Получатели сообщения
●
2 + 3
●
'Hello world' size
●
42 class parent methods at: #absolute
●
Array with: 42 with: true
●
(Array new: 5) at: 2 put: 42
88
nil testSum
Undefined » testSum |sum|
sum ← 0.
1 to: 100 do:
[ :x | sum ← sum + x ].
^sum
89
nil testSum
Undefined » testSum |sum|
sum ← 0.
1 to: 100 do:
[ :x | sum ← sum + x ].
^sum
Number » to: aLimit do: aBlock |i|
i ← self.
[ i ≤ aLimit ] whileTrue:
[ aBlock value: i. i ← i + 1 ]
90
nil testSum
Undefined » testSum |sum|
sum ← 0.
1 to: 100 do:
[ :x | sum ← sum + x ].
^sum
Number » to: aLimit do: aBlock |i|
i ← self.
[ i ≤ aLimit ] whileTrue:
[ aBlock value: i. i ← i + 1 ]
Block » value: aValue
<8 aValue self>
91
Фрагмент графа Undefined » testSum
0.1 : AssignTemporary 0
0.2 : Special popTop
0.3 : PushConstant 1
0.4 : PushLiteral 0
0.6 : MarkArguments 3
0
0.5 : PushBlock 1 1
2
0.7 : SendMessage  to:do:
0.8 : Special popTop
[ ] Instruction Arg
0 PushConstant 0
1 AssignTemporary 0
2 PopTop
3 PushConstant 1
4 PushLiteral 0
5 PushBlock 1
6 MarkArguments 3
7 SendMessage to:do:
8 PopTop
9 PushTemporary 0
10 StackReturn
92
Система типов
●
Позволяет решить задачу
вывода типов в общем виде
●
Гораздо мощнее тривиального вывода
●
Глобальна, хоть и опирается на
локальные контексты
93
Классификация типов
Название Символ Примеры
Literal 42 'Hello'
Monotype ( ) (SmallInt) (String)
Variant ( , ) ( 42, 'Hello' ) ( (String), nil, * )
Array [ , ] Array [ (42, 'Hello'), nil ] Dictionary [ (Array), (Array) ]
Polytype *
Undefined ?
94
Операция объединения
Тип A Тип B Результат A | B
42 42 42
2 3 ( 2, 3 )
nil (SmallInt) ( nil, (SmallInt) )
(SmallInt) (SmallInt) (SmallInt)
42 * ( 42, * )
42 ? ( 42, ? )
95
Операция свертки
Тип A Тип B Результат A & B
42 42 42
2 3 (SmallInt)
nil (SmallInt) *
(SmallInt) (SmallInt) (SmallInt)
42 * *
42 ? ?
96
Назначаем типы
0.1 : AssignTemporary 0
0.2 : Special popTop
0.3 : PushConstant 1
0.4 : PushLiteral 0
0.6 : MarkArguments 3
0
0.5 : PushBlock 1 1
2
0.7 : SendMessage  to:do:
0.8 : Special popTop
0.9 : PushTemporary 0
[] Instruction Arg Type
0 PushConstant 0
1 AssignTemporary 0
2 PopTop
3 PushConstant 1
4 PushLiteral 0
5 PushBlock 1
6 MarkArguments 3
7 SendMessage
to:
do:
8 PopTop
9 PushTemporary 0
10 StackReturn
97
Назначаем типы
0.1 : AssignTemporary 0
0.2 : Special popTop
0.3 : PushConstant 1
0.4 : PushLiteral 0
0.6 : MarkArguments 3
0
0.5 : PushBlock 1 1
2
0.7 : SendMessage  to:do:
0.8 : Special popTop
0.9 : PushTemporary 0
[] Instruction Arg Type
0 PushConstant 0
1 AssignTemporary 0
2 PopTop
3 PushConstant 1 1
4 PushLiteral 0
5 PushBlock 1
6 MarkArguments 3
7 SendMessage
to:
do:
8 PopTop
9 PushTemporary 0
10 StackReturn
98
Назначаем типы
0.1 : AssignTemporary 0
0.2 : Special popTop
0.3 : PushConstant 1
0.4 : PushLiteral 0
0.6 : MarkArguments 3
0
0.5 : PushBlock 1 1
2
0.7 : SendMessage  to:do:
0.8 : Special popTop
0.9 : PushTemporary 0
[] Instruction Arg Type
0 PushConstant 0
1 AssignTemporary 0
2 PopTop
3 PushConstant 1 1
4 PushLiteral 0 100
5 PushBlock 1
6 MarkArguments 3
7 SendMessage
to:
do:
8 PopTop
9 PushTemporary 0
10 StackReturn
99
Назначаем типы
0.1 : AssignTemporary 0
0.2 : Special popTop
0.3 : PushConstant 1
0.4 : PushLiteral 0
0.6 : MarkArguments 3
0
0.5 : PushBlock 1 1
2
0.7 : SendMessage  to:do:
0.8 : Special popTop
0.9 : PushTemporary 0
[] Instruction Arg Type
0 PushConstant 0
1 AssignTemporary 0
2 PopTop
3 PushConstant 1 1
4 PushLiteral 0 100
5 PushBlock 1 Block@1
6 MarkArguments 3
7 SendMessage
to:
do:
8 PopTop
9 PushTemporary 0
10 StackReturn
100
Назначаем типы
0.1 : AssignTemporary 0
0.2 : Special popTop
0.3 : PushConstant 1
0.4 : PushLiteral 0
0.6 : MarkArguments 3
0
0.5 : PushBlock 1 1
2
0.7 : SendMessage  to:do:
0.8 : Special popTop
0.9 : PushTemporary 0
[] Instruction Arg Type
0 PushConstant 0
1 AssignTemporary 0
2 PopTop
3 PushConstant 1 1
4 PushLiteral 0 100
5 PushBlock 1 Block@1
6 MarkArguments 3 [1, 100, Block@1]
7 SendMessage
to:
do:
8 PopTop
9 PushTemporary 0
10 StackReturn
101
Назначаем типы
0.1 : AssignTemporary 0
0.2 : Special popTop
0.3 : PushConstant 1
0.4 : PushLiteral 0
0.6 : MarkArguments 3
0
0.5 : PushBlock 1 1
2
0.7 : SendMessage  to:do:
0.8 : Special popTop
0.9 : PushTemporary 0
[] Instruction Arg Type
0 PushConstant 0
1 AssignTemporary 0
2 PopTop
3 PushConstant 1 1
4 PushLiteral 0 100
5 PushBlock 1 Block@1
6 MarkArguments 3 [1, 100, Block@1]
7 SendMessage
to:
do:
*
8 PopTop
9 PushTemporary 0
10 StackReturn
102
Вывод [1, 100, Block@1] :: Number»to:do
Undefined » testSum |sum|
sum ← 0.
1 to: 100 do:
[ :x | sum ← sum + x ].
^sum
Number » to: aLimit do: aBlock |i|
i ← self.
[ i ≤ aLimit ] whileTrue:
[ aBlock value: i. i ← i + 1 ]
103
Вывод [1, 100, Block@1] :: Number»to:do
Undefined » testSum |sum|
sum ← 0.
1 to: 100 do:
[ :x | sum ← sum + x ].
^sum
Number » to: aLimit do: aBlock |i|
i ← self.
[ i ≤ aLimit ] whileTrue:
[ aBlock value: i. i ← i + 1 ]
104
Вывод [1, 100, Block@1] :: Number»to:do
Undefined » testSum |sum|
sum ← 0.
1 to: 100 do:
[ :x | sum ← sum + x ].
^sum
Number » to: aLimit do: aBlock |i|
i ← self.
[ i ≤ aLimit ] whileTrue:
[ aBlock value: i. i ← i + 1 ]
105
Вывод [1, 100, Block@1] :: Number»to:do
Undefined » testSum |sum|
sum ← 0.
1 to: 100 do:
[ :x | sum ← sum + x ].
^sum
Number » to: aLimit do: aBlock |i|
i ← self.
[ i ≤ aLimit ] whileTrue:
[ aBlock value: i. i ← i + 1 ]
106
Вывод [1, 100, Block@1] :: Number»to:do
Undefined » testSum |sum|
sum ← 0.
1 to: 100 do:
[ :x | sum ← sum + x ].
^sum
Number » to: aLimit do: aBlock |i|
i ← self.
[ i ≤ aLimit ] whileTrue:
[ aBlock value: i. i ← i + 1 ]
107
Полиморфизм операторов
a + b
108
Полиморфизм операторов
a ← a + b
109
Полиморфизм операторов
a ← a + b
testPlus1 |a b|
a ← 42.
b ← 1.
a ← a + b
110
Полиморфизм операторов
a ← a + b
testPlus1 |a b|
a ← 42.
b ← 1.
a ← a + b
testPlus2 |a b|
…
[ … ] whileTrue:
[ a ← a + b ]
111
Вывод типов в циклах
●
Тип результата зависит от типов аргументов
●
Тип выражения зависит от типов
переменных на момент входа
●
Каждая итерация зависит от предыдущей
112
i   1←
i ≤ 100
BranchIfFalse
i   i + 1← Exit
Branch
Упрощенный граф управления
113
i   1←
i ≤ 100
BranchIfFalse
i   i + 1← Exit
Branch
Зависимости выражения i ≤ 100
114
i   1←
i ≤ 100
BranchIfFalse
i   i + 1← Exit
Branch
Зависимости выражения i ≤ 100
115
i   1←
i ≤ 100
BranchIfFalse
i   i + 1← Exit
Branch
Зависимости выражений с i
116
i   1←
i ≤ 100
BranchIfFalse
i   i + 1← Exit
Branch
Добавляем τ-узлы записи
117
i   1←
i ≤ 100
BranchIfFalse
i   i + 1← Exit
Branch
Добавляем τ-узлы записи
W1
W2
118
i   1←
i ≤ 100
BranchIfFalse
i   i + 1← Exit
Branch
Добавляем τ-узел чтения
W1
W2
R
119
i   1←
i ≤ 100
BranchIfFalse
i   i + 1← Exit
Branch
Связываем τ-узлы воедино
W1
W2
R
120
Реальный граф
управления
Number » to: aLimit do: aBlock |i|
i ← self.
[ i ≤ aLimit ] whileTrue: [
aBlock value: i.
i ← i + 1
]
0.0 : PushArgument 0
0.1 : AssignTemporary 0
0.2 : Special popTop 23
0.4 : Special branch 10
10.19 : PushTemporary 0
target
10.20 : PushArgument 1
10.21 : SendBinary <=
0
16.5 : PushArgument 2
16.6 : PushTemporary 0
16.7 : MarkArguments 2
0
1
16.8 : SendMessage  value:
16.9 : Special popTop
16.10 : PushTemporary 0
16.11 : PushConstant 1
16.12 : SendBinary +
0
1
16.13 : AssignTemporary 0
16.14 : Special popTop 24
16.15 : Special branch 10
target
29.18 : Special selfReturn
1
10.22 : Special branchIfFalse 29
skiptarget
25
121
0.0 : PushArgument 0
0.1 : AssignTemporary 0
0.2 : Special popTop 23
0.4 : Special branch 10
10.19 : PushTemporary 0
target
10.20 : PushArgument 1
10.21 : SendBinary <=
0
16.5 : PushArgument 2
16.6 : PushTemporary 0
16.7 : MarkArguments 2
0
1
29.18 : Special selfReturn
1
10.22 : Special branchIfFalse 29
skiptarget
Реальный граф
управления
Number » to: aLimit do: aBlock |i|
i ← self.
[ i ≤ aLimit ] whileTrue: [
aBlock value: i.
i ← i + 1
]
122
16.5 : PushArgument 2
16.6 : PushTemporary 0
16.7 : MarkArguments 2
0
1
16.8 : SendMessage  value:
16.9 : Special popTop
16.10 : PushTemporary 0
16.11 : PushConstant 1
16.12 : SendBinary +
0
1
16.13 : AssignTemporary 0
16.14 : Special popTop 24
16.15 : Special branch 10
target
29.18 : Special selfReturn
skiptarget
25
Реальный граф
управления
Number » to: aLimit do: aBlock |i|
i ← self.
[ i ≤ aLimit ] whileTrue: [
aBlock value: i.
i ← i + 1
]
123
τ-узлы — ключ к выводу типов в циклах
124
Индуктивный вывод типов
●
Вычисляем базу индукции
●
Вычисляем следующую итерацию
●
Выполняем свертку индуктивных типов
125
i   1←
i ≤ 100
BranchIfFalse
i   i + 1← Exit
Branch
Выводим типы τ-узлов
W1
W2
R
τ Type
W1 1
W2 2
R (1, 2)
Rf (SmallInt)
126
Когда вывод не работает
●
Чтение и запись полей
●
Утекающие блоки
●
Модификация иерархии объектов
127
Collection » sort:
sort: criteria | left right mediane |
self size < 2 ifTrue: [ ^self ].
mediane ← self popFirst.
left ← List new.
right ← List new.
self do: [ :x |
(criteria value: x value: mediane)
ifTrue: [ left add: x ]
ifFalse: [ right add: x ] ].
left ← left sort: criteria.
right ← right sort: criteria.
right add: mediane.
^ left appendList: right
128
Еще раз о блоках
Undefined » testSum |sum|
sum ← 0.
1 to: 100 do:
[ :x | sum ← sum + x ].
^sum
129
Без учета записи в блоке
Undefined » testSum |sum|
sum ← 0.
1 to: 100 do:
[ :x | … ].
^sum
130
С учетом записи в блоке
Undefined » testSum |sum|
sum ← 0.
1 to: 100 do:
[ :x | sum ← sum + x ].
^sum
sum   0←
1 to: 100 do: [ ... ]
^ sum
RW
131
Что можно почитать
●
halt.habrahabr.ru/topics/
●
llst.org
●
doc.rust-lang.org
●
blog.llvm.org
●
llvm.org/docs
Спасибо за внимание!

Дмитрий Кашицын, Вывод типов в динамических и не очень языках I