A számítástechnikában a hívási verem olyan verem-adatstruktúra, amely információkat tárol a számítógépes program aktív alprogramjairól. Ezt a fajta vermet végrehajtási veremnek, programveremnek, vezérlő veremnek, futásiidő-veremnek vagy gépi veremnek is nevezik, és gyakran csak veremnek rövidítik. Bár a hívási verem karbantartása fontos a legtöbb szoftver megfelelő működéséhez, a részletek nem láthatók és automatikusan működnek a magas szintű programozási nyelveken. Számos számítógépes utasításkészlet különleges utasításokat tartalmaz a verem kezelésére.

A hívási verem több célra használható, de elsődleges feladata az, hogy nyomon kövesse azt a pontot, amelyhez az aktív alprogramoknak vissza kell adniuk az irányítást, amikor végrehajtják. Az aktív alprogram az, amelyet meghívtak, de a végrehajtás még nem fejeződik be, miután az irányítást vissza kell adni a hívás helyére. Az alprogramok ilyen aktiválása bármilyen szintre beágyazható (különleges esetként rekurzív), következésképpen a verem szerkezetébe is. Például, ha egy alprogram a DrawSquare négy különféle helyről hív fel egy DrawLine alprogramot, akkor a DrawLine-nak tudnia kell, hogy hova kell visszatérnie, amikor a végrehajtás befejeződik. Ennek megvalósításához a DrawLine-ra a visszatérési címre ugró utasításokat követő címre kell lépnie, a visszatérési cím mindegyik hívással a hívási verem tetejére tolódik.

Leírás Mivel a hívási verem veremként van rendezve, a hívó kitolja a visszatérési címet a verembe, és a hívott alprogram amikor befejezi, kiszedi a visszatérési címét a hívási veremből, és átirányítja a vezérlést erre a címre. Ha a hívott alprogram újabb alprogramot hív, akkor egy másik visszatérési címet továbbít a híváskötegre, és így tovább, az információk egymásra helyezésével és leválasztásával, amint a program előírja. Ha a bepakolás a hívásveremhez kiosztott összes helyet elhasználja, verem túlcsordulásnak nevezett hiba jelentkezik, ami általában a program összeomlását okozza. Egy alprogram bejegyzésének hozzáadása a hívási veremhez néha "felsorakoztatás (felhúzás)"-nak nevezzük, ezzel szemben a bejegyzések eltávolítása a "felszabadítás".

Általában pontosan egy hívási verem van társítva egy futó programmal (vagy pontosabban, minden egyes feladattal vagy egy folyamat szálával), bár további vermek is létrehozhatók jelkezeléshez vagy kooperatív multitaskinghoz (mint setcontext). Mivel csak egy van ebben a fontos összefüggésben, ezért nevezhetjük a veremnek (konkrétabban „a feladat vermének”); azonban a Forth programozási nyelv az adatköteghez vagy a paraméterveremhez kifejezetten hozzáférnek mint a hívásveremhez, és általában veremnek nevezik (lásd alább).

Magas szintű programozási nyelvben a hívási verem specifikációit általában elrejtik a programozótól. Csak egy sor funkcióhoz férnek hozzá, nem pedig a verem memóriájához. Ez egy példa az absztrakcióra. A legtöbb assembly nyelv viszont megköveteli a programozók részvételét a verem manipulálásában. A verem tényleges részletei programozási nyelven a fordítótól, az operációs rendszertől és a rendelkezésre álló utasításkészlettől függnek.

A hívási verem funkciói Mint fentebb megjegyeztük, a hívási verem elsődleges célja a visszatérő címek tárolása. Alprogram behívásakor az utasítás helyét (címét), amelyen a hívási rutin később folytatódhat, valahol el kell menteni. A verem használata a visszatérési cím mentéséhez fontos előnyökkel jár az alternatív hívási konvenciókkal szemben. Az egyik az, hogy minden egyes feladat lehet saját verem, és így az alprogram lehet szálbiztos, azaz egyszerre aktív különböző feladatok elvégzésekor. Egy másik előnye az, hogy az újraértékelés biztosításával a rekurzió automatikusan támogatott. Amikor egy eljárás rekurzívan hívja magát, a függvényben minden aktiválásakor visszaadási címet kell tárolni, hogy később felhasználható legyen a metódus aktiválásakor. A verem struktúrák ezt a képességet automatikusan biztosítják.

A nyelvtől, az operációs rendszertől és a számítógépes környezettől függően a hívásverem további célokat szolgálhat, többek között:

Helyi adattárolás

Az alprogramnak gyakran szükséges memóriaterület a helyi változók értékeinek tárolására, olyan változókra, amelyek csak az aktív alprogramban ismertek, és amelyek visszatérés után nem tartják meg az értékeket. Gyakran kényelmes erre a célra helyet kiosztani, ha egyszerűen csak mozgatják a verem tetejét a hely biztosításához. Ez nagyon gyors a dinamikus memóriaelosztáshoz képest, amely a kezelt területet használja fel. Figyelembe veszi, hogy az alprogram minden egyes aktiválása külön helyet fog kapni a veremben a helyiek számára. Paraméterátadás Az alprogramok gyakran megkövetelik, hogy a paraméterek értékeit a hívó kód adja meg nekik, és nem ritka, hogy ezeknek a paramétereknek a helyét el lehet helyezni a hívási veremben. Általában, ha csak kevés paraméter van, akkor az értékek átadására a processzorregisztereket használjuk, de ha több paraméter létezik, mint amennyit ilyen módon lehet kezelni, akkor memóriahelyre lesz szükség. A hívási verem jól működik ezeknek a paramétereknek a helyén, különösen mivel minden olyan alprogramhoz történő hívás, amelynek a paraméterek eltérő értékei vannak, külön helyet fog hagyni a hívási veremben ezekre az értékekre. Értékelési verem A számtani vagy logikai műveletek operandusait általában regiszterekbe helyezik, és ott működtetik. Bizonyos helyzetekben azonban az operandusok tetszőleges mélységbe rakhatók, ami azt jelenti, hogy nem csupán regisztereket kell használni (ez vonatkozik a regiszterek kiszórására). Az ilyen operandusok halmazát, inkább, mint egy RPN számológépben, értékelési veremnek nevezik, és helyet foglalhat el a hívási veremben. Mutató az aktuális példányra Néhány objektumorientált nyelv (pl. C ++) tárolja <i id="mwVQ">ezt a</i> mutatót a függvény argumentumokkal együtt a hívási veremben metódusok meghívásakor. Ez a mutató a meghívandó módszerhez társított objektumpéldányra mutat. Befoglaló alprogram kontextus Néhány programozási nyelv (pl. Pascal és Ada) támogatja a beágyazott alprogramok deklarálását, amelyek hozzáférhetnek a záró rutinok kontextusához, azaz a külső rutinok hatókörén belüli paraméterekhez és helyi változókhoz. Az ilyen statikus beágyazás megismétlődik - egy függvényen belül deklarált függvényen deklarált függvény. A megvalósításnak olyan eszközt kell biztosítania, amellyel a meghívott függvény adott statikus beágyazás szinteken hivatkozhat a körülvevő keretre minden egyes záró beágyazás szintnél. Általában ezt a hivatkozást egy mutató hajtja végre a mellékelt funkció legutóbb aktivált példányának keretére, úgynevezett "downstack link" vagy "statikus link", hogy megkülönböztesse azt a "dinamikus link" -től, amely a közvetlen hívóra utal (amelyeknek nem kell a statikus szülőfüggvénynek lennie).

Statikus kapcsolat helyett a mellékelt statikus keretekre mutató hivatkozásokat össze lehet gyűjteni egy olyan mutatóegység-sorozatban, amelyet kijelzőnek hívunk, és amelyet a kívánt képmeghatározáshoz indexelnek. A rutin lexikális beágyazás mélysége ismert állandó, tehát a rutin megjelenítésének mérete rögzített. Szintén ismert az áthaladási skálák száma, a kijelzőn szereplő index is rögzítve van. Általában egy rutin kijelzője a saját verem keretében található, de a Burroughs B6500 olyan képernyőt valósított meg a hardverben, amely akár 32 szintű statikus beágyazást is támogatott. A hatóköröket tartalmazó kijelzőbejegyzéseket a hívó fél kijelzőjének megfelelő előtagjából szerezzük. A visszatérő belső rutin minden híváshoz külön hívási keretet hoz létre. Ebben az esetben a belső rutin statikus összeköttetései ugyanarra a külső rutin kontextusra mutatnak. Egyéb visszatérési állapot A visszatérési cím mellett néhány környezetben előfordulhatnak más gépi vagy szoftver állapotok is, amelyeket vissza kell állítani, amikor az alprogram visszatér. Ide tartozhatnak például a jogosultsági szint, a kivételkezelési információk, a számtani módok és így tovább. Szükség esetén ez tárolható a hívási veremben, ugyanúgy, mint a visszatérési cím.

A tipikus hívási vermet a visszatérési címek, a helyi változók és a paramétereket használják(más néven a hívást keret). Bizonyos környezetekben előfordulhat, hogy több vagy kevesebb funkció van hozzárendelve a hívási veremhez. Például az Forth programozási nyelvben általában csak a visszatérési címet, a megszámlált hurok paramétereket és az indexeket, valamint esetlegesen a helyi változókat tárolják a hívási veremben (amelyet ebben a környezetben visszatérési veremnek neveznek), bár bármilyen adat átmenetileg elhelyezhető ott speciális visszatérő verem kezelési kódot használnak mindaddig, amíg a hívások és a visszatérések igényeit tiszteletben tartják. A paramétereket általában külön adathalmazon vagy paraméter-verm-en tárolják, amelyet általában a veremnek hívnak a Forth terminológiában, annak ellenére, hogy van egy hívási verem, mivel ehhez általában kifejezetten hozzáférnek. Néhány Forth-nak van egy harmadik verem is a lebegőpontos paraméterekhez.

Szerkezet jobbra|bélyegkép|342x342px| Hívásverem-elrendezés felfelé növekvő veremhez A hívási verem veremkeretből áll (más néven aktiválás bejegyzések vagy aktiválás keretek). Ezek gépi és ABI-függő adatstruktúrák, amelyek alprogram állapotinformációkat tartalmaznak. Mindegyik verem-keret egy olyan alprogram hívására vonatkozik, amely még nem fejeződött be visszatéréssel. Például, ha jelenleg egy DrawLine nevű alprogram fut, amelyet egy DrawSquare alprogram DrawSquare, akkor a DrawSquare felső része el lehet DrawSquare, mint a szomszédos képen.

Az ilyen ábra bármelyik irányba rajzolható, amennyiben megértjük a teteje elhelyezkedését és a verem növekedésének irányát. Ezen felül, az építményektől függetlenül, abban különbözik egymástól, hogy a híváskötegek magasabb vagy alacsonyabb címek felé növekednek-e. A diagram logikája független a címzés választásától.

A verem tetején lévő veremkeret az éppen végrehajtott rutinhoz tartozik. A veremkeret általában legalább a következő elemeket tartalmazza (nyomtatási sorrendben):

a rutinnak átadott argumentumok (paraméterértékek) (ha vannak ilyenek); a visszatérési cím a rutin hívójának vissza (pl. a DrawLine verem keretben, egy cím a DrawSquare kódjába); és hely a rutin helyi változóinak (ha vannak).

Verem- és keretmutatók

Ha a verem keretmérete eltérő lehet, például a különféle függvények között vagy egy adott eljárás meghívása között, akkor a keret felbukkanása a veremből nem jelenti a veremmutató rögzített csökkenését. A függvény visszatérésekor a veremmutató a keretmutatóra áll vissza, értéke pedig a függvény hívása előtti érték. Mindegyik veremkeret tartalmaz egy veremmutatót, ami közvetlenül a keret tetejére mutat. A veremmutató egy változtatható regiszter, amely megoszlik az összes meghívás között. A függvény adott meghívásának keretmutatója a veremmutató példánya, ahogyan a függvény indítása előtt állt.

A keret összes többi mezőjének helyét a keret felső részéhez viszonyítva lehet meghatározni, a keret mutatójának negatív eltolásaként vagy az alatta lévő keret felső részéhez viszonyítva, a keret mutatójának pozitív eltolásaként. Maga a keretmutató helyét önmagában a kötegmutató negatív eltolásaként kell meghatározni.

A cím tárolása a hívó keretbe