Subrutina
ideea unei subrutine a fost elaborată după ce mașinile de calcul existau deja de ceva timp.Instrucțiunile aritmetice și condiționale de salt au fost planificate din timp și s-au schimbat relativ puțin, dar instrucțiunile speciale utilizate pentru apelurile de procedură s-au schimbat foarte mult de-a lungul anilor.Cele mai vechi computere și microprocesoare, cum ar fi Manchester Baby și RCA 1802, nu aveau o singură instrucțiune de apel subrutină.Subrutinele ar putea fi implementate, dar au cerut programatorilor să utilizeze secvența de apel—o serie de instrucțiuni—la fiecare site de apel.
subrutinele au fost implementate în Z4 al lui Konrad Zuse în 1945.
în 1945, Alan M. Turing a folosit termenii „bury” și „unbury” ca mijloc de a chema și de a reveni din subrutine.
În ianuarie 1947 John Mauchly a prezentat note generale la ‘un Simpozion de mașini digitale de calcul la scară largă’ sub sponsorizarea comună a Universității Harvard și a Biroului de artilerie, Marina Statelor Unite. Aici discută despre funcționarea serială și paralelă sugerând
…structura mașinii nu trebuie să fie complicată un pic. Este posibil, deoarece toate caracteristicile logice esențiale pentru această procedură sunt disponibile, să se dezvolte o instrucțiune de codificare pentru plasarea subrutinelor în memorie în locuri cunoscute mașinii și în așa fel încât acestea să poată fi ușor folosite.
cu alte cuvinte, se poate desemna subrutina a ca diviziune și subrutina B ca multiplicare complexă și subrutina C ca evaluare a unei erori standard a unei secvențe de numere și așa mai departe prin lista subrutinelor necesare pentru o anumită problemă. … Toate aceste subrutine vor fi apoi stocate în mașină și tot ce trebuie să faceți este să faceți o scurtă referire la ele după număr, așa cum sunt indicate în codificare.
Kay McNulty a lucrat îndeaproape cu John Mauchly în echipa ENIAC și a dezvoltat o idee pentru subrutine pentru computerul ENIAC pe care îl programa în timpul celui de-al doilea război mondial. ea și ceilalți programatori ENIAC au folosit subrutinele pentru a ajuta la calcularea traiectoriilor rachetelor.
Goldstine și von Neumann au scris o lucrare datată 16 August 1948 discutând utilizarea subrutinelor.
unele computere și microprocesoare foarte timpurii, cum ar fi IBM 1620, Intel 4004 și Intel 8008 și microcontrolerele PIC, au un apel de subrutină cu o singură instrucțiune care utilizează o stivă hardware dedicată pentru a stoca adresele returnate-un astfel de hardware acceptă doar câteva niveluri de cuibărire a subrutinelor, dar poate suporta subrutine recursive. Mașinile înainte de mijlocul anilor 1960-cum ar fi UNIVAC I, PDP—1 și IBM 1130-folosesc de obicei o convenție de apelare care a salvat contorul de instrucțiuni în prima locație de memorie a subrutinei numite. Acest lucru permite niveluri arbitrare profunde de cuibărire a subrutinelor, dar nu acceptă subrutine recursive. PDP-11 (1970) este unul dintre primele computere cu o instrucțiune de apel subrutină care împinge stiva; această caracteristică acceptă atât cuiburi subrutine adânci arbitrar, cât și subrutine recursive.
Language supportEdit
în asamblorii foarte timpurii, suportul subrutinei a fost limitat. Subrutinele nu au fost separate în mod explicit unele de altele sau de programul principal și, într-adevăr, codul sursă al unei subrutine ar putea fi intercalat cu cel al altor subprograme. Unii asamblori ar oferi macrocomenzi predefinite pentru a genera secvențele de apel și returnare. Până în anii 1960, asamblorii aveau de obicei un suport mult mai sofisticat atât pentru subrutinele asamblate în linie, cât și separat, care puteau fi legate între ele.
subrutine librariesEdit
chiar și cu această abordare greoaie, subrutinele s-au dovedit foarte utile. În primul rând, au permis utilizarea aceluiași cod în multe programe diferite. Mai mult, memoria a fost o resursă foarte rară pe computerele timpurii, iar subrutinele au permis economii semnificative în dimensiunea programelor.
multe computere timpurii au încărcat instrucțiunile programului în memorie de pe o bandă de hârtie perforată. Fiecare subrutină ar putea fi apoi furnizată de o bucată separată de bandă, încărcată sau îmbinată înainte sau după programul principal (sau „linia principală”); și aceeași bandă de subrutină ar putea fi apoi utilizată de multe programe diferite. O abordare similară aplicată în computerele care au folosit carduri perforate pentru intrarea lor principală. Numele subrutine library însemna inițial o bibliotecă, în sens literal, care păstra colecții indexate de benzi sau pachete de cărți pentru uz colectiv.
Return by indirect jumpEdit
pentru a elimina nevoia de cod de auto-modificare, proiectanții de computere au furnizat în cele din urmă o instrucțiune de salt indirect, al cărei operand, în loc să fie adresa de returnare în sine, a fost locația unei variabile sau a unui registru de procesor care conține adresa de returnare.
pe acele computere, în loc să modifice saltul de întoarcere al subrutinei, programul de apelare ar stoca adresa de întoarcere într-o variabilă, astfel încât atunci când subrutina s-a finalizat, ar executa un salt indirect care ar direcționa execuția către locația dată de variabila predefinită.
Salt la subrutineedit
un alt avans a fost instrucțiunea salt la subrutină, care a combinat salvarea adresei de retur cu saltul de apelare, reducând astfel semnificativ cheltuielile generale.
în IBM System / 360, de exemplu, instrucțiunile de ramură BAL sau BALR, concepute pentru apelarea procedurii, ar salva adresa de retur într-un registru de procesor specificat în instrucțiune, prin registrul Convenției 14. Pentru a reveni, subrutina a trebuit doar să execute o instrucțiune indirectă de ramură (br) prin acel registru. Dacă subrutina avea nevoie de acel registru pentru un alt scop (cum ar fi apelarea unei alte subrutine), ar salva conținutul registrului într-o locație de memorie privată sau într-o stivă de registru.
în sisteme precum HP 2100, instrucțiunea JSB ar efectua o sarcină similară, cu excepția faptului că adresa de retur a fost stocată în locația de memorie care a fost ținta ramurii. Executarea procedurii ar începe de fapt la următoarea locație de memorie. În limbajul de asamblare HP 2100, s-ar scrie, de exemplu
... JSB MYSUB (Calls subroutine MYSUB.) BB ... (Will return here after MYSUB is done.)
pentru a apela o subrutină numită MYSUB din programul principal. Subrutina ar fi codificată ca
MYSUB NOP (Storage for MYSUB's return address.) AA ... (Start of MYSUB's body.) ... JMP MYSUB,I (Returns to the calling program.)
instrucțiunea JSB a plasat adresa următoarei instrucțiuni (și anume, BB) în locația specificată ca operand (și anume, MYSUB) și apoi ramificată la următoarea locație după aceea (și anume, AA = MYSUB + 1). Subrutina ar putea reveni apoi la programul principal executând saltul indirect JMP MYSUB, i care s-a ramificat la locația stocată la locația MYSUB.
compilatoare pentru Fortran și alte limbi ar putea face cu ușurință utilizarea acestor instrucțiuni atunci când sunt disponibile. Această abordare a acceptat mai multe niveluri de apeluri; cu toate acestea, deoarece adresa de returnare, parametrii și valorile de returnare ale unei subrutine au fost atribuite locații de memorie fixe, nu a permis apeluri recursive.de altfel, o metodă similară a fost folosită de Lotus 1-2-3, la începutul anilor 1980, pentru a descoperi dependențele de recalculare într-o foaie de calcul. Anume, o locație a fost rezervată în fiecare celulă pentru a stoca adresa de returnare. Deoarece referințele circulare nu sunt permise pentru ordinea naturală de recalculare, acest lucru permite o plimbare în copac fără a rezerva spațiu pentru o stivă în memorie, care a fost foarte limitată pe computerele mici, cum ar fi IBM PC.
Call stackEdit
majoritatea implementărilor moderne ale unui apel de subrutină utilizează o stivă de apeluri, un caz special al structurii de date a stivei, pentru a implementa apeluri și returnări de subrutină. Fiecare apel de procedură creează o nouă intrare, numită cadru de stivă, în partea de sus a stivei; când procedura revine, cadrul stivei sale este șters din stivă, iar spațiul său poate fi utilizat pentru alte apeluri de procedură. Fiecare cadru de stivă conține datele private ale apelului corespunzător, care include de obicei parametrii procedurii și variabilele interne și adresa de returnare.
secvența de apel poate fi implementată printr-o secvență de instrucțiuni obișnuite (o abordare încă utilizată în calculul setului de instrucțiuni reduse (RISC) și arhitecturi de cuvinte de instrucțiuni foarte lungi (VLIW)), dar multe mașini tradiționale proiectate de la sfârșitul anilor 1960 au inclus instrucțiuni speciale în acest scop.stiva de apeluri este de obicei implementată ca o zonă contiguă de memorie. Este o alegere arbitrară de proiectare dacă partea de jos a stivei este cea mai mică sau cea mai mare adresă din această zonă, astfel încât stiva să poată crește înainte sau înapoi în memorie; cu toate acestea, multe arhitecturi au ales-o pe aceasta din urmă.
unele modele, în special unele implementări, au folosit două stive separate, una în principal pentru informații de control (cum ar fi adresele de returnare și contoarele de buclă) și cealaltă pentru date. Primul a fost sau a funcționat ca o stivă de apeluri și a fost accesibil doar indirect programatorului prin alte construcții lingvistice, în timp ce acesta din urmă a fost mai direct accesibil.
când au fost introduse pentru prima dată apeluri de procedură bazate pe stivă, o motivație importantă a fost salvarea memoriei prețioase. Cu această schemă, compilatorul nu trebuie să rezerve spațiu separat în memorie pentru datele private (parametrii, adresa de returnare și variabilele locale) ale fiecărei proceduri. În orice moment, stiva conține numai datele private ale apelurilor care sunt active în prezent (și anume, care au fost apelate, dar nu s-au întors încă). Din cauza modurilor în care programele erau de obicei asamblate din biblioteci, nu a fost (și încă este) neobișnuit să găsești programe care includ mii de subrutine, dintre care doar o mână sunt active la un moment dat. Pentru astfel de programe, mecanismul stivei de apeluri ar putea economisi cantități semnificative de memorie. Într-adevăr, mecanismul stivei de apeluri poate fi privit ca cea mai timpurie și mai simplă metodă pentru gestionarea automată a memoriei.cu toate acestea, un alt avantaj al metodei stivei de apeluri este că permite apeluri subrutine recursive, deoarece fiecare apel imbricat la aceeași procedură primește o instanță separată a datelor sale private.
stivuire întârziată editare
un dezavantaj al mecanismului stivei de apeluri este costul crescut al unui apel de procedură și randamentul său de potrivire. Costul suplimentar include incrementarea și decretarea indicatorului stivei (și, în unele arhitecturi, verificarea depășirii stivei) și accesarea variabilelor și parametrilor locali prin adrese cadru-relative, în loc de adrese absolute. Costul poate fi realizat în timp de execuție crescut sau complexitate crescută a procesorului sau ambele.
acest regie este cel mai evident și inacceptabil în procedurile de frunze sau funcții de frunze, care se întorc fără a face nici o procedură solicită ei înșiși.Pentru a reduce această regie, mulți compilatori moderni încearcă să întârzie utilizarea unui stack de apeluri până când este cu adevărat necesar. De exemplu, apelul unei proceduri P poate stoca adresa de returnare și parametrii procedurii apelate în anumite registre ale procesorului și poate transfera controlul către corpul procedurii printr-un simplu salt. Dacă procedura p revine fără a efectua niciun alt apel, stiva de apeluri nu este utilizată deloc. Dacă P trebuie să apeleze o altă procedură Q, va folosi stiva de apeluri pentru a salva conținutul oricăror registre (cum ar fi adresa de returnare) care vor fi necesare după ce q se întoarce.