Projekt kompilatora - środowisko wykonawcze
Program jako kod źródłowy jest jedynie zbiorem tekstu (kod, instrukcje itp.) I aby go ożywić, wymaga wykonania działań na maszynie docelowej. Program potrzebuje zasobów pamięci do wykonywania instrukcji. Program zawiera nazwy procedur, identyfikatorów itp., Które wymagają odwzorowania na rzeczywistą lokalizację pamięci w czasie wykonywania.
Przez środowisko uruchomieniowe rozumiemy program w trakcie wykonywania. Środowisko wykonawcze to stan maszyny docelowej, który może obejmować biblioteki oprogramowania, zmienne środowiskowe itp. W celu świadczenia usług procesom działającym w systemie.
System wsparcia runtime to pakiet, w większości generowany przez sam program wykonywalny, który ułatwia komunikację procesu pomiędzy procesem a środowiskiem wykonawczym. Zajmuje się alokacją i cofnięciem alokacji pamięci podczas wykonywania programu.
Drzewa aktywacji
Program to sekwencja instrukcji połączonych w szereg procedur. Instrukcje w procedurze są wykonywane sekwencyjnie. Procedura ma ogranicznik początku i końca, a wszystko, co się w niej znajduje, nazywane jest treścią procedury. Identyfikator procedury i sekwencja skończonych instrukcji wewnątrz niego tworzą treść procedury.
Wykonanie procedury nazywa się jej aktywacją. Rekord aktywacji zawiera wszystkie niezbędne informacje wymagane do wywołania procedury. Rekord aktywacji może zawierać następujące jednostki (w zależności od używanego języka źródłowego).
Tymczasowe | Przechowuje tymczasowe i pośrednie wartości wyrażenia. |
Dane lokalne | Przechowuje lokalne dane wywoływanej procedury. |
Stan maszyny | Przechowuje stan maszyny, taki jak rejestry, licznik programów itp., Przed wywołaniem procedury. |
Control Link | Przechowuje adres rekordu aktywacji procedury dzwoniącego. |
Łącze dostępu | Przechowuje informacje o danych, które są poza zakresem lokalnym. |
Rzeczywiste parametry | Przechowuje aktualne parametry, tj. Parametry używane do wysyłania danych wejściowych do wywoływanej procedury. |
Wartość zwracana | Przechowuje wartości zwracane. |
Za każdym razem, gdy wykonywana jest procedura, jej rekord aktywacji jest przechowywany na stosie, znanym również jako stos kontrolny. Gdy procedura wywołuje inną procedurę, wykonywanie obiektu wywołującego jest zawieszane do momentu zakończenia wykonywania wywoływanej procedury. W tym momencie rekord aktywacji wywoływanej procedury jest przechowywany na stosie.
Zakładamy, że sterowanie programem przepływa sekwencyjnie i gdy wywoływana jest procedura, jej sterowanie jest przekazywane do wywoływanej procedury. Gdy wywoływana procedura jest wykonywana, zwraca sterowanie z powrotem do obiektu wywołującego. Ten typ przepływu sterowania ułatwia przedstawienie serii aktywacji w postaci drzewa, znanego jakoactivation tree.
Aby zrozumieć tę koncepcję, weźmy fragment kodu jako przykład:
. . .
printf(“Enter Your Name: “);
scanf(“%s”, username);
show_data(username);
printf(“Press any key to continue…”);
. . .
int show_data(char *user)
{
printf(“Your name is %s”, username);
return 0;
}
. . .
Poniżej znajduje się drzewo aktywacji podanego kodu.
Teraz rozumiemy, że procedury są wykonywane w pierwszej kolejności w głąb, więc alokacja stosu jest najlepszą odpowiednią formą przechowywania dla aktywacji procedur.
Alokacja pamięci
Środowisko wykonawcze zarządza wymaganiami dotyczącymi pamięci w czasie wykonywania dla następujących jednostek:
Code: Jest znany jako tekstowa część programu, która nie zmienia się w czasie wykonywania. Jego wymagania dotyczące pamięci są znane w czasie kompilacji.
Procedures: Część tekstowa jest statyczna, ale są wywoływane w losowy sposób. Dlatego do zarządzania wywołaniami procedur i aktywacjami służy magazyn stosu.
Variables: Zmienne są znane tylko w czasie wykonywania, chyba że są globalne lub stałe. Schemat alokacji pamięci sterty służy do zarządzania alokacją i cofaniem alokacji pamięci dla zmiennych w czasie wykonywania.
Alokacja statyczna
W tym schemacie alokacji dane kompilacji są powiązane ze stałą lokalizacją w pamięci i nie zmieniają się podczas wykonywania programu. Ponieważ wymagania dotyczące pamięci i miejsca przechowywania są znane z góry, pakiet obsługi środowiska wykonawczego do alokacji i cofania alokacji nie jest wymagany.
Alokacja stosu
Wywołania procedur i ich aktywacje są zarządzane poprzez alokację pamięci stosu. Działa w metodzie Last-In-First-Out (LIFO) i ta strategia alokacji jest bardzo przydatna w przypadku wywołań procedur rekurencyjnych.
Alokacja sterty
Zmienne lokalne dla procedury są przydzielane i cofane tylko w czasie wykonywania. Alokacja sterty służy do dynamicznego przydzielania pamięci do zmiennych i odbierania jej z powrotem, gdy zmienne nie są już potrzebne.
Z wyjątkiem obszaru pamięci przydzielonego statycznie, zarówno pamięć stosu, jak i sterty mogą rosnąć i kurczyć się dynamicznie i nieoczekiwanie. Dlatego nie można im zapewnić stałej ilości pamięci w systemie.
Jak pokazano na powyższym obrazku, część tekstowa kodu ma przydzieloną stałą ilość pamięci. Pamięć stosu i sterty są rozmieszczone na krańcach całkowitej pamięci przydzielonej programowi. Obie kurczą się i rosną przeciwko sobie.
Przekazywanie parametru
Medium komunikacyjne między procedurami jest znane jako przekazywanie parametrów. Wartości zmiennych z procedury wywołującej są przenoszone do wywoływanej procedury przez jakiś mechanizm. Zanim przejdziesz dalej, zapoznaj się najpierw z podstawowymi terminologiami dotyczącymi wartości w programie.
wartość r
Wartość wyrażenia nazywana jest jego wartością r. Wartość zawarta w jednej zmiennej również staje się wartością r, jeśli pojawia się po prawej stronie operatora przypisania. Wartości-r można zawsze przypisać do jakiejś innej zmiennej.
Wartość l
Lokalizacja pamięci (adres), w której przechowywane jest wyrażenie, nazywana jest wartością l tego wyrażenia. Zawsze pojawia się po lewej stronie operatora przypisania.
Na przykład:
day = 1;
week = day * 7;
month = 1;
year = month * 12;
Z tego przykładu rozumiemy, że stałe wartości, takie jak 1, 7, 12 i zmienne, takie jak dzień, tydzień, miesiąc i rok, mają wartości r. Tylko zmienne mają l-wartości, ponieważ reprezentują również przypisaną im lokalizację pamięci.
Na przykład:
7 = x + y;
jest błędem wartości l, ponieważ stała 7 nie reprezentuje żadnego miejsca w pamięci.
Parametry formalne
Zmienne pobierające informacje przekazane przez procedurę wywołującą nazywane są parametrami formalnymi. Te zmienne są zadeklarowane w definicji wywoływanej funkcji.
Rzeczywiste parametry
Zmienne, których wartości lub adresy są przekazywane do wywoływanej procedury, nazywane są parametrami rzeczywistymi. Te zmienne są określone w wywołaniu funkcji jako argumenty.
Example:
fun_one()
{
int actual_parameter = 10;
call fun_two(int actual_parameter);
}
fun_two(int formal_parameter)
{
print formal_parameter;
}
Parametry formalne przechowują informacje o rzeczywistym parametrze, w zależności od zastosowanej techniki przekazywania parametrów. Może to być wartość lub adres.
Podaj wartość
W mechanizmie przekazywania wartości procedura wywołująca przekazuje wartość r rzeczywistych parametrów, a kompilator umieszcza ją w rekordzie aktywacji wywoływanej procedury. Następnie parametry formalne przechowują wartości przekazane przez procedurę wywołującą. Jeżeli wartości przechowywane przez parametry formalne ulegną zmianie, nie powinno to mieć wpływu na parametry rzeczywiste.
Przekaż przez odniesienie
W przejściu przez mechanizm odniesienia wartość l rzeczywistego parametru jest kopiowana do rekordu aktywacji wywoływanej procedury. W ten sposób wywołana procedura ma teraz adres (lokalizację w pamięci) aktualnego parametru, a parametr formalny odnosi się do tej samej lokalizacji pamięci. Dlatego też, jeśli wartość wskazywana przez parametr formalny ulegnie zmianie, wpływ należy zobaczyć na rzeczywisty parametr, ponieważ powinny one również wskazywać na tę samą wartość.
Przejdź przez kopiowanie-przywracanie
Ten mechanizm przekazywania parametrów działa podobnie do „przekazywania przez referencję”, z tą różnicą, że zmiany rzeczywistych parametrów są dokonywane po zakończeniu wywoływanej procedury. Po wywołaniu funkcji wartości rzeczywistych parametrów są kopiowane do rekordu aktywacji wywoływanej procedury. Parametry formalne, jeśli są manipulowane, nie mają wpływu w czasie rzeczywistym na parametry rzeczywiste (w miarę przekazywania l-wartości), ale po zakończeniu wywoływanej procedury wartości l parametrów formalnych są kopiowane do l-wartości parametrów rzeczywistych.
Example:
int y;
calling_procedure()
{
y = 10;
copy_restore(y); //l-value of y is passed
printf y; //prints 99
}
copy_restore(int x)
{
x = 99; // y still has value 10 (unaffected)
y = 0; // y is now 0
}
Po zakończeniu tej funkcji wartość l parametru formalnego x jest kopiowana do rzeczywistego parametru y. Nawet jeśli wartość y zostanie zmieniona przed zakończeniem procedury, wartość l z x jest kopiowana do wartości l z y, dzięki czemu zachowuje się jak wywołanie przez odniesienie.
Być znanym pod imieniem
Języki takie jak Algol zapewniają nowy rodzaj mechanizmu przekazywania parametrów, który działa jak preprocesor w języku C. W mechanizmie przechodzenia przez nazwę nazwa wywoływanej procedury jest zastępowana jej treścią. Przekazywanie nazwy tekstowo zastępuje wyrażenia argumentów w wywołaniu procedury dla odpowiednich parametrów w treści procedury, dzięki czemu może teraz działać na rzeczywistych parametrach, podobnie jak przekazywanie przez odwołanie.