http://sscript.4programmers.net/download/23-07-2013.zip
Voilà - w ten oto sposób prezentuję przed Wami kolejne pełne wydanie pełne równie "fajnych" zmian, jak i poprzednio (hm, w gruncie rzeczy mógłbym wymyślać jakieś fajne nazwy głównych branchy, jak np.ma Android :D).
Kompilator 2.2.3
:
-
SSA zostało zaimplementowane! :) (chociaż w imo słaby sposób - części związane z SSA mam na mojej liście rzeczy do refatoryzacji; bez zapasu tabletek na ból głowy nie polecam oglądać zawartości
ssa_stage1.pas
oraz ssa_stage2.pas
...)
- naprawione zostały wzajemne zależności, teraz już działają poprawnie (tzn.bezproblemowo może istnieć bezpośrednia wzajemna zależność pomiędzy dwoma plikami (oraz np.procedurami wewnątrz nich) i nie jest to żaden błąd, a wręcz przeciwnie: kompilator wtedy działa jak najbardziej poprawnie i kompiluje wszystko bezproblemowo ;) Kilkanaście godzin myślenia nad rozwiązaniem się jednak opłaciło ;>)
- delikatnie zmieniona została składnia typów enumerycznych:
type<enum> foo = [enum1, enum2, enum3];
wygląda imo lepiej, niżeli type<enum> foo = {enum1, enum2, enum3};
- doszło parę istotnych zmian w optymalizacjach (tzn.pomijając te, które zostały usprawnione przez SSA): zaimplementowałem optymalizator pojedynczych wyrażeń (aktywowany
--tree-simplify
z linii poleceń i/lub w trybie -O1
). Optymalizacja ta stara się maksymalnie uprościć każde pojedyncze wyrażenie, np.:
-
x = x + 50;
zostaje zamienione do x += 50;
*,
-
x = 10*x + 50*x;
zostaje zamienione do x *= 60;
*,
-
x = x;
jest zamieniane na puste wyrażenie,
- plus parę innych; jeżeli ktoś chce poznać wszystkie, może sobie przejrzeć: https://github.com/Piterolex/SScript-Compiler/blob/2.2.3-stable/compiler/optimization/tree_simplification.pas#L87 (spokojnie, są tam ogólne komentarze ;P)
W ramach ciekawostki dodam, że w/w optymalizator np.w GCC to plik simplify-rtx.c
posiadający ~6.2 tysięcy linii (https://github.com/mirrors/gcc/blob/master/gcc/simplify-rtx.c - nie polecam otwierać na Operze :P), mój posiada "zaledwie" 360, ale cóż: zawsze to coś! ;>
Wirtualna maszyna 0.3.3
:
- zaimplementowano
garbage-collector
- yay! Póki co jest to prosta wersja zwykłego mark-and-sweep
, lecz potem planuję dodać dodatkowo zliczanie referencji (jako drugą metodę; bodajże podobnie ma JVM). Dla testu można spróbować uruchomić np.taki kod:
function<int> main()
{
while (true)
new int[100000];
}
(+- te kilka zer przy tym new
) i patrzeć na wykres użycia pamięci np.w menedżerze zadań Windows: domyślnie GC uruchamia się po osiągnięciu limitu 256 MB pamięci, lecz można to zmienić za pomocą przełącznika -gc
(lub z poziomu ustawień wirtualnej maszyny w edytorze).
Zmian edytora raczej podawać nie muszę, widać w postach powyżej ;)
Moja aktualna lista TODO prezentuje się tak:
- na pewno na pierwszy ogień idzie załatanie tych wszystkich memleaków... zaczynam powoli żałować, że nie zaimplementowałem tego w jakimś języku z wbudowanym GC, bo teraz muszę sam dbać o zwalnianie pamięci, więc póki co cały kompilator to jeden wielki memleak. Phi, fixnie się w wolnym czasie ;]
- potem poprawa wydajności; ten release powinien być nieco wydajniejszy od poprzednich, lecz mimo wszystko mój kompilator jest wolny jak cholera :P
- optymalizacje dla tablic
- konstrukcja
var<int[]> tab = {1, 2, 3, 4, 5};
(o której było już w którymś z tematów, który założyłem)
- usprawnić generator kodu (póki co wypluwa sporą liczbę zbędnych opcodów, które optymalizator bajtkodu nie zawsze wyłapuje)
- optymalizacje międzyfunkcyjne; np.:
function<int> callme()
{
return 10;
}
function<int> main()
{
var<int> x = 5*callme(); // wartość wywołania "callme();" jest z góry znana, zatem można by od razu podłożyć i policzyć tę wartość
}
Mam jeszcze listę takich "może kiedyś":
- SSA dla bajtkodu
- operator
?
(na takiej samej zasadzie, jak w C++: wyrażenie?prawda:fałsz
)
Cóż, sporo pozycji na tej liście nie ma :]
Ach, oraz oczywiście parę screenshotów:
function<void> callme(int param)
{
}
function<int> main()
{
for (var<int> i=0; i<10; i++)
callme(i);
return 0;
}
W formie grafu to:
Forma SSA może wydawać się nieco dziwna, lecz wygląda to tak:
zmienna.(pre-ssa)$(post-ssa)
pre-ssa
to SSA-ID, które są używane przez zmienną
post-ssa
to SSA-ID, które są zwracane przez zmienną (dla operatora przypisania zawsze pre-ssa=post-ssa
, różnica jest w przypadku całej reszty)
Musiałem wprowadzić dwa oznaczenia z racji na operatory ++
, +=
i pochodne, ponieważ np.: wyrażenia i++;
nie można by przedstawić w formie i$(0)
, bo jest to zarówno przypisanie, jak i wykorzystanie wartości zmiennej i
. Ot, lubię takie niemainstreamowe rozwiązania ;)
function<int> callme()
{
return 0;
}
function<int> main()
{
var<int> x = 0;
if (callme())
x = 10; else
x = 20;
if (x%2)
x++;
return x;
}
Uff, powoli całość zaczyna chyba nawet przypominać jakiś "normalny" kompilator :D
Mimo wszystko muszę sobie zrobić jakąś przerwę od kodzenia tego: są wakacje, a ja przesiaduję przed komputerem** rozmyślając, jak by usprawnić ten kompilator c:
`*` oraz ofc.obliczane w momencie kompilacji, gdy "x" jest znane i odpowiednia optymalizacja włączona (w tym przypadku `--constant-propagation` oraz `--constant-folding` (lub po prostu `-O2`)).
`**` oczywiście wychodzę również na dwór/rower etc., tak żeby nie było :D
Well, to tyle na teraz - jak wam się podoba/co myślicie? ;)