Files
elte-ik-pti-bsc-zarovizsga/9.Programok fordítása és végrehajtása/tetel9.tex
2021-06-13 11:18:20 +02:00

544 lines
20 KiB
TeX
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

\documentclass[margin=0px]{article}
\usepackage{listings}
\usepackage[utf8]{inputenc}
\usepackage{graphicx}
\usepackage{float}
\usepackage[a4paper, margin=1in]{geometry}
\usepackage{amsthm}
\renewcommand{\figurename}{ábra}
\newenvironment{tetel}[1]{\paragraph{#1 \\}}{}
\usepackage{listings}
\usepackage{color}
% A dokument itt kezdődik
\title{Záróvizsga tételsor \\ \large 9. Programok fordítása és végrehajtása}
\date{}
\author{}
\begin{document}
\maketitle
\begin{tetel}{ Programok fordítása és végrehajtása }
Fordítás és interpretálás összehasonlítása.
Fordítási egység és a szerkesztés fogalma.
Fordítóprogramok komponenseinek feladata és működési elveik vázlatos ismertetése.
Kódgenerálás assemblyben alapvető imperatív vezérlési szerkezetekhez.
A szekvenciális és
párhuzamos/elosztott végrehajtás összehasonlítása
\end{tetel}
\section{Bevezetés}
Amikor programot írunk, azt valamilyen programozási nyelven tesszük. Ezután a nyelvtől függően vagy lefordítjuk, vagy interpreterrel futtatjuk.
\section{Fordítás és Interpretálás}
\subsection{Fordítás}
A fordítás során általában egy magas szintű programozási nyelvből gépi kód keletkezik, amelyet a processzor már képes értelmezni és futtatni. Előnye, hogy gyors, mivel a lexikális, szintaktikus és szemantikus elemzés fordítási időben, egyszer fut le, valamint ekkor optimalizáljuk a kódot. Fordítási időben sok hibát ki lehet szűrni, ezáltal megkönnyítve a debugolást. A gépi kód nehezen visszafejthető. Általában nagyobb programokhoz használjuk, ahol fontos a hatékonyság. A lefordított kódon később már nem (vagy csak nagyon nehezen) tudunk változtatni.
Hátránya, hogy a keletkezett kód nem platformfüggetlen, minden architektúrára külön-külön le kell fordítani.
\textbf{Példák}: C, C++, Ada, Haskell
\begin{figure}[H]
\centering
\includegraphics[width=0.5\textwidth]{img/forditas_folyamatabra.png}
\caption{a fordítás folyamata}
\label{fig:forditas_folyamatabra}
\end{figure}
\subsection{Interpretálás}
Az interpretálás során a programkódot az értelmező futás közben hajtja végre. Platformfüggetlen, csak az interpretert kell minden rendszerre egyszer megírni. Nehéz benne a hibakeresés, mivel sok olyan hiba maradhat a kódban, amit egy fordító kiszűrt volna (pl. típus egyezőség).
\textbf{Példák}: PHP, JavaScript, ShellScript
\begin{figure}[H]
\centering
\includegraphics[width=0.5\textwidth]{img/interpretalas_folyamatabra.png}
\caption{az interpretálás folyamata}
\label{fig:interpretalas_folyamatabra}
\end{figure}
\subsection{Fordítás és Interpretálás együtt}
Egyes nyelvek (pl. Java) előfordítást használnak, melynek eredménye a \textit{bájtkód}, amely gépi kód egy virtuális gép számára. Ezzel elérhető a fordítási idejű hibaellenőrzés és optimalizálás, de megmarad a platformfüggetlenség.
\begin{figure}[H]
\centering
\includegraphics[width=0.5\textwidth]{img/bajtkod_folyamatabra.png}
\caption{az interpretálás folyamata}
\label{fig:bajtkod_folyamatabra}
\end{figure}
\section{Fordítási egység és a szerkesztés}
A tárgykód létrehozása két fázisban történik. Először a forrásfájlokat \textit{lefordítjuk}, ebből keletkezik az un. \textit{objektumkód} (pl.: .obj, .class). Ebben a gépi utasítások már megvannak, de hiányzik belőle a hivatkozások (pl változók, függvények), melyek más fájlokban vannak megvalósítva. \textit{Fordítási egységnek} nevezzük azt, amiből egy objektumkód keletkezik.
A \textit{linker} (szerkesztő) feladata, hogy a hiányzó referenciákat kitöltse, hogy egyetlen fájlt generálva futtatható kódot kapjunk.
A linkelés lehet statikus, amikor a fordító tölti fel a hiányzó referenciákat; vagy dinamikus, mikor fordítási időben, jellemzően egy másik fájlból (pl.: .dll) tölti be a hiányzó kódot. Az utóbbi akkor praktikus, ha egy modult több, különálló program használ.
\section{A fordítóprogram komponensei}
\begin{figure}[H]
\centering
\includegraphics[width=0.5\textwidth]{img/forditas_teljes_folyamata.png}
\caption{a fordítás lépései}
\label{fig:forditas_teljes_folyamatabra}
\end{figure}
\subsection{Lexikális elemző}
Bemenete maga a forráskód. A lexikális elemző feladata, hogy tokenekre bontsa a forráskódot. Adott egy reguláris (hármas típusú) nyelvtan, mely a nyelvre jellemző. Ez adja meg, hogy milyen típusú tokenek szerepelhetnek a forrásban. A tokenekhez tulajdonságokat rendelhet (pl. változó neve, literál értéke). Kimenete ez a tokensorozat. Amennyiben az elemző olyan karaktersorozatot talál, amelynek nem feleltethető meg token, akkor az lexikális hibát vált ki.
\textit{megjegyzés: Lexikális hibánál nem feltétlen szakad meg a fordítás folyamata, megpróbálhatjuk átugrani az adott részt és folytatni az elemzést, így ha több hiba is van, akkor azokat egyszerre jelezhetjük.}
A reguláris kifejezéseket \textit{véges determinisztikus automatákkal} ismerjük fel. Amennyiben egy lexikális elemre az egyik automata elfogadó állapotba kerül, úgy felismertünk egy tokent. Egy karaktersorozatot egyszerre több automata is felismerhet. Amennyiben ezek azonosan hosszúak, akkor a nyelv konfliktusos. Ennek nem szabad előfordulnia. Az viszont lehetséges, hogy egy szót, és az ő prefixét is felismerte egy automata. Ekkor mindig a hosszabbat választjuk.
\subsection{Szintaktikus elemző}
Bemenete a lexikális elemző kimenete. Feladata, hogy \textit{szintaxisfát} építsen a tokenekből, a nyelvez tartozó egy környezetfüggetlen (kettes típusú) grammatika alapján, vagy ha ez lehetetlen, akkor jelezze ezt \textit{szintaktikus hiba}ként.
\subsubsection{LR0 elemzés}
A lexikális elemző által előállított szimbólumsorozatot balról
jobbra olvassuk, a szimbólumokat az elemző vermébe tesszük.
\textit{Léptetés}: egy új szimbólumot teszünk a bemenetről a verem
tetejére.
\textit{Redukálás}: a verem tetején lévő szabály-jobboldalt
helyettesítjük a szabály bal oldalán álló nemterminálissal
A háttérben egy véges determinisztikus automata működik:
az automata átmeneteit a verem tetejére kerülő szimbólumok
határozzák meg
ha az automata végállapotba jut, redukálni kell
egyéb állapotban pedig léptetni.
Az automata bizonyos nyelvek esetén konfliktusos lehet: nem tudjuk eldönteni, hogy léptessünk vagy redukáljunk.
\subsubsection{LR1 elemzés}
Az előző problémára kínál megoldást, kibővítve a lehetséges nyelvek halmazát.
Az ötlet, hogy \textit{olvassunk előre} egy szimbólumot.
Ha az aktuális állapot \textit{i}, és az előreolvasás eredménye az a
szimbólum:
ha $ [A \rightarrow \alpha.a\beta, b] \in I_i $ és $read(I_i, a) = I_j$
akkor léptetni kell, és átlépni a \textit{j} állapotba.
ha $ [A \rightarrow \alpha., a] \in I_i (A \neq S'), $
akkor redukálni kell az $ A \rightarrow \alpha $ szabály szerint.
ha $ [S' \rightarrow S., \#] \in I_i $ és $ a = \# $, akkor el kell fogadni a szöveget, minden más esetben hibát kell jelezni.
Ha az \textit{i} állapotban \textit{A} kerül a verem tetejére:
ha$ read(I_i,A) = I_j $ ,
akkor át kell lépni a \textit{j} állapotba, egyébként hibát kell jelezni.
\subsubsection{Jelmagyarázat/Kanonikus halmazok}
\paragraph{Closure/lezárás}
Ha $ I $ a grammatika egy $ LR(1) $ elemhalmaza, akkor $ closure(I) $ a
legszűkebb olyan halmaz, amely az alábbi tulajdonságokkal
rendelkezik:
$ I \subseteq closure(I) $ ha $ [A \rightarrow \alpha.B\gamma,a] \in closure(I) $,
és $ B \rightarrow \beta $ a grammatika egy szabálya,
akkor $ \forall b \in FIRST1(\gamma{}a) $ esetén $ [B \rightarrow .\beta,b] \in closure(I) $
\paragraph{Read/olvasás}
Ha $ I $ a grammatika egy $ LR(1) $ elemhalmaza, $ X $ pedig terminális vagy nemterminális szimbóluma, akkor $ read(I, X) $ a legszűkebb olyan halmaz, amely az alábbi tulajdonsággal rendelkezik:
Ha $ [A \rightarrow \alpha. X\beta,a] \in I $, akkor $ closure([ A \rightarrow \alpha X.\beta,a]) \subseteq read(I, X) $.
\paragraph{LR(1) kanonikus halmazok ($ I_n $)}
\begin{itemize}
\item
$ closure([S' \rightarrow .S, \#]) $ a grammatika egy kanonikus halmaza.
\item
Ha $ I $ a grammatika egy kanonikus elemhalmaza, $ X $ egy terminális vagy nemterminális szimbóluma, és $ read(I, X) $ nem üres, akkor $ read(I, X) $ is a grammatika egy kanonikus halmaza.
\item
Az első két szabállyal az összes kanonikus halmaz előáll.
\end{itemize}
\subsection{Szemantikus elemző}
A szemantikus elemzés jellemzően a környezetfüggő ellenőrzéseket
valósítja meg.
%Dévai diáiról
\begin{itemize}
\item
deklarációk kezelése: változók, függvények, eljárások, operátorok, típusok
\item
láthatósági szabályok
\item
aritmetikai ellenőrzések
\item
a program szintaxisának környezetfüggő részei
\item
típusellenőrzés
\item
stb.
\end{itemize}
A szemantikus elemzéshez ki kell egészítenünk a grammatikát. Rendeljünk a szimbólumokhoz attribútumokat és a szabályokhoz akciókat! Egy adott szabályhoz tartozó feltételek csak a szabályban előforduló attribútumoktól függhetnek. (Ha egy feltétel nem teljesül, akkor szemantikus hibát kell jelezni!). A szemantikus rutinok csak annak a szabálynak az attribútumait használhatják és számíthatják ki, amelyikhez az őket reprezentáló akciószimbólum tartozik. Minden szintaxisfában minden attribútumértéket pontosan egy
szemantikus rutin határozhat meg. Az így létrejövő nyelvtant \textit{attribútum fordítási grammatikának} (ATG) hívjuk.
A \textit{jól definiált attribútum fordítási grammatika}, olyan attribútum fordítási grammatika, amelyre igaz, hogy a grammatika által definiált nyelv mondataihoz tartozó minden szintaxisfában minden attribútum értéke egyértelműen kiszámítható.
Egy attribútumot kétféleképpen lehet meghatározni:
\paragraph{Szintézissel} a szintaxisfában alulról felfelé terjed az információ, egy szülő attribútumát a gyerekekből számoljuk. Kitüntetettnek hívjuk azokat az attribútumokat, melyeket a lexikális elemző szolgáltat.
\paragraph{Öröklődéssel} a szintaxisfában felülről lefelé terjed az információ. A gyerekek attribútumait a szülőé határozza meg.
Az \textit{L-ATG} olyan attribútum fordítási grammatika, amelyben minden
$ A \rightarrow X_1X_2 . . . X_n $szabályban az attribútumértékek az alábbi sorrendben meghatározhatók:
\begin{itemize}
\item
A örökölt attribútumai
\item
$ X_1 $ örökölt attribútumai
\item
$ X_1 $ szintetizált attribútumai
\item
$ X_2 $ örökölt attribútumai
\item
$ X_2 $ szintetizált attribútumai
\item
\dots
\item
$ X_n $ örökölt attribútumai
\item
$ X_n $ szintetizált attribútumai
\item
A szintetizált attribútumai
\end{itemize}
Amennyiben a nyelvtanunk ennek eleget tesz, úgy hatékonyan meghatározható minden attribútum.
A szemantikus elemzéshez jellemzően szimbólumtáblát használunk, verem szerkezettel és keresőfával vagy hash-táblával. Minden blokk egy új szint a veremben, egy szimbólum keresése a verem tetejéről indul.
% Kódgenerálás assemblyben alapvető imperatív vezérlési szerkezetekhez.
\section{Kódgenerálás alapvető vezérlési szerkezetekhez}
A kódgenerálás feladata, hogy a szintaktikusan és szemantikusan elemzett programot tárgykóddá alakítsa. Általában szorosan összekapcsolódik a
szemantikus elemzéssel.
\subsection{Értékadás}
assignment $ \rightarrow $ variable assignmentOperator expression
\begin{verbatim}
a kifejezést az eax regiszterbe kiértékelö kód
2 mov [Változó],eax
\end{verbatim}
\subsection{Egy ágú elágazás}
statement $ \rightarrow $ if condition then program end
\begin{verbatim}
1 a feltételt az al regiszterbe kiértékelö kód
2 cmp al,1
3 je Then
4 jmp Vége
5 Then: a then-ág programjának kódja
6 Vége:
\end{verbatim}
\textit{megjegyzés: a dupla ugrásra azért van szükség, mert a feltételes ugrás hatóköre limitált.}
\subsection{Több ágú elágazás}
statement $ \rightarrow $
\\if $ condition_1 $ then $ program_1 $
\\elseif $ condition_2 $ then $ program_2 $
\\\dots
\\elseif $ condition_n $ then $ program_n $
\\else $ program_{n+1} $ end
\begin{verbatim}
1 az 1. feltétel kiértékelése az al regiszterbe
2 cmp al,1
3 jne near Feltétel_2
4 az 1. ág programjának kódja
5 jmp Vége
6
...
7 Feltétel_n: az n-edik feltétel kiértékelése az al regiszterbe
8 cmp al,1
9 jne near Else
10 az n-edik ág programjának kódja
11 jmp Vége
12 Else: az else ág programjának kódja
13 Vége:
\end{verbatim}
\subsection{Switch-case}
statement $ \rightarrow $ switch variable
\\case $ value_1 $ : $ program_1 $
\\...
\\case $ value_n $ : $ program_n $
\begin{verbatim}
1 cmp [Változó],Érték_1
2 je near Program_1
3 cmp [Változó],Érték_2
4 je near Program_2
5
. ..
6 cmp [Változó],Érték_n
7 je near Program_n
8 jmp Vége
9 Program_1: az 1. ág programjának kódja
10
. ..
11 Program_n: az n-edik ág programjának kódja
12 Vége:
\end{verbatim}
\subsection{Ciklus}
%Ez egy vagy két szó?
\subsubsection{Elől tesztelő}
statement $ \rightarrow $ while condition statements end
\begin{verbatim}
1 Eleje: a ciklusfeltétel kiértékelése az al regiszterbe
2 cmp al,1
3 jne near Vége
4 a ciklusmag programjának kódja
5 jmp Eleje
6 Vége:
\end{verbatim}
%Ez egy vagy két szó?
\subsubsection{Hátul tesztelő}
statement $ \rightarrow $ loop statements while condition
\begin{verbatim}
1 Eleje: a ciklusmag programjának kódja
2 a ciklusfeltétel kiértékelése az al regiszterbe
3 cmp al,1
4 je near Eleje
\end{verbatim}
\subsubsection{For ciklus}
statement $ \rightarrow $ for variable from $ value_1 $ to $ value_2 $ statements end
\begin{verbatim}
1 a "from" érték kiszámítása a [Változó] memóriahelyre
2 Eleje: a "to" érték kiszámítása az eax regiszterbe
3 cmp [Változó],eax
4 ja near Vége
5 a ciklusmag kódja
6 inc [Változó]
7 jmp Eleje
8 Vége:
\end{verbatim}
\subsection{Statikus változók}
Kezdőérték nélküli változódefiníció fordítása:
\begin{verbatim}
section .bss
; a korábban definiált változók...
Lab12: resd 1 ; 1 x 4 bájtnyi terület
\end{verbatim}
Kezdőértékkel adott változódefiníció fordítása:
\begin{verbatim}
section .data
; a korábban definiált változók...
Lab12: dd 5 ; 4 bájton tárolva az 5-ös érték
\end{verbatim}
\subsection{Logikai kifejezések}
\subsubsection{kifejezés1 $ < $ kifejezés2 }
\begin{verbatim}
; a 2. kifejezés kiértékelése az eax regiszterbe
push eax
; az 1. kifejezés kiértékelése az eax regiszterbe
pop ebx
cmp eax,ebx
jb Kisebb
mov al,0 ; hamis
jmp Vége
Kisebb:
mov al,1 ; igaz
Vége:
\end{verbatim}
\subsubsection{kifejezés1 $ \lbrace $ és, vagy, nem, kizáróvagy $ \rbrace $ kifejezés2 }
\begin{verbatim}
; a 2. kifejezés kiértékelése az al regiszterbe
push ax ; nem lehet 1 bájtot a verembe tenni!
; az 1. kifejezés kiértékelése az al regiszterbe
pop bx ; bx-nek a bl részében van,
; ami nekünk fontos
and al,bl
\end{verbatim}
\subsubsection{ lusta "és" kiértékelés }
\begin{verbatim}
; az 1. kifejezés kiértékelése az al regiszterbe
cmp al,0
je Vége
push ax
; a 2. kifejezés kiértékelése az al regiszterbe
mov bl,al
pop ax
and al,bl
Vége:
\end{verbatim}
\subsubsection{ Alprogramok megvalósítása }
\begin{verbatim}
; az 1. kifejezés kiértékelése az al regiszterbe
cmp al,0
je Vége
push ax
; a 2. kifejezés kiértékelése az al regiszterbe
mov bl,al
pop ax
and al,bl
Vége:
\end{verbatim}
\subsubsection{ Alprogramok hívása }
\begin{verbatim}
Alprogramok sémája
; utolsó paraméter kiértékelése eax-be
push eax
; ...
; 1. paraméter kiértékelése eax-be
push eax
call alprogram
add esp,a paraméterek összhossza
\end{verbatim}
\section{Kódoptimalizáló}
Az optimalizálás feladata, hogy a keletkezett kód kisebb és gyorsabb legyen, úgy hogy a futás eredménye nem változik. A gyorsaság és a tömörség gyakran ellentmondanak egymásnak, és az egyik csak a másik rovására lehet javítani. Általában három lépésben szokás elvégezni:
\begin{itemize}
\item
Optimalizálási lépések végrehajtása az eredeti programon (vagy annak egyszerűsített változatán)
\item
Kódgenerálás
\item
Gépfüggő optimalizálás végrehajtása a generált kódon
\end{itemize}
\subsection{Lokális optimalizáció}
Egy programban egymást követő utasítások sorozatát \textit{alapblokknak} nevezzük, ha az első utasítás kivételével egyik utasítására sem lehet távolról átadni a vezérlést (assembly programokban: ahová a jmp, call, ret utasítások "ugranak"; magas szintű nyelvekben: eljárások, ciklusok eleje, elágazások ágainak első utasítása, goto utasítások célpontjai). Az utolsó utasítás kivételével nincs benne vezérlés-átadó utasítás (assembly programban: jmp, call, ret magas szintű nyelvekben: elágazás vége, ciklus vége, eljárás vége, goto). Az utasítás-sorozat nem bővíthető a fenti két szabály megsértése nélkül.
Ha az optimalizálás az alapblokkok keretein belül történik, akkor garantált, hogy az átalakításnak nincs mellékhatása. Ez a \textit{lokális optimalizálás}.
\paragraph{Ablakoptimalizálás}
Ez egy módszer a lokális optimalizálás egyes fajtáihoz. Egyszerre csak egy néhány utasításnyi részt vizsgálunk a kódból. A vizsgált részt előre megadott mintákkal hasonlítjuk össze. Ha illeszkedik, akkor a mintához megadott szabály szerint átalakítjuk ezt az "ablakot" végigcsúsztatjuk a programon. Az átalakítások megadása:
\begin{center}
$ \lbrace$ minta $ \rightarrow $ helyettesítés $\rbrace$ szabályhalmazzal
(a mintában lehet paramétereket is használni)
\end{center}
Példák:
\begin{itemize}
\item
felesleges műveletek törlése: nulla hozzáadása vagy kivonása
\item
egyszerűsítések: nullával szorzás helyett a regiszter törlése
\item
regiszterbe töltés és ugyanoda visszaírás esetén a visszaírás elhagyható
\item
utasításismétlések törlése: ha lehetséges, az ismétlések törlése
\end{itemize}
\subsection{Globális optimalizáció}
A teljes program szerkezetét meg kell vizsgálni. Ennek módszere az adatáram-analízis:
\begin{itemize}
\item
Mely változók értékeit számolja ki egy adott alapblokk?
\item
Mely változók értékeit melyik alapblokk használja fel?
\end{itemize}
Ez lehetővé teszi az azonos kifejezések többszöri kiszámításának kiküszöbölését akkor is, ha különböző alapblokkokban szerepelnek; valamint a konstansok és változók továbbterjesztését alapblokkok között is elágazások, ciklusok optimalizálását.
% kell ide példa? tudok szerezni de nem hiszem hogy annyira fontos lenne
\section{A szekvenciális és párhuzamos/elosztott végrehajtás összehasonlítása}
\subsection{Szekvenciális végrehajtás:}
Ilyenkor a végrehajtás egy processzoron történik. Minden művelet atomi. Egy inputhoz egy output tartozik. Két szekvenciális program ekvivalens, ha ezek a párosok megegyeznek. Nem használja fel az összes rendelkezésre álló erőforrást.
\subsection{Párhuzamos végrehajtás:}
Több processzoron hajtódik végre a program. A párhuzamos folyamatok egymással kommunikálva, szinkronban oldják meg az adott problémát. A konkurens program szétbontható elemi szekvenciális programokra, ezek a folyamatok. A folyamatok használhatnak közös erőforrásokat: pl. változók, adattípus objektumok, kommunikációs csatornák.
A kommunikációt általában kétféleképpen szokták megvalósítani.
\paragraph{Osztott memóriával.} Ekkor szinkronizálni kell, hogy ki mikor fér hozzá, hogy ne legyen ütközés.
\paragraph{Kommunikációs csatornával.} Garantálni kell, hogy ha egy folyamat üzenetet küld egy másiknak, akkor az meg is kapja azt, és jelezzen is vissza. Ügyelni kell, nehogy deadlock alakuljon ki.
\end{document}