Architektura Komputerów/Wykład 3: Synteza modelu programowego

Plan wykładu




Na wstępie zidentyfikujemy wymagania, jakie stawia język wysokiego poziomu przez modelem logicznym procesora i komputera.


Pokazane zostanie odwzorowanie obiektów występujących w programie w obiekty obecne w pamięci podczas wykonania programu.


Następnie zbudujemy model programowy prostego procesora, nadający się do implementacji podstawowych mechanizmów języka wysokiego poziomu.


Na końcu zostanie pokazane przejście od modelu przykładowego procesora do rzeczywistego modelu programowego procesorów rodziny x86.



Języki wysokiego poziomu





Głównym celem projektantów pierwszych komputerów było uzyskanie urządzenia, które byłoby w stanie działać bezawaryjnie wystarczająco długo, aby wykonać potrzebne obliczenia. Czasy międzyawaryjne pierwszych komputerów nie przekraczały kilkudziesięciu minut, a ich struktura logiczna była podporządkowana uwarunkowaniom implementacyjnym.


Wkrótce po upowszechnieniu komputerów ustabilizowały się metody ich programowania. Obecnie powszechnie używa się języków wysokiego poziomu - proceduralnych i obiektowych. Współczesne komputery są budowane z myślą o programowaniu ich w takich właśnie językach, a ich struktura logiczna jest zaprojektowana tak, aby mogły one łatwo i wydajnie wykonywać programy powstałe przez translację zapisu algorytmów w językach wysokiego poziomu.


Pomimo odrębnych paradygmatów programowania, języki obiektowe od strony implementacji nie różnią się znacząco od języków proceduralnych. Możny przyjąć, że komputer, który daje się efektywnie programować w języku C będzie podobnie skutecznie wykonywał programy napisane w innych podobnych językach proceduralnych i obiektowych, takich jak np. Pascal, C++ czy Java.



Program w języku wysokiego poziomu




Przykładowy program w języku C zawiera podstawowe obiekty i mechanizmy występujące w typowych programach.


Główna procedura programu wywołuje inne procedury, przekazując do nich argumenty i odbierając wartości.


W programie występują dane o różnym zasięku i czasie życia: zmienne zewnętrzne x, y i p; zmienne lokalne i argumenty procedur ora zmienna wskazywana przez zmienną wskaźnikową p.



Wymagania języka wysokiego poziomu



  • Klasy pamięci (sekcje)
    • Odpowiadają różnym klasom obiektów niezbędnych do działania programu
  • Przekazywanie sterowania
    • Wywołania procedur i powroty
    • Przekazywanie parametrów
    • Zmienne lokalne procedur

Wykonanie programu napisanego w języku wysokiego poziomu wymaga umieszczenia w pamięci komputera wszystkich występujących w programie obiektów oraz realizacji mechanizmów związanych z wywoływaniem procedur.


Poszczególne rodzaje obiektów – instrukcje i dane – będą odwzorowane w odrębne sekcje, czyli klasy pamięci.


Wywoływanie procedur wymaga zrealizowania mechanizmów przekazywania argumentów, przekazywania i zwracania sterowania oraz zwracania wartości funkcji.



Klasy pamięci - sekcje



  • Kod
  • Dane statyczne
    • Można wyróżnić 3 sekcje:
      • Stałe
      • Zmienne zainicjowane
      • Zmienne niezainicjowane (BSS)
  • Dane dynamiczne automatyczne 
  • Dane dynamiczne kontrolowane
  • We współczesnych systemach operacyjnych mogą dodatkowo występować inne sekcje
    • Kod współdzielony (biblioteki dynamiczne)
    • Dane współdzielone - obszary komunikacji międzyprocesowej. pliki odwzorowane w pamięci (MMF)

Podczas wykonania programu w pamięci znajdą się obiekty czterech podstawowych rodzajów, odwzorowane w różne klasy pamięci.


Klasa kodu będzie zawierała instrukcje tworzące program.


Klasa danych statycznych zawiera dane zadeklarowane na poziomie zewnętrznym.


Klasa danych automatycznych zawiera argumenty wywołania i zmienne lokalne procedur.


Klasa danych kontrolowanych zawiera dane dynamiczne jawnie tworzone i usuwane przez programistę.



Kod (sekcja TEXT)




  • Statyczny, obecny w pamięci przez cały czas życia programu 
  • Stały rozmiar 
  • Tylko do odczytu 
  • Może zawierać:
    • instrukcje programu
    • stałe. np.
      • tablice adresów dla instrukcji switch
      • literały


Kodu programu w postaci ciągów instrukcji jest odwzorowany w sekcję kodu, występującą często pod nazwą TEXT.


Sekcja ta nie zmienia swoich rozmiarów ani zawartości przez cały czas wykonywania programu. Jest ona tworzona przed rozpoczęciem wykonywania programu, a niszczona po jego zakończeniu. Z punktu widzenia wykonywanego programu „żyje” ona ciągle, a więc jest statyczna.


Oprócz samych instrukcji sekcja może zawierać stałe niezbędne do działania programu, w tym m.in. adresy ciągów instrukcji odpowiedzialnych za poszczególne ścieżki instrukcji złożonych typu switch oraz stałe (literały) występujące w programie, które nie mogą być zapisane bezpośrednio jako argumenty natychmiastowe.



Dane statystyczne (STATIC)




  • Czas życia równy czasowi życia programu 
  • Stały rozmiar
  • Mogą być podzielone na obszary
    • Stałe - tylko do odczytu
    • Zmienne zainicjowane - możliwy odczyt i zapis, nadane wartości początkowe
    • Zmienne niezainicjowane - bez wartości początkowych
      • nazwa sekcji - "BSS" - pochodzi od Block Started by Symbol

Sekcja danych statycznych zawiera dane deklarowane w języku C na poziomie zewnętrznym oraz dane deklarowane lokalnie wewnątrz procedur ze słowem kluczowym static.


Sekcja danych statycznych, podobnie jak sekcja kodu, ma stały rozmiar i istnieje przez cały czas wykonania programu. W sekcji tej mogą jednak występować obiekty o różnych dozwolonych rodzajach dostępu i odmiennych sposobach inicjowania. Wygodnie jest podzielić sekcję danych statycznych na trzy „podsekcje” o różnych atrybutach.



Dane dynamiczne automatyczne



  • Argumenty i zmienne lokalne procedur
  • Tworzone i usuwane w czasie działania programu - zmienny rozmiar
  • Kolejność usuwania zawsze odwrotna do kolejności tworzenia 
  • Tworzą stos (stack)

Sekcja danych dynamicznych automatycznych służy do przechowywania argumentów procedur i ich zmiennych lokalnych. Są one powoływane do życia w miarę zagłębiania wywołań procedur i usuwane podczas kończenia wykonania procedur. Kolejność usuwania jest zawsze odwrotna do kolejności tworzenia. Dane te tworzą strukturę danych zwaną stosem.


Dane dynamiczne kontrolowane



  • Tworzone i usuwane jawnie przez programistę (malloc/free, new/dispose)
  • Czas życia nie związany z czasem życia i zagłębianiem procedur, nie wynika ze struktury wywołań
  • Kolejność tworzenia i usuwania - dowolna
  • Tworzą stertę (heap)

Dane dynamiczne kotrolowane są alokowane i dealokowane jawnie przez programistę, a ich czas życia nie ma bezpośredniego związku z zagłębianiem procedur. Przed rozpoczęciem dealokacji obszar tych danych zachowuje się podobnie do stosu, jednak kolejność dealokacji może być dowolna, a nowe obiekty mogą być alokowane w miejscu zwolnionym przez poprzednie. Dane dynamiczne kontrolowane tworzą strukturę danych zwaną stertą.


Mapa przestrzeni adresowej procesu użytkowego



  • Cztery główne obszary - kod, dane statyczne, stos i sterta
  • Sterta rośnie zwykle w górę, a stos - w dół
  • Adresy bliskie zera nie są używane
    • Umożliwia to wykrywanie odwołań wykonywanych przy użyciu niezainicjowanych wskaźników
  • Duże puste obszary w przestrzeni adresowej
    • Przestrzeń adresowa to nie to samo. co pamięć
      • Adresom nie muszą odpowiadać fizyczne komórki pamięci

Cztery obszary/klasy obiektów – kod, dane statyczne, stos i sterta – muszą być podczas wykonania programu odwzorowane w przestrzeni adresowej procesora. Na ogół przyjmuje się, że sterta rośnie w kierunku rosnących adresów, a stos – w kierunku adresów malejących.


Kolejność rozmieszczenia sekcji w przestrzeni adresowej oraz wartości ich adresów początkowych zależy od decyzji projektanta systemu operacyjnego. Adresy bliskie zera pozostają wolne, a dostęp do nich nie jest dozwolony. Dzięki temu próby odwołań przy użyciu niezainicjowanych zmiennych wskaźnikowych lub wskaźników o wartości NULL (zwykle równej 0) są wychwytywane i sygnalizowane jako błędy wykonania.


Poszczególne sekcje ne muszą sąsiadować ze sobą w przestrzeni adresowej. Przestrzeń adresowa – to tylko zbiór adresów,a adresy nie muszą być odwzorowane w fizyczne lokacje pamięci, stąd puste obszary przestrzeni adresowej pomiędzy sekcjami nie zajmują pamięci komputera.



Licznik instrukcji (PC - program counter)




  • Rejestr przechowujący adres kolejnej instrukcji, która ma być wykonana
    • w trakcie wykonania danej instrukcji wskazuje adres instrukcji położonej bezpośrednio za nią
      • jest to tzw. nextPC 
    • jest to zawsze adres w obrębie sekcji kodu
  • Inkrementowany w trakcie pobrania instrukcji
  • Przy wykonaniu instrukcji skoku ładowany nową wartością -adresem docelowym skoku
  • Niezbędny w maszynie von Neumanna

Z cech maszyny von Neumanna wynika pośrednio obecność w procesorze rejestru, służącego do wskazywania kolejnych wykonywanych instrukcji. Ponieważ zawartość tego rejestru jest inkrementowana po pobraniu każdej instrukcji, rejestr ten nosi nazwę licznika instrukcji.


Podczas wykonywania instrukcji rejestr PC wskazuje następną instrukcję po aktualnie wykonywanej. Taka wartość PC jest określana jako nextPC (PC następnej instrukcji).


Podczas wykonywania instrukcji skoku do rejestru licznika instrukcji jest ładowana nowa wartość.



Procedury - wywołanie i powrót




  • wywołanie następuje poprzez instrukcję skoku ze śladem
    • skocz zapamiętując bieżącą wartość PC (wskazującą na następną instrukcję po instrukcji skoku)
  • zakończenie procedury (powrót) - instrukcja powrotu według śladu
    • skocz do adresu zapamiętanego przez instrukcję skoku ze śladem

Procesor przystosowany do wykonywania programu napisanego w języku wysokiego poziomu musi umożliwiać łatwą implementację przekazywania sterowania pomiędzy procedurami. W tym celu potrzebne są dwie instrukcje – skoku ze śladem i powrotu według śladu.


Instrukcja skoku ze śladem wykonuje skok po uprzednim zapamiętaniu wartości rejestru PC. Podczas wykonywania instrukcji skoku ze śladem PC wskazuje następną instrukcję po instrukcji skoku, i taka właśnie wartość PC podlega zapamiętaniu. Instrukcja skoku ze śladem służy do przekazania sterowania do procedury (wywołania procedury).


Instrukcja powrotu według śladu jest instrukcją skoku, której adres docelowy jest uprzednio zapamiętany m adresem śladu wywołania, czyli adresem wskazującym następną instrukcję po ostatnio wykonanej instrukcji skoku ze śladem.

 


Stos




  • Struktura danych używana do przekazywania sterowania pomiędzy procedurami programu
    • Parametry wywołania
    • Ślad powrotu
    • Zmienne lokalne

Z opisu mechanizmów wywołania i powrotu z procedur wynika, że stos oprócz argumentów i zmiennych lokalnych procedur powinien przechowywać również ślady powrotów i ewentualnie inne informacje wymagające przechowania podczas wywoływania procedur.


Model procesora I



  • Rejestry
    • PC - licznik instrukcji
    • Jeden rejestr danych (akumulator) bez nazwy
      • do opisu działania instrukcji będziemy oznaczali go symbolem A
  • Stos o bliżej nieokreślonej realizacji 
  • Dostęp do danych przez nazwy lub wartości (stałe)
    • Później zbudujemy prawdziwszy model

Pierwszy model procesora zawiera minimum mechanizmów niezbędnych do wykonania zaprezentowanego wcześniej programu w języku C. Modelowy procesor jest wyposażony w rejestr licznika instrukcji oraz rejestr przechowujący wynik obliczeń, służący jednocześnie jako rejestr argumentu źródłowego. Rejestr taki jest nazywany akumulatorem.


Model procesora obsługuje stos i realizuje podstawowe operacje na stosie, jednak implementacja stosu pozostaje nieokreślona.


Procesor wykonuje dostępy do danych wyspecyfikowanych przez podanie nazw lub wartości. Oczywiście ten aspekt modelu jest wysoce nierealistyczny i ma charakter tymczasowy.



Model procesora I - instrukcje




Tabela zawiera opis instrukcji pierwszego modelu procesora.


Procesor wykonuje pięć instrukcji dotyczących stosu. PUSH umieszcza wartość na stosie. Instrukcja skoku ze śladem przekazuje sterowanie do procedury po uprzednim zapamiętaniu śladu na stosie. Instrukcja powrotu według śladu odtwarza ślad ze stosu jednocześnie usuwając go. Instrukcja alokacji danej CREATE przypomina instrukcję PUSH, lecz nie nadaje danej na wierzchołku stosu żadnej wartości. Instrukcja DESTROY usuwa daną z wierzchołka stosu.



Kompilacja programu - wywołanie procedury



  • Zgodnie z typową konwencją wołania dla języka C argumenty procedury są umieszczane na stosie w kolejności od ostatniego do pierwszego
    • w ten sposób pierwszy argument znajdzie się najbliżej wierzchołka stosu
    • do wkładania argumentów na stos służy instrukcja PUSH
  • Po przygotowaniu argumentów następuje skok ze śladem
    • rozpoczyna się wykonanie wywoływanej procedury
    • zapamiętany na stosie ślad wskazuje instrukcję następną po instrukcji skoku ze śladem
      • od tej instrukcji będzie kontynuowane wykonanie programu po powrocie z wywołanej procedury

W typowej konwencji wołania używanej w języku C argumenty wywoływanej procedury są wkładane na stos w kolejności od ostatniego do pierwszego. Dzięki temu tuż przed wywołaniem na wierzchołku stosu jest położony pierwszy argument,a głębiej na stosie znajdują się kolejne argumenty wywołania.


Kompilacja - wywołanie procedury



Wywołanie procedury jest realizowane przez pierwsze trzy instrukcje. Rysunek pokazuje zawartość stosu bezpośrednio po wykonaniu instrukcji skoku ze śladem.


Kompilacja - procedura



  • Alokacja zmiennych lokalnych - instrukcja CREATE 
  • Dostęp do danych przez nazwy
    • uproszczenie dla modelu I
  • Założenie: każda instrukcja języka wysokiego poziomu kompilowana niezależnie od wcześniejszych (całkowity brak optymalizacji)
  • Wartość funkcji zwracana w rejestrze A
  • Przed zakończeniem procedury następuje dealokacja zmiennych lokalnych (instrukcja DESTROY)
  • Powrót przy użyciu instrukcji skoku według śladu (RETURN)



Pierwsza instrukcja procedura należy do prologu procedury i służy przygotowaniu środowiska jej działania – w tym przypadku alokacji jednej zmiennej lokalnej.


Rysunek przedstawia zawartość stosu po zakończeniu prologu.


Kolejne cztery instrukcje stanowią ciało procedury. Ostatnia z nich przygotowuje wartość zwracaną przez procedurę.


Ostatnie dwie instrukcje stanowią epilog procedury. Służą one do dealokacji zmiennej lokalnej i powrotu do procedury wywołującej.



Kompilacja - po wywołaniu 



  • Zgodnie z typową konwencją wołania dla języka C procedura wywołująca usuwa ze stosu niepotrzebne już argumenty wywołania (instrukcja DESTROY)
  • Wartość funkcji dostępna w rejestrze zostaje przesłana do zmiennej x


Implementacja stosu w pamięci



  • Procesor posiada rejestr wskaźnika stosu (SP - Stack Pointer)
    • SP zawiera adres danej ostatnio umieszczonej na stosie w pamięci, stos rośnie w kierunku malejących adresów
      • jest to tzw. stos "pełny schodzący" (full descending)
      • istnieją trzy inne możliwości
  • Operacje na stosie
    • PUSH - umieszczenie danej na stosie (po dekrementacji SP)
    • POP - zdjęcie danej ze stosu i inkrementacja SP

Stos jest najczęściej implementowany w pamięci komputera. W tym celu do procesora wprowadza się rejestr wskaźnika stosu, który zawiera adres wierzchołka stosu.


Najczęściej implementowany rodzaj stosu nazywa się stosem pełnym schodzącym. Przy takiej implementacji SP zawiera adres danej ostatnio umieszczonej na stosie, a stos rośnie w kierunku malejących adresów.



Model procesora II





Druga wersja modelu procesora stanowi urealnioną odmianę modelu pierwszego.


Stos został zaimplementowany w pamięci, co wymagało umieszczenia w procesorze rejestru SP. Posługując się rejestrem SP zdefiniowano jawnie operacje wykonywane przez instrukcje odnoszące się do stosu.


Model wciąż korzysta z dostępu do danych przez nazwy. Ten element zostanie dookreślony w trzecim modelu.



Model procesora II – instrukcje





Ramka stosu



  • Procedura posługuje się bezpośrednio niewielkim fragmentem stosu
  • Przy rozpoczęciu wykonania procedura na stosie znajdują się parametry wywołania i ślad powrotu -jest to tzw. rekord aktywacji
  • Procedura tworzy swoje zmienne lokalne
  • Rekord aktywacji + zmienne lokalne + ... = ramka stosu

Każda procedura do swojego wykonania potrzebuje tylko niewielkiego fragmentu stosu, złożonego z argumentów wywołania, śladu powrotu i zmiennych lokalnych. Fragment stosu używany przez procedurę nazywa się ramką stosu procedury. Część ramki, która jest obecna na stosie w chwili przekazania sterowania do procedury, nazywa się rekordem aktywacji.


Adresowania ramki stosu



  • Adresy obiektów w ramce stosu można tworzyć poprzez zsumowanie zawartości SP i stałej
  • Jest to niezbyt wygodne, bo wartość SP może się zmieniać podczas wykonania procedury
    • np. w trakcie przygotowywania parametrów wywołania następnej procedury
    • kompilator musi w takim przypadku śledzić odległość poszczególnych obiektów lokalnych od przesuwającego się wierzchołka stosu
    • problem ten nie występuje w procedurach - liściach (czyli takich, które nie wywołują żadnych innych procedur)

W każdej chwili podczas wykonania procedury można wyznaczyć odległość pomiędzy wierzchołkiem stosu i każdym obiektem ramki stosu. Dostęp do obiektów ramki stosu może być realizowany poprzez adresy, stanowiące sumę wskaźnika stosu i stałej, określającej odległość obiektu od wierzchołka stosu. Adresy takie mogą zastąpić nazwy, którymi posługuje się dotychczas wprowadzony model procesora.


Adresowanie obiektów poprzez podania ich przemieszczenia względem SP nie jest jednak w ogólnym przypadku wygodne, gdyż podczas wykonywania procedur wywołujących inne procedury wartość SP może się zmieniać. Tym samym zmieniałyby się przemieszczenia liczone względem SP.


Adresowani względem SP nie nastręcza problemów w procedurach nie wywołujących innych procedur, czyli tzw. liściach.



Wskaźnik ramki



  • W celu ułatwienia adresowania ramki stosu wprowadza się kolejny rejestr procesora - wskaźnik ramki (FP - Frame Pointer)
  • Zawartość wskaźnika ramki jest stała w czasie wykonania procedury
    • W czasie wykonania KAŻDEJ procedury!
    • Procedura musi zachować wartość wskaźnika ramki procedury wołającej

Do adresowania ramki stosu można posłużyć się rejestrem wskaźnika ramki. Zawartość wskaźnika ramki, w przeciwieństwie do zawartości wskaźnika stosu, nie zmienia się podczas wykonania ciała procedury.


Wskaźnik ramki wskazuje ramkę bieżącej procedury. Oznacza to, że każda procedura ustanawia własną wartość wskaźnika ramki. Każda procedura spodziewa się również, że jej wartość wskaźnika ramki nie zostanie zmieniona. Oznacza to, że procedura musi przed powrotem odtworzyć wskaźnik ramki procedura wołającej.



Operacje na wskaźniku ramki



  • Pierwszą czynnością procedury jest zapamiętanie FP na stosie i ustanowienie nowej wartości FP
    • od tej pory wartość wskaźnika ramki będzie stała podczas wykonywania ciała procedury
  • Dane ramki stosu są adresowane względem FP
    • przemieszczenia względem wskaźnika ramki są stałe
  • Przed powrotem należy odtworzyć starą wartość FP
    • przywracamy w ten sposób wartość wskaźnika ramki dla procedury wywołującej


Model II  podzbiór x86



  • Akumulator/rejestr wartości - EAX
  • Wskaźnik stosu - ESP
  • Wskaźnik ramki - EBP
  • Licznik instrukcji - EIP
  • Instrukcje w większości dwuargumentowe
  • Dostępne adresowanie rejestrowe pośrednie z przemieszczeniem
    • adres danej jest sumą zawartości rejestru i stałej ze znakiem
    • np. zapis [ESP+4] oznacza komórkę pamięci o adresie o 4 większym niż aktualna zawartość rejestru ESP

Po wprowadzeniu wskaźnika ramki możemy wskazać odwzorowanie modelu procesora w rzeczywisty model programowy procesorów rodziny x86, używanych w komputerach PC. Procesory x86 posiadają 8 rejestrów uniwersalnych ( w tym główny akumulator EAX, wskaźnik stosu ESP i wskaźnik ramki EBP) oraz rejestr licznika instrukcji oznaczony symbolem EIP.


Większość instrukcji występuje w postaci dwuargumentowej, a w zapisie symbolicznym instrukcji jako pierwszy podaje się argument docelowy, a następnie argumenty źródłowe. W instrukcjach arytmetycznych i logicznych argument docelowy jest jednocześnie pierwszym argumentem źródłowym.


Procesory x86 umożliwiają adresowanie pamięci sumą zawartości rejestru i stałej (przemieszczenia). Taki tryb adresowania jest wygodny do adresowania obiektów w ramce stosu. Możliwe jest adresowanie zarówno względem wskaźnika stosu, jak i względem wskaźnika ramki.



Potrzebne instrukcje x86




Tabela zawiera zestawienie instrukcji modelu II i ich odpowiedników w x86. instrukcje LOAD i STOR są zastąpione pojedynczą, dwuargumentową instrukcją przesłania MOV.


Operacje CREATE i DESTROY są realizowane jako odjęcie i dodanie stałej określającej rozmiar obiektów do wskaźnika stosu.



Kompilacja dla x86, adresowanie względem SP




Składnia zapisu przykładowego programu jest zgodna z asemblerami rodziny NASM.


Sekwencja wywołania dla x86 jest taka sama, jak w dotychczasowych modelach.


Alokacja i dealokacj zmiennej r w procedurze wywoływanej polega na wykonaniu prostej operacji na wskaźniku stosu. Podczas wykonania ciała procedury wskaźnik stosu wskazuje zmienną r. Dostępy do obiektów lokalnych są realizowane przy użyciu adresowania względem wskaźnika stosu.


Po powrocie z procedury następuje usunięcie argumentów. Polega ono na przesunięciu wskaźnika stosu o rozmiar argumentów – w tym przypadku o 8 (dwa argumenty po 32 bity).


Wartość funkcji zostaje zapamiętana w zmiennej statycznej x. Zapis [x] oznacza komórkę pamięci o adresie x. Ponieważ x jest zmienną statyczną, jej adres jest stałą. która może zostać zapisana symbolicznie.



Kompilacja dla x86, adresowanie względem FP




W wersji korzystającej ze wskaźnika ramki w prologu procedura następuje zapamiętanie wskaźnika ramki procedury wołającej i ustanowienie własnej wartości wskaźnika ramki. Ostatnia instrukcja prologu służy do alokacji zmiennej lokalnej.


Można zauważyć, że każda procedura będzie miała niemal identyczny prolog, zawierający trzy instrukcje. Jedyna różnica pomiędzy różnymi procedurami polega na odmiennych rozmiarach alokowanych zmiennych lokalnych.


Rysunek przedstawia ramkę stosu procedury podczas wykonywania ciała procedury. pomiędzy śladem powrotu i zmiennymi lokalnymi znajduje się zapamiętany wskaźnik ramki procedury wołającej. Na rysunku zaznaczono adresy poszczególnych obiektów ramki stosu liczone względem rejestru wskaźnika ramki (EBP).


Epilog każdej procedury jest identyczny i zawiera trzy instrukcje. Począwszy od modelu 80186 dwie instrukcje (MOV ESP, EBP; POP EBP) mogą być zastąpione pojedynczą, bezargumentową instrukcją LEAVE.



Stos – inne realizacje




  • Operacje na pamięci są stosunkowo wolne 
  • Potrzebne obiekty z wierzchołka stosu można umieścić w rejestrach procesora
  • Umieszczenie wybranych obiektów należących do stosu w rejestrach procesora zamiast w pamięci wydatnie przyspiesza operacje stosowe
    • i jednocześnie komplikuje wywoływanie procedur

Ponieważ odwołania do pamięci są wolne, we współczesnych procesorach często stosuje się inne implementacje stosu, nie wymagające ciągłych odwołań do pamięci. Jedna lub kilka ramek stosu może zostać umieszczonych w rejestrach procesora. Ponieważ możliwość taka dotyczy tylko obiektów skalarnych określonych typów, w praktyce takie rozwiązanie powoduje istotne komplikacje w przekazywaniu argumentów i alokacji zmiennych lokalnych. Problemem tym zajmuje się jednak kompilator, a programista nie musi być świadomy stopnia komplikacji programu wynikowego.