Articles

Subroutine

het idee van een subroutine werd uitgewerkt nadat computers al enige tijd bestonden.De rekenkundige en conditionele spronginstructies waren van tevoren gepland en zijn relatief weinig veranderd, maar de speciale instructies voor procedureoproepen zijn in de loop der jaren sterk veranderd.De vroegste computers en microprocessors, zoals de Manchester Baby en de RCA 1802, hadden geen enkele subroutine oproep instructie.Subroutines konden worden geà mplementeerd, maar ze vereiste programmeurs om de oproepvolgorde—een reeks instructies—te gebruiken op elke oproepsite.

Subroutines werden geïmplementeerd in Konrad Zuse ‘ s Z4 in 1945.in 1945 gebruikte Alan M. Turing de termen “bury “en” unbury ” als een middel om te bellen en terug te keren van subroutines.in januari 1947 presenteerde John Mauchly algemene aantekeningen op “a Symposium of Large Scale Digital Calculating Machinery” onder de gezamenlijke sponsoring van Harvard University en het Bureau of Ordnance, United States Navy. Hier bespreekt hij de seriële en parallelle operatie die

suggereert…de structuur van de machine hoeft niet ingewikkeld een beetje. Aangezien alle voor deze procedure essentiële logische kenmerken beschikbaar zijn, is het mogelijk een coderingsinstructie te ontwikkelen voor het plaatsen van de subroutines in het geheugen op plaatsen die de machine kent, en op zodanige wijze dat zij gemakkelijk in gebruik kunnen worden genomen.

met andere woorden, men kan subroutine A als deling en subroutine B als complexe vermenigvuldiging en subroutine C als de evaluatie van een standaardfout van een reeks getallen, enzovoort door middel van de lijst van subroutines die nodig zijn voor een bepaald probleem. … Al deze subroutines worden dan opgeslagen in de machine, en alles wat men hoeft te doen is een korte verwijzing naar hen door nummer, zoals ze zijn aangegeven in de codering.

Kay McNulty had nauw samengewerkt met John Mauchly in het ENIAC-team en ontwikkelde een idee voor subroutines voor de ENIAC-computer die ze programmeerde tijdens de Tweede Wereldoorlog. Goldstine en von Neumann schreven een artikel van 16 augustus 1948 over het gebruik van subroutines.

sommige zeer vroege computers en microprocessors, zoals de IBM 1620, Intel 4004 en Intel 8008, en de PIC microcontrollers, hebben een single-instruction subroutine aanroep die een dedicated hardware stack gebruikt om retouradressen op te slaan—dergelijke hardware ondersteunt slechts een paar niveaus van subroutine nesting, maar kan recursieve subroutines ondersteunen. Machines voor het midden van de jaren zestig-zoals de UNIVAC I, de PDP—1 en de IBM 1130-gebruiken meestal een aanroepconventie die de instructieteller opslaat in de eerste geheugenlocatie van de aangeroepen subroutine. Dit staat willekeurig diepe niveaus van subroutine nesting toe, maar ondersteunt geen recursieve subroutines. De PDP-11 (1970) is een van de eerste computers met een stack-pushing subroutine aanroep instructie; deze functie ondersteunt zowel willekeurig diepe subroutine nesting en ondersteunt ook recursieve subroutines.

Language supportEdit

in de zeer vroege assemblers was subroutine ondersteuning beperkt. Subroutines werden niet expliciet gescheiden van elkaar of van het hoofdprogramma, en inderdaad kon de broncode van een subroutine worden afgewisseld met die van andere subprogramma ‘ s. Sommige assemblers zouden voorgedefinieerde macro ‘ s aanbieden om de call en return sequences te genereren. Tegen de jaren 1960 hadden assemblers meestal veel meer geavanceerde ondersteuning voor zowel inline als afzonderlijk geassembleerde subroutines die aan elkaar konden worden gekoppeld.

subroutine librariesEdit

zelfs met deze omslachtige aanpak bleken subroutines zeer nuttig. Voor een ding, ze toegestaan het gebruik van dezelfde code in veel verschillende programma ‘ s. Bovendien was geheugen een zeer schaarse bron op vroege computers, en subroutines konden aanzienlijke besparingen in de grootte van programma ‘ s.

veel vroege computers geladen de programma-instructies in het geheugen van een geponst Papier tape. Elke subroutine kan dan worden geleverd door een apart stuk tape, geladen of gesplitst voor of na het hoofdprogramma (of “mainline”); en dezelfde subroutine tape kan dan worden gebruikt door veel verschillende programma ‘ s. Een soortgelijke aanpak toegepast in computers die ponskaarten gebruikt voor hun belangrijkste input. De naam subroutine library betekende oorspronkelijk een bibliotheek, in de letterlijke zin, die geïndexeerde collecties van tapes of kaart-decks voor collectief gebruik hield.

Return by indirect jumpEdit

om de noodzaak van zelfmodificerende code te verwijderen, gaven computerontwerpers uiteindelijk een indirecte springinstructie, waarvan de operand, in plaats van het retouradres zelf, de locatie was van een variabele of processorregister dat het retouradres bevatte.

Op die computers, in plaats van de terugkeersprong van de subroutine te wijzigen, zou het aanroepende programma het retouradres in een variabele opslaan, zodat wanneer de subroutine voltooid is, het een indirecte sprong zou uitvoeren die de uitvoering zou leiden naar de locatie die door de vooraf gedefinieerde variabele wordt gegeven.

Jump to subroutineEdit

een ander voordeel was de spring naar subroutine instructie, die het opslaan van het retouradres combineerde met de aanroepende sprong, waardoor de overhead aanzienlijk werd geminimaliseerd.

in het IBM-systeem / 360, bijvoorbeeld, zouden de branch instructions BAL of BALR, ontworpen voor het aanroepen van de procedure, het retouradres opslaan in een in de instructie gespecificeerd processorregister, volgens convention register 14. Om terug te keren hoefde de subroutine alleen een indirecte branch instructie (BR) uit te voeren via dat register. Als de subroutine dat register nodig had voor een ander doel (zoals het aanroepen van een andere subroutine), zou het de inhoud van het register opslaan op een privé geheugen locatie of een register stack.

in systemen zoals de HP 2100 zou de JSB instructie een soortgelijke taak uitvoeren, behalve dat het retouradres was opgeslagen in de geheugenlocatie die het doel was van de branch. De uitvoering van de procedure zou eigenlijk beginnen bij de volgende geheugenlocatie. In de HP 2100 assembly taal zou men bijvoorbeeld

 ... JSB MYSUB (Calls subroutine MYSUB.) BB ... (Will return here after MYSUB is done.)

schrijven om een subroutine genaamd MYSUB aan te roepen vanuit het hoofdprogramma. De subroutine zou worden gecodeerd als

 MYSUB NOP (Storage for MYSUB's return address.) AA ... (Start of MYSUB's body.) ... JMP MYSUB,I (Returns to the calling program.)

De JSB instructie plaatste het adres van de volgende instructie (namelijk, BB) in de locatie gespecificeerd als zijn operand (namelijk, MYSUB), en vervolgens vertakt naar de volgende locatie daarna (namelijk, AA = MYSUB + 1). De subroutine kon dan terugkeren naar het hoofdprogramma door het uitvoeren van de indirecte sprong JMP MYSUB, I die vertakt naar de locatie opgeslagen op locatie mysub.

Compilers voor Fortran en andere talen kunnen gemakkelijk gebruik maken van deze instructies indien beschikbaar. Deze aanpak ondersteunde meerdere niveaus van oproepen; echter, omdat het retouradres, parameters en retourwaarden van een subroutine vaste geheugenlocaties werden toegewezen, stond het geen recursieve oproepen toe.

Overigens werd een soortgelijke methode gebruikt door Lotus 1-2-3, in de vroege jaren 1980, om de herberekeningsafhankelijkheden in een spreadsheet te ontdekken. Namelijk, een locatie was gereserveerd in elke cel om het retouradres op te slaan. Aangezien cirkelreferenties niet zijn toegestaan voor natuurlijke herberekeningsvolgorde, staat dit een tree walk toe zonder ruimte te reserveren voor een stack in het geheugen, die zeer beperkt was op kleine computers zoals de IBM PC.

Call stackEdit

De meeste moderne implementaties van een subroutine aanroep gebruiken een call stack, een speciaal geval van de stack data structuur, om subroutine aanroepen en retourneren te implementeren. Elke procedure aanroep maakt een nieuwe regel aan, genaamd een stack frame, aan de bovenkant van de stack; wanneer de procedure terugkeert, wordt het stack-frame van de stack verwijderd en kan de spatie worden gebruikt voor andere procedureaanroepen. Elk stack frame bevat de persoonlijke gegevens van de overeenkomstige oproep, die meestal de parameters en interne variabelen van de procedure, en het retouradres omvat.

De aanroepvolgorde kan worden geà mplementeerd door een reeks gewone instructies (een benadering die nog steeds wordt gebruikt in RISC-architecturen (reduced instruction set computing) en very long instruction word (VLIW) architecturen), maar veel traditionele machines die sinds de late jaren zestig zijn ontworpen, hebben speciale instructies voor dat doel opgenomen.

de call stack wordt meestal geà mplementeerd als een aaneengesloten gebied van geheugen. Het is een willekeurige ontwerpkeuze of de onderkant van de stack het laagste of hoogste adres binnen dit gebied is, zodat de stack vooruit of achteruit in het geheugen kan groeien; veel architecturen kozen echter voor het laatste.

sommige ontwerpen, met name enkele Forth-implementaties, gebruikten twee afzonderlijke stacks, één voornamelijk voor controle-informatie (zoals retouradressen en lus-tellers) en de andere voor gegevens. De eerste was, of werkte als, een call stack en was alleen indirect toegankelijk voor de programmeur via andere taalconstructies, terwijl de laatste meer direct toegankelijk was.

toen stack-based procedure calls voor het eerst werden geïntroduceerd, was een belangrijke motivatie om kostbaar geheugen te besparen. Met dit schema hoeft de compiler geen aparte ruimte in het geheugen te reserveren voor de privégegevens (parameters, retouradres en lokale variabelen) van elke procedure. Op elk moment bevat de stack alleen de privégegevens van de oproepen die momenteel actief zijn (namelijk, die zijn aangeroepen maar nog niet zijn teruggekeerd). Vanwege de manieren waarop programma ’s gewoonlijk uit bibliotheken werden samengesteld, was (en is) het niet ongewoon om programma’ s te vinden die duizenden subroutines bevatten, waarvan er slechts een handvol op een bepaald moment actief zijn. Voor dergelijke programma ‘ s kan het call stack mechanisme aanzienlijke hoeveelheden geheugen besparen. Inderdaad, de call stack mechanisme kan worden gezien als de vroegste en eenvoudigste methode voor automatisch geheugenbeheer.

echter, een ander voordeel van de call stack methode is dat het recursieve subroutine aanroepen toestaat, omdat elke geneste call naar dezelfde procedure een aparte instantie van zijn privégegevens krijgt.

Delayed stacking Edit

een nadeel van het call stack mechanisme is de verhoogde kosten van een procedure call en de bijbehorende return. De extra kosten omvatten het verhogen en verwijderen van de stack pointer (en, in sommige architecturen, controleren op stack overflow), en toegang tot de lokale variabelen en parameters door frame-relatieve adressen, in plaats van absolute adressen. De kosten kunnen worden gerealiseerd in verhoogde uitvoeringstijd, of verhoogde processor complexiteit, of beide.

deze overhead is het meest voor de hand liggend en verwerpelijk in bladprocedures of bladfuncties, die terugkeren zonder zelf een procedure aan te roepen.Om die overhead te verminderen, proberen veel moderne compilers het gebruik van een call stack uit te stellen totdat het echt nodig is. Bijvoorbeeld, de oproep van een procedure P kan het retouradres en de parameters van de opgeroepen procedure op te slaan in bepaalde processor registers, en de overdracht van de controle aan het lichaam van de procedure door een eenvoudige sprong. Als de procedure P terugkeert zonder een andere call, wordt de call stack helemaal niet gebruikt. Als P een andere procedure Q moet aanroepen, zal het de call stack gebruiken om de inhoud van registers (zoals het retouradres) op te slaan die nodig zijn nadat Q retourneert.