Тема 7

Инструкции за управление на програмата. Извикване на подпрограми. Програми без преходи.


Важно!! Тези лекции не са предвидени за директен copy-paste на контролното. Прочетете ги внимателно, и ги научете. Хора с еднакви контролни ще бъдат санкционирани! Да не кажете после че не сте предупредени



страницата се нуждае от дописване/преглеждане


Вече сме писали програми и подпрограми, сега е време да разберем как компютърът реализира изпълнението на тези програми и техните подпрограми.

Управляващи инструкции

Имаме специална група инструкции, които нарушават концептуалния ред на последователно изпълнение в Ноймановата архитектура. Тези инструкции включват инструкцията за условен преход (branch), безусловен преход (jump), за влизане в подпрограма (program calls), връщане на подпрограми (program return) също и командите system calls и system returns. Последните 2 команди са за достъп до системни програми и системните програми са достъпни само чрез system call.

Аспекти на управляващите инструкции

  • Ще/Няма да има преход.
  • Как се вгражда(задава) условието при branch(често условието проверява флагов регистър). Флаговия регистър се определя от операциите, който АЛУ-то извършва. При всяка операция се променя флагът.
  • Как се изчислява адресът на прехода.
  • Link Return Address - връзване на адресите на връщане(прави се при преходи calls и returns - тоест при подпрограмите)

Инструкции за управление

  • jump за безусловен преход (unconditional);
  • branch за условен преход (conditional)
  • инструкции за извикване връщане на подпрограми
    • call
    • return
  • инструкции за системно извикване и връщане
    • system calls
    • system returns

Детайли

jump / branch

jump винаги променя съдържанието на програмния брояч, докато при branch има условие и PC се променя единствено ако това условие е изпълнено. Как ще се вгради условието при branch е въпрос на реализация на архитектурата. Условните преходи могат да се направят по такъв начин, че в тях да се изчислява стойността на условието. Най-често условието е сравнение – преход се извършва, ако резултатът от сравнението е положителен, така най-често в инструкциите branch се описва какво се сравнява и адресът на прехода. Сравнението се изпълнява в АЛУ-то, това е единият тип архитектури с условни преходи с инструкции за сравнение. При другия тип архитектури инструкциите за сравнение са отделени от инструкциите за условен преход и те се извикват непосредствено преди това.

flags

t07_alu_flags

Условието често тества флагов регистър.
Признаци на резултата се записват във флаговия регистър. Всяка аритметична операция слага някакви флагове. Те са особени правила, по които се разпознава условието при прехода, в зависимост от състоянието на флаговете.

Недостатък на кодовете за условен преход е, че чрез тях се получава неявна връзка между инструкциите – примерно условен преход задължително трябва да се изпълни след изпълнение на аритметична операция с АЛУ за числа с фиксирана точка. При pipelining(конвейер) това може да наруши бързодействието - при инструкциите за преход целият конвейер се изчиства.

call / return

call съдържа адрес на преход, с който се сменя съдържанието на PC и по този начин се отива в друга програма. При фон Ноймановата архитектура извиканата подпрограма задължително трябва да се върне в извикващата

system call / return

system call означава, че програмата се обръща към някоя особена системна програма, която е различна от обикновените програми. Операционната система е съставена от такива системни програми. system call трябва да запази цялото състояние на процесора (при обикновен call се запазва стойността само на общите регистри ( AX, BX, CX, DX, SI, DI, BP – Base Pointer )).
Това е необходимо, тъй като при system call се напуска режима на обикновена програма и се влиза в режим на системна програма. При връщане със system return трябва да се възстанови цялото състояние на процесора.
Един начин за изпълнение на system call е прекъсване, инициирано от програмата. При него в PC се зарежда адрес на фиксиран вектор на прекъсване – всеки такъв вектор съдържа начален адрес на системна програма. Векторите са номерирани и system call с операнд номер на вектор означава, че по този номер се адресира векторът и се прави преход по неговата стойност.
При връщане от прекъсване се възстановява състоянието на целия процесор (включително стойността на стековия указател).

Формиране на адреса на прехода

Адресът на прехода се формира по 4 възможни начина:

  1. като относителен адрес спрямо програмния брояч (PC + offset);
  2. Чрез базов регистър и отместване (Rbase + displacement);
  3. Чрез задаване на абсолютен адрес (частен случай на горното при Rbase == 0);
  4. Чрез вектори.
t07_addr_formation

PC-relative

Формирането на адреса на прехода чрез програмния брояч и определено отместване от него е относителна адресация. В нея няма зададен адрес само отместването спрямо текущия. Това се нарича PC-relative адресация. Според статистиката, в 94% от случаите отместванията са на не повече от 256 байта спрямо текущата позиция, тоест достатъчно е дължината на полето на offset-a да е 8 бита. Това е най-правилният и систематичен метод(наистина така пише), но недостатъкът му е, че при фиксирана дължина на преместването не могат да се правят далечни преходи. Това го прави неуниверсален.

Rbase + displacement

При втория случай в базов регистър се държи адресът на началото на 64КВ сегмент в паметта а displacement-ът определя отместването спрямо този адрес. Това е стандартният начин за получаване на адрес на прехода. По този начин могат да се достъпват всички части на оперативната памет. Отместването е 16 бита. Базовите регистри могат да сочат към произволно място в оперативната памет, отделено за една програма. Недостатъкът на този механизъм е, че адресът на прехода не е свързан с текущия адрес и това затруднява конвейера, при достигане на етапа на изпълнение.

t07_pipeline_setip

В програмния брояч е заредена следващата инструкция и ако по време на изпълнение (execution) се достигне до преход, то това накъсва работата на конвейера.
При адресиране от първия тип броячът се обновява директно със offset. (т.е няма нужда да се стига до АЛУ-то, за да направи преходът).

absolute

Третият начин е чрез директно указване на адрес на преход или указване на регистър, който го съдържа. Този метод е най-бърз, тъй като новата стойност директно се записва в програмния брояч, но полето на адреса е по голямо (32 бита) и програмата не е мобилна. Това означава, че ако програмата се зареди на друг адрес в паметта, няма да работи коректно (или поне ако мястото, в/у което прескача е заредено на друго място). Затова този начин не е ефективен (дървен е).

vector

Четвъртият начин на адресация е чрез използването на специализираните векторни пространства. (няма нищо общо с алгебрата. Може да си мислите за векторното пространство като масив от указатели на различни преходи). Те започват от известен адрес в паметта. При векторните пространства от този адрес нататък следват 256 вектора по 4 байта, като адресирането се определя само първия чрез jmp x, където дължината на х е 1 байт (х е от 0 до 255). Векторът х сочи в полето на програмата. Частта от ОП, която се заема от векторното пространство. ( При Intel архитектурата то е от адресите 0х0000000 до 0х00000FF ). В тази адресация има неявни база и отместване. Базовият адрес на началото на ВП се пази в специален регистър. ВП се използва за специални нужди, например за адресиране на преходите при прекъсване. Схемата за бърза дешифрация не може да следи дали има преход при векторната система. Тя се счита за опасна, тъй като е неудобна за конвейера. Не се счита за добър начин на програмиране, предимствата й са прескачанията при прекъсванията.

Хардуерната възможност процесора да прави прескачания се нарича система за прекъсвания. Това е модификация на архитектурата на фон Нойман.

Преходи към подпрограми

Друга особеност на инструкциите за прехода е преходът към подпрограма. За да може да е динамично(извиканата подпрограма да се връща там откъдето е извикана), програмата трябва да знае с какъв преход да се върне – това става чрез link return register.

t07_call_pic1

В самата инструкция на извиканата подпрограма не се вижда адресът на връщането. Той може да се укаже експлицитно или да бъде в регистър по премълчаване (имплицитно указване). При извикването на подпрограмата програмният брояч се обновява със следващата инструкция в паметта, новополученият адрес се запомня във указания регистър Rx или регистър по премълчаване Re или в стека. Когато програмистът указва в кой регистър се запазва адреса на връщане, трябва да внимава подпрограмата да не промени стойността на този регистър.

Друга възможност е адресът за връщане директно да се записва в стек – тогава при всяко извикване на подпрограма адресът за връщане се вмъква във върха на стека и при връщане от подпрограмата този адрес се изважда от върха на стека и се записва в PC. По този начин се решава проблемът с извикването на няколко вложени подпрограми.

При всяко влизане в подпрограма се налага да се запазва състоянието на процесора. При извикване на call се запазват само регистрите, при извикване на system call освен регистрите се запазват указателите към стека и др.

Най-често начинът за връзка между подпрограмите се осъществява с имплицитен регистър за връзка, в който се записва адресът за връщане.
При имплицитните регистри за връзка всяка инструкция за преход към подпрограма записва адреса на връщане в един и същи регистър – за това трябва да се внимава при последователни преходи да не се изгубят предишни адреси.
Друга възможност е адресът за връщане директно да се записва в стек – тогава при всяко извикване на подпрограма адресът за връщане се вмъква във върха на стека и при връщане от подпрограмата този адрес се изважда от върха на стека и се записва в PC.

Софтуерното запазване на състоянието става преди инструкцията call, така че адресът за връщане не е бил запазен в запомненото състояние на регистрите. При последователни извиквания не е проблем новият адрес за връщане да се записва в същия регистър за връзка, поради запазването на състоянието – единственото ограничение е самите подпрограми да не променят регистъра за връзка по време на изпълнението си. В края на всяка подпрограма се извиква инструкция jump с номер регистъра за връзка и по този начин управлението се връща на извикващата програма.

t07_call_savearea

Запазване на регистрите

Последният аспект на управляващите инструкции е запазването на регистрите. За да работи по-бързо една програма се работи с операнди записани в регистрите. При всеки преход към подпрограма този контекст се губи и трябва да се възстанови при връщането. Тази програма трябва да има област на запазване (save area), в която временно се записва този контекст. Софтуерната конвенция е извикващата функция да има грижата да запази контекста си, и при връщане от подпрограмата да го възстанови. В мястото за връщане трябва да има инструкция за връщане на контекста, тоест всеки път се прави multiple load и multiple
store
, за запазване и зареждане на всички регистри. Има регистър Rsa, в който се пази адресът на тази save area. Има и друга конвенция(callee) при която подпрограмата се грижи преди return да попълни регистрите със информацията, такава каквато е била при нейното извикване.

Групи регистри

  • in - аргументите които приема една подпрограма
  • out - резултатът от изпълнението (той може да се връща на извикващата програма)
  • local - локални променливи
  • global - глобални променливи

При влизане се пазят in и local, а при излизане out и local. При системните извиквания се пазят всички регистри, а при обикновените само базовите.

Инструкции за условно преместване на данни. Предикатни данни

Заради конвейера казваме, че условните преходи са „убиец”. Устройството за предварително разпознаване не може да разпознае прехода, поради което инструкциите за условен преход прекъсват конвейера. Това може да се предотврати като се използва
инструкция с предикати

Условно преместване

blez

Aко имаме фрагмента

if (a > 0) c = b*a;

това със стандартни инструкции би изглеждало така
#0 blez r1, #2 ( Branch Less than or Equal to Zero )
#1 mul r3, r2, r1
#2 ...

Ако r1 е по-малко или равно на нула се отива директно на инструкция #2, т.е прескача се умножението
При условната инструкция blez се разкъса конвейера и целия се изчиства за да се почисти само стъпка #1

cmovgt

Ако има инструкция с условно местене:

#0 mul r4,r2,r1
#1 cmovgt r3,r4,r1 (Conditional MOVe Greater Than zero)

Ако r1 <= 0 в r3 не отива резултатът - няма преход, нито нарушаване на конвейра.

При blez варианта умножението не се изпълнява ако не е нужно, докато при втория вариант само запазването на резултата се прескача. При по-дълги конвейри е по-добре да се направят излишни сметки (като във втория случай) само за да не се наложи изпразване на целия конвейр (защото това би било по-бавно).

предикати

Третият начин е чрез създаване на предикатни дейности. При предиката трябва да се определя процесорна променлива, която е самостоятелна и еднобитова. Но те не са адресируеми. Ако има предикати трябва всички инструкции освен от операндите си да зависят и от определен номер предикат, който ако е 1 се изпълнява инструкцията, а ако е 0 не се изпълнява. В зависимост от него се определя дали ще има действие на стъпка изпълнение, но това не нарушава конвейера. Програмирането с предикати е много сложно, и до сега няма реализиран компютър, който да използва този метод.

#0 sgtzp  p1,r1 (Set Greater Than Zero Predicate)
#1 mulp r3,r2,r1,p1 (MULtiply on Predicate)

Първият ред зарежда в предиката p1 резултата от сравнението на r1 с нула. Вторият ред се изпълнява, само ако предикатът е 1, т.е r1 > 0.

Разлики между RISC и CISC процесори

RISC CISC
брой цикли на процесора за 1 инструкция моноцикъл - инструкциите се изпълняват за 1 цикъл на процесора. Операндите и резултата са в регистрите мултицикъл - всяка инструкция отнема повече от 1 такт на процесора
хардуерно/софтуерно управление хардуерно управление - всички инструкции са реализирани на хардуерно ниво - т.е в интегрални схеми всяка сложна инструкция е микропрограма, записана в процесора
връзка с паметта само load/store операциите работят с паметта. Всички сметки минават през регистрите има по-сложни инструкции за адресиране от вид M-R и M-M
дължина на инструкциите фиксирана дължина на инструкциите (пр. 4B) променлива дължина на инструкциите
режими на работа на CPU малко режими на работа на процесора много режими на работа на процесора

Идеята за RISC идва от учени в университета в Бъркли. Те публикуват манифест за това какво представлява RISC-машината. Няма определен манифест, по който да се определя дали една архитектура е CISC, счита се че тя е такава, когато не е RISC. RISC – фактора определя колко пъти са по-бързи RISC машините – между 2.7 и 3.7 пъти.ите

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-NonCommercial-ShareAlike 3.0 License