Skip to content

Commit

Permalink
Merge fc6d1dd into ff4d76d
Browse files Browse the repository at this point in the history
  • Loading branch information
0x7CFE committed Jul 25, 2016
2 parents ff4d76d + fc6d1dd commit b604bc8
Show file tree
Hide file tree
Showing 13 changed files with 4,251 additions and 505 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,9 @@ add_library(stapi
src/ParsedBlock.cpp

src/ControlGraph.cpp
src/TauLinker.cpp
src/ControlGraphVisualizer.cpp
src/TypeAnalyzer.cpp
)

set(MM_CPP_FILES
Expand Down
97 changes: 93 additions & 4 deletions doc/types.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
id: param
^param

Ти́повое выражение:
Т́иповое выражение:
self::*, param::* -> self, param


Expand All @@ -26,13 +26,13 @@
^ a + b

Контекст object sum: intvara and: intvarb
self::*, (SmallInt), (SmallInt) -> self, (SmallInt)
self::*, a::(SmallInt), b::(SmallInt) -> self', (SmallInt)

Контекст object sum: 2 and: 3
self::*, a::2, b::3 -> self, 5

Обобщенный метод
self::*, a::*, b::* -> self'::*, *
self::*, a::*, b::* -> self', *

то есть, литеральные значения протаскиваются прямо через тип

Expand Down Expand Up @@ -72,9 +72,10 @@
Контекст object dirty: 42
self::[_,_,_], param::42 -> self'::[_,_,42], 43

Примечание: в случае утекания self, мы не можем утверждать, что self' будет таким. Придется фолбечиться на self'::*


А теперь самый вынос мозга — система контекста виртуальной машине — это монод в категории эндофункторов! Монада то бишь.
А теперь самый вынос мозга — система контекста виртуальной машине — это моноид в категории эндофункторов! Монада то бишь.
Переход виртуальной машины от одного состояния к другому — это монадическая операция.

А это внезапно означает:
Expand Down Expand Up @@ -106,6 +107,7 @@
Монадические отношения в связи с объектом self позволяют формализовать тот факт, что при вызове метода поля самого объекта не меняются.
Это позволяет более смело выполнять оптимизации и связывать далекие участки графа без опасения что типы совпали «случайно».

Поправка — нихрена :( Если self утек в другой объект, то вызывая методы он может поменять и нас. Только при инлайнинге и доказательсве.


Типы методов:
Expand Down Expand Up @@ -184,3 +186,90 @@
Впрочем, ветви кэша кодируются как вызовы специализированных версий методов,
поскольку в пределах ветви класс отправителя (self) становится известен.


Блоки, акт второй.

Классификация блоков относительно типов
ContextFree [ 42 ], [ :x | x + 1 ], [ :x :y | x < y ]
ContextAccessor [ x message ], [ x + 1 ], [ anArgument message: aTemporary ]
ContextMutator [ x <- nil ], [ :x | sum <- sum + x ]

BlockReturn [ ^42 ]

Классификация точки вызова блоков
Never
Once y <- [ :x | x ] value: 42
Maybe <Never + Once>
Multi 1 to: 10 do: [ :x | sum <- sum + x ]
Escape *::anObject take: (Block)::aBlock


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

В наиболее общем случае необходимо выполнять подстановку графа блока в лексический контекст
вызывающего метода в зависимости от того как происходит работа с блоком в теле обработчика сообщения.

Never
Блок не учитывается в выводе типов

Once
Блок подключается параллельно инструкции, принимающей его параметром, например 1 to: 10: do: [ ... ]

Maybe
То же, что и для Once, но есть параллельное ребро мимо блока

Multi
То же, что и Multi, но еще добавляется возвратное ребро в начало блока

Escape
Вызовы этого типа не могут быть представлены в виде графа и блокируют вывод типов для задействованных переменных

После построения inline графа, выполняется проход TauLinker-а на нем,
полученные тау узлы могут быть использованы для вывода типов переменных.


Итак, алгоритм.

Для метода, в лексическом контексте которого объявляется блок:
1. Получаем список переменных, которые мутирует блок (известно после анализа графа блока)
2. Выполняем анализ для каждой посылки, которая использует блок как параметр
3. В точку вызова передаем тип всех временных переменных, используемых блоком (как аргументы)
4. После анализа посылки изучаем контекст на предмет поменявшихся типов переменных
5. Если тип поменялся, то маркируем текущую посылку как мутатор
6. После маркировки всех мутаторов обновляем тау узлы графа и точки их использования
7. Пересчитываем все заново с обновленным графом (новых мутаторов добавиться не должно)

Для метода, принимающего литеральный блок параметром:
1. В каждой посылке, использующей переданный аргументом блок выполняем анализ «как есть»
2. Аккумулируем типы меняющихся переменных
3. Возвращаем аккумулированный результат

Для методов Block>>value* (код обработчика примитива 8)
1. Проводим анализ блока, передав типы аргументов и переменных
2. Получаем типы переменных и результата
3. Возвращаем все добро наверх


Проходы анализатора, в зависимости от типа метода:
1. Метод, не содержащий ветвлений доказывается за 1 проход
2. Метод, с литеральными условиями при ветвлениях доказывается за 1 проход
3. Метод, не содержащий циклов (обратных ребер) доказывается за 1 проход
4. Метод, содержащий циклы, но не имеющий тау узлов с обратными ребрами, доказывается за 1? проход
5. Метод, содержащий циклы и имеющий циклическую зависимость по данным (x <- x + 1), доказывается за 2 прохода

6. Метод, содержащий литеральные блоки, вне циклов: если нет замыканий или только читают — 1 проход, если пишут — минимум 2 прохода
7. Метод, содержащий литеральные блоки, в циклах — минимум 3 прохода
8. Метод, содержащий литеральные блоки, записанные в переменную

Блоки, вызываемые несколько раз (утекающие в поле) не могут быть выведены, без отслеживания полей

9. Метод, содержащий литеральные блоки, утекающие в неизвестность — полная блокировка вывода переменных, которые они пишут


Алгоритм анализатора:
1. Делаем базовый проход без обратных ребер
2. Если предыдущий проход вывел возврат по литеральному пути и метод не содержит блоков мутаторов — выход
3. Делаем базовый проход с нетривиальными мутаторами
4. Если есть литеральный возврат — выход
5. Делаем индукционный переход

50 changes: 26 additions & 24 deletions include/Core.ll
Original file line number Diff line number Diff line change
Expand Up @@ -153,23 +153,23 @@
;;;;;;;;;;;;;;;;;;;; functions ;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

define i1 @isSmallInteger(%TObject* %value) alwaysinline {
define i1 @isSmallInteger(%TObject* %value) alwaysinline nounwind {
; return reinterpret_cast<int32_t>(value) & 1;

%int = ptrtoint %TObject* %value to i32
%result = trunc i32 %int to i1
ret i1 %result
}

define i32 @getIntegerValue(%TObject* %value) alwaysinline {
define i32 @getIntegerValue(%TObject* %value) alwaysinline nounwind {
; return (int32_t) (value >> 1)

%int = ptrtoint %TObject* %value to i32
%result = ashr i32 %int, 1
ret i32 %result
}

define %TObject* @newInteger(i32 %value) alwaysinline {
define %TObject* @newInteger(i32 %value) alwaysinline nounwind {
; return reinterpret_cast<TObject>( (value << 1) | 1 );

%shled = shl i32 %value, 1
Expand All @@ -178,7 +178,7 @@ define %TObject* @newInteger(i32 %value) alwaysinline {
ret %TObject* %result
}

define i32 @getSlotSize(i32 %fieldsCount) alwaysinline {
define i32 @getSlotSize(i32 %fieldsCount) alwaysinline nounwind {
;sizeof(TObject) + fieldsCount * sizeof(TObject*)

%fieldsSize = mul i32 4, %fieldsCount
Expand All @@ -188,44 +188,44 @@ define i32 @getSlotSize(i32 %fieldsCount) alwaysinline {
}


define i32 @getObjectSize(%TObject* %this) alwaysinline {
define i32 @getObjectSize(%TObject* %this) alwaysinline nounwind {
%1 = getelementptr %TObject* %this, i32 0, i32 0, i32 0
%data = load i32* %1
%result = lshr i32 %data, 2
ret i32 %result
}

define %TObject* @setObjectSize(%TObject* %this, i32 %size) alwaysinline {
define %TObject* @setObjectSize(%TObject* %this, i32 %size) alwaysinline nounwind {
%addr = getelementptr %TObject* %this, i32 0, i32 0, i32 0
%ssize = shl i32 %size, 2
store i32 %ssize, i32* %addr
ret %TObject* %this
}

define i1 @isObjectRelocated(%TObject* %this) alwaysinline {
define i1 @isObjectRelocated(%TObject* %this) alwaysinline nounwind {
%1 = getelementptr %TObject* %this, i32 0, i32 0, i32 0
%data = load i32* %1
%field = and i32 %data, 1
%result = trunc i32 %field to i1
ret i1 %result
}

define i1 @isObjectBinary(%TObject* %this) alwaysinline {
define i1 @isObjectBinary(%TObject* %this) alwaysinline nounwind {
%1 = getelementptr %TObject* %this, i32 0, i32 0, i32 0
%data = load i32* %1
%field = and i32 %data, 2
%result = icmp ne i32 %field, 0
ret i1 %result
}

define %TClass** @getObjectClassPtr(%TObject* %this) alwaysinline {
define %TClass** @getObjectClassPtr(%TObject* %this) alwaysinline nounwind {
%pclass = getelementptr inbounds %TObject* %this, i32 0, i32 1
ret %TClass** %pclass
}

@SmallInt = external global %TClass

define %TClass* @getObjectClass(%TObject* %this) alwaysinline {
define %TClass* @getObjectClass(%TObject* %this) alwaysinline nounwind {
; TODO SmallInt case
%test = call i1 @isSmallInteger(%TObject* %this)
br i1 %test, label %is_smallint, label %is_object
Expand All @@ -237,44 +237,44 @@ is_object:
ret %TClass* %class
}

define %TObject* @setObjectClass(%TObject* %this, %TClass* %class) alwaysinline {
define %TObject* @setObjectClass(%TObject* %this, %TClass* %class) alwaysinline nounwind {
%addr = call %TClass** @getObjectClassPtr(%TObject* %this)
store %TClass* %class, %TClass** %addr
ret %TObject* %this
}

define %TObject** @getObjectFieldPtr(%TObject* %object, i32 %index) alwaysinline {
define %TObject** @getObjectFieldPtr(%TObject* %object, i32 %index) alwaysinline nounwind {
%fields = getelementptr inbounds %TObject* %object, i32 0, i32 2
%fieldPtr = getelementptr inbounds [0 x %TObject*]* %fields, i32 0, i32 %index
ret %TObject** %fieldPtr
}

define %TObject** @getObjectFields(%TObject* %this) alwaysinline {
define %TObject** @getObjectFields(%TObject* %this) alwaysinline nounwind {
%fieldsPtr = call %TObject** @getObjectFieldPtr(%TObject* %this, i32 0)
ret %TObject** %fieldsPtr
}

define %TObject* @getObjectField(%TObject* %object, i32 %index) alwaysinline {
define %TObject* @getObjectField(%TObject* %object, i32 %index) alwaysinline nounwind {
%fieldPtr = call %TObject** @getObjectFieldPtr(%TObject* %object, i32 %index)
%result = load %TObject** %fieldPtr
ret %TObject* %result
}

define %TObject** @setObjectField(%TObject* %object, i32 %index, %TObject* %value) alwaysinline {
define %TObject** @setObjectField(%TObject* %object, i32 %index, %TObject* %value) alwaysinline nounwind {
%fieldPtr = call %TObject** @getObjectFieldPtr(%TObject* %object, i32 %index)
store %TObject* %value, %TObject** %fieldPtr
ret %TObject** %fieldPtr
}

define %TObject* @getArgFromContext(%TContext* %context, i32 %index) alwaysinline {
define %TObject* @getArgFromContext(%TContext* %context, i32 %index) alwaysinline nounwind {
%argsPtr = getelementptr inbounds %TContext* %context, i32 0, i32 2
%args = load %TObjectArray** %argsPtr
%argsObj = bitcast %TObjectArray* %args to %TObject*
%arg = call %TObject* @getObjectField(%TObject* %argsObj, i32 %index)
ret %TObject* %arg
}

define %TObject* @getLiteralFromContext(%TContext* %context, i32 %index) alwaysinline {
define %TObject* @getLiteralFromContext(%TContext* %context, i32 %index) alwaysinline nounwind {
%methodPtr = getelementptr inbounds %TContext* %context, i32 0, i32 1
%method = load %TMethod** %methodPtr
%literalsPtr = getelementptr inbounds %TMethod* %method, i32 0, i32 3
Expand All @@ -284,40 +284,40 @@ define %TObject* @getLiteralFromContext(%TContext* %context, i32 %index) alwaysi
ret %TObject* %literal
}

define %TObject* @getTempsFromContext(%TContext* %context) alwaysinline {
define %TObject* @getTempsFromContext(%TContext* %context) alwaysinline nounwind {
%tempsPtr = getelementptr inbounds %TContext* %context, i32 0, i32 3
%temps = load %TObjectArray** %tempsPtr
%tempsObj = bitcast %TObjectArray* %temps to %TObject*
ret %TObject* %tempsObj
}

define %TObject* @getTemporaryFromContext(%TContext* %context, i32 %index) alwaysinline {
define %TObject* @getTemporaryFromContext(%TContext* %context, i32 %index) alwaysinline nounwind {
%temps = call %TObject* @getTempsFromContext(%TContext* %context)
%temporary = call %TObject* @getObjectField(%TObject* %temps, i32 %index)
ret %TObject* %temporary
}

define void @setTemporaryInContext(%TContext* %context, i32 %index, %TObject* %value) alwaysinline {
define void @setTemporaryInContext(%TContext* %context, i32 %index, %TObject* %value) alwaysinline nounwind {
%temps = call %TObject* @getTempsFromContext(%TContext* %context)
call %TObject** @setObjectField(%TObject* %temps, i32 %index, %TObject* %value)
ret void
}

define %TObject* @getInstanceFromContext(%TContext* %context, i32 %index) alwaysinline {
define %TObject* @getInstanceFromContext(%TContext* %context, i32 %index) alwaysinline nounwind {
%self = call %TObject* @getArgFromContext(%TContext* %context, i32 0)
%instance = call %TObject* @getObjectField(%TObject* %self, i32 %index)
ret %TObject* %instance
}

define void @setInstanceInContext(%TContext* %context, i32 %index, %TObject* %value) alwaysinline {
define void @setInstanceInContext(%TContext* %context, i32 %index, %TObject* %value) alwaysinline nounwind {
%self = call %TObject* @getArgFromContext(%TContext* %context, i32 0)
%instancePtr = call %TObject** @getObjectFieldPtr(%TObject* %self, i32 %index)
call void @checkRoot(%TObject* %value, %TObject** %instancePtr)
store %TObject* %value, %TObject** %instancePtr
ret void
}

define void @dummy() gc "shadow-stack" {
define void @dummy() nounwind gc "shadow-stack" {
; enabling shadow stack init on this module
ret void
}
Expand All @@ -336,7 +336,7 @@ declare %TByteObject* @newBinaryObject(%TClass*, i32)
;declare %TObject* @sendMessage(%TContext* %callingContext, %TSymbol* %selector, %TObjectArray* %arguments, %TClass* %receiverClass, i32 %callSiteOffset)
declare { %TObject*, %TContext* } @sendMessage(%TContext* %callingContext, %TSymbol* %selector, %TObjectArray* %arguments, %TClass* %receiverClass, i32 %callSiteOffset)

declare %TBlock* @createBlock(%TContext* %callingContext, i8 %argLocation, i16 %bytePointer)
declare %TBlock* @createBlock(%TContext* %callingContext, i8 %argLocation, i16 %bytePointer, i32 %stackTop)
declare { %TObject*, %TContext* } @invokeBlock(%TBlock* %block, %TContext* %callingContext)
;declare %TObject* @invokeBlock(%TBlock* %block, %TContext* %callingContext)

Expand All @@ -350,6 +350,8 @@ declare %TObject* @callPrimitive(i8 %opcode, %TObjectArray* %args, i1* %primitiv
%TContext* ; targetContext
}

declare i32 @printf(i8* noalias nocapture, ...)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;; exception API ;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Expand Down
Loading

0 comments on commit b604bc8

Please sign in to comment.