ArchiArchitektura Komputerów/Wykład 5: Model programowy procesora w podejściu CISC i RISC

Plan wykładu





Konstrukcja modelu programowego




Model programowy procesora jest kompozycją czterech składników – zestawu rejestrów, zestawu trybów adresowania, modelu operacji warunkowych i listy instrukcji.


Istnieją trzy główne podejścia do kompozycji modelu programowego, określane skrótami RISC, CISC i – pochodzące od koncepcji RISC – podejście VLIW.



CISC i RISC




Podejścia CISC i RISC różnią się złożonością poszczególnych instrukcji. Istnieją procesory RISC wykonujące zaledwie około 30 instrukcji, ale również takie, które wykonują ponad 200 różnych instrukcji. Typowe procesory CISC wykonują kilkadziesiąt instrukcji.


CISC




Podejście CISC jest klasycznym podejściem do budowy modelu programowego komputera. W podejściu tym zakłada się, że instrukcje języka wysokiego poziomu powinny mieć proste odwzorowani e w instrukcjach procesora, a dane lokalne są przechowywane na stosie w pamięci. Rejestry służą do adresowania pamięci i przechowywania danych tymczasowych.




Z wcześniejszych założeń wynika, że procesor CISC powinien być wyposażony w tryby adresowania odzwierciedlające mechanizmy stosowane w językach wysokiego poziomu (tablice, struktury). Tymczasowe zastosowanie rejestrów nie wymaga dużej pojemności zestawu rejestrów.



Ponieważ rejestry służą do przechowywania wyników pośrednich, a nie danych programu, mogą one być zamazywane podczas kolejnych faz obliczeń. Z tego powodu dominującą grupę instrukcji stanowią instrukcje dwuargumentowe, w których wynik zastępuj jeden z argumentów źródłowych.



CISC – problemy




Złożoność poszczególnych instrukcji CISC powoduje, że jednostka wykonawcza jest skomplikowana, a wykonanie instrukcji – wielofazowe, Repertuar trybów adresowania wpływa na długość pól specyfikacji argumentów w instrukcjach.


RISC



  • Podejście RISC wprowadzono na początku lat 80-tych XX wieku
    • projekt IBM 801
    • architektury MIPS i Berkeley RISC
  • Wtedy również wprowadzono skróty CISC i RISC
  • Ważniejsze współczesne architektury RISC
    • MIPS
    • SPAR
    • ARM

Podejście RISC powstało stosunkowo niedawno. Stanowiło ono sposób na budowę szybkiego procesora o prostej strukturze.


Praktycznie wszystkie architektury opracowane po 1985 roku są architekturami klasy RISC. Niektóre z nich są stosowane zarówno w komputerach uniwersalnych, jak i w zastosowaniach wbudowanych.



RISC – charakterystyka




W podejściu RISC zakłada się, że skalarne obiekty lokalne są przechowywane w rejestrach procesora. Tym samym odwołania do pamięci ograniczają się do dostępów do danych strukturalnych oraz przeładowywania ramki stosu podczas przekazywania sterowania pomiędzy procedurami.


Duży zestaw rejestrów mieści co najmniej dane jednej procedury. Aby nie niszczyć danych podczas operacji, instrukcje specyfikują oddzielnie argumenty źródłowe i przeznaczenia. Ograniczona liczba odwołań do pamięci umożliwia redukcję liczby i złożoności dostępnych trybów adresowania.




  • Proste instrukcje dają się wykonać w prostej i szybkiej jednostce wykonawczej
    • Każda instrukcja ma tylko jeden argument docelowy
    • Najwyżej jedno odwołanie do pamięci
  • Złożone operacje można zsyntezować z kilku instrukcji
    • Również tryby adresowania
  • Instrukcje arytmetyczne i logiczne operują tylko na danych w rejestrach i argumentach natychmiastowych
    • długość argumentu jest zwykle równa długości rejestru
    • brak operacji 8- i 16-bitowych
  • Tylko dwa rodzaje instrukcji operują na pamięci:
    • Load - "ładuj"
    • Store - "składuj"
    • Tzw. "architektura load-store"

Prosta budowy procesora wynikająca z prostoty instrukcji umożliwia uzyskanie wysokiej wydajności dzięki wysokiej częstotliwości pracy. Duża wydajność procesora pozwala z kolei na syntezę dowolnie złożonych operacji i trybów adresowania na drodze programowej.


Ponieważ dane są przechowywane w rejestrach – instrukcje operujące na danych korzystają wyłącznie z rejestrów i stałych natychmiastowych. Dzięki temu długość obrazu binarnego instrukcji jest ograniczona.



RISC



  • Więcej instrukcji - dłuższa postać binarna programu.
  • Wszystkie instrukcje mają taką samą długość obrazu binarnego
    • Zwykle 32 bity
  • Jeden lub dwa tryby adresowania pamięci - rejestrowy pośredni z przemieszczeniem, ew. dwu rejestrowy pośredni


Reprezentacje instrukcji



  • Instrukcje są zapisane w pamięci podobnie jak dane - w postaci słów binarnych
  • Poszczególne instrukcje procesorów CISC mają różne długości
    • procesory x86 - instrukcje zajmują od 1 do 15 bajtów
    • w innych architekturach granularność instrukcji jest wyrażana w słowach 16-bitowych
  • RISC - stała długość instrukcji
    • w typowych procesorach RISC (np. MIPS. ARM) - każda instrukcja ma długość 32 bitów (4 bajtów)
    • ARM Thumb - instrukcje 16-bitowe, dwuargumentowe
  • Zapis instrukcji:
    • kod operacji
    • specyfikacja argumentów


Niezależności w liście instrukcji CISC




  • Dobrze skonstruowany model programowy CISC charakteryzuje się trzema niezależnościami:
    • Ortogonalność rejestrów względem trybów adresowania
      • każdy rejestr może być użyty w dowolnym charakterze w każdym trybie adresowania
  • Ortogonalność instrukcji względem trybów adresowania
    • w każdej instrukcji można użyć każdego trybu adresowania
  • Ortogonalność rejestrów względem instrukcji
    • każdy rejestr może być argumentem każdej instrukcji


Architektura x86 (IA-32)



  • Niezbyt typowa architektura CISC, zapoczątkowana przez firmę Intel
  • Pierwotnie 16-bitowa (rok 1976 - model 8086). rozszerzona do 32 bitów w 1985 roku (80386)
  • W dalszej części będzie omawiany wyłącznie model programowy w "płaskim" trybie 32-bitowym
    • w takim trybie działają aplikacje w systemach operacyjnych z lat 1995. 2004. m in. Windows 2000. Linux
  • W 2004 roku architektura x86 została ulepszona i rozszerzona do 64 bitów przez firmę AMD - AMD64


Architektura x86 jest dość nietypową, lecz najbardziej obecnie popularną architekturą CISCową. Stało się tak dzięki zastosowaniu przez firmę IBM w 1980 roku procesorów tej rodziny w komputerach IBM PC.


Architektura x86 przechodziła szereg ewolucji. W dalszej części wykładu będziemy bazowali na wersji 32-bitowej, wprowadzonej w 1985 roku. Procesory x86 mają wiele trybów pracy i wariantów organizacji modelu pamięci dla aplikacji. Prezentowane przykłady będą dotyczyły tzw płaskiego modelu pamięci, z liniowym adresowaniem i 32-bitowym adresem.



x86



  • Mały zestaw rejestrów uniwersalnych
    • 8 rejestrów, niektóre z przypisanymi funkcjami Instrukcje głównie dwuargumentowe
  • Rejestr - rejestr, rejestr - pamięć, rejestr - stała
    • Pamięć - rejestr, pamięć - stała
    • Brak operacji pamięć - pamięć
    • Operacje na danych 8-, 16-, 32-bitowych

Operacje na danych w x86, podobnie jak w większości innych architektur CISC, mogą być prowadzone na słowach o długościach 8, 16 lub 32 bitów.


x86 – operacje warunkowe



  • Model operacji warunkowych ze znacznikami
    • 6 znaczników: ZF. SF. CF. OF. AF. PF
  • Ustawianie znaczników
    • Wszystkie znaczniki są ustawiane przez dwuargumentowe instrukcje arytmetyczne i logiczne
      • Instrukcje logiczne zerują CF i OF
    • Instrukcje jednoargumentowe - nietypowo!
    • Znaczniki nie są zmieniane przez instrukcje przesłań!


x86 – rejestry



  • 8 32-bitowych rejestrów uniwersalnych: EAX. ECX, EDX. EBX, ESP, EBP, ESI, EDI
  • Rejestr stanu (w tym znaczników) EFLAGS
  • Licznik instrukcji EIP
  • Rejestry selektorów ("segmentowe") CS, SS, DS, ES, FS, GS
    • nie używane w oprogramowaniu użytkowym w trybie "płaskim"
    • nie powinny być modyfikowane przez program
      • modyfikacja zwykle powoduje późniejszy błąd
  • Jednostka zmiennopozycyjna i wektorowa x87/MMX/3DNow!
    • 8 rejestrów 64/80-bitowych MM0..7
    • obecnie wychodzi z użycia
  • Jednostka zmiennopozycyjna i wektorowa SSE
    • rejestry 128-bitowe XMM0..7

Poza jednostką stałopozycyjną, procesory rodziny x86 posiadają jednostkę zmiennopozycyjną x87, wprowadzoną jeszcze w latach 70-tych XX wieku i zbudowaną na jej bazie jednostkę wektorową MMX/3DNow! oraz nowszą jednostkę zmiennopozycyjną i wektorową SSE.


Jednostka x87 powoli wychodzi z użycia, chociaż współczesne procesory są w nią wyposażone dla zachowania zgodności binarnej oprogramowania ze starszymi modelami.


Nowe oprogramowanie korzysta z jednostki SSE, a sama jednostka jest intensywnie rozwijana poprzez rozszerzanie listy instrukcji i obsługę nowych formatów danych. Kolejne wersje jednostki są oznaczane jako SSE2, SSE3, SSSE3 i SSE4.



x86 – rejestry



  • Dostępne 16-bitowe dolne połówki wszystkich rejestrów 32-bitowych: AX, CX, DX, BX, SP, BP, SI, Dl
  • Dostępne po dwie 8-bitowe dolne ćwiartki pierwszych czterech rejestrów AH, AL, CH, CL, DH, DL, BH, BL
    • Niedostępne najmniej znaczące bajty ESP. EBP. ESI. EDI !

Dość nietypową i niewygodną z punktu widzenia konstrukcji kompilatorów cechą x86 jest niedostępność najmniej znaczących bajtów czterech z ośmiu rejestrów. W zamian za to jest możliwy dostęp do kolejnych, „drugich” bajtów czterech pierwszych rejestrów, z czego w praktyce kompilatory nie są w stanie efektywnie skorzystać. Taki model zestawu rejestrów wynika z historii architektury x86 i jej pochodzenia od 8-bitowych procesorów serii 8080.


x86 – adresowanie pamięci w trybie 32-bitowym



  • Adres złożony z trzech składników: [Rb + Ri * S + d]
    • Rb - rejestr bazowy - dowolny
    • Ri - rejestr indeksowy- dowolny oprócz ESP
    • S - skala: 1,2,4, 8
    • d - przemieszczenie lub adres absolutny
  • Każdy z składników opcjonalny, jeden musi wystąpić (jeśli tylko d, to jest to 32-bitowy adres absolutny)


x86 – format instrukcji



  • Prefiksy - jednobajtowe kody operacyjne modyfikujące sposób dekodowania instrukcji
  • ModRM - specyfikacja argumentów lub rozszerzenie kodu operacyjnego
  • SIB - bajt specyfikacji trybu adresowania pamięci dla trybów indeksowych

Szczególną cechą zapisu binarnego instrukcji w x86jest obecność prefiksów – jednobajtowych kodów operacyjnych nie reprezentujących żadnych instrukcji, lecz modyfikujących sposób dekodowania przez procesor następującej po prefiksach instrukcji.


Proste tryby adresowania, w tym rejestrowe pośrednie, są specyfikowane przez bajt ModRM. Tryby indeksowe wymagają następnego bajtu - SIB.



x86 – instrukcje jednostki stałopozycyjnej



  • Pełny opis wszystkich instrukcji jest zawarty w dokumentach: IA-32 Intel® Architecture Software Developer's Manual AMD64 Architecture Programmer's Manual
  • Przedstawimy tylko wybrane instrukcje
    • przesłania
    • podstawowe instrukcje arytmetyczne i logiczne
    • przesunięcia i rotacje bitowe
    • instrukcja LEA
    • mnożenie i dzielenie
    • skoki
    • instrukcje wspomagające implementację języków wysokiego poziomu


Instrukcje przesłań



  • MOV - przesłanie
  • XCHG - wymiana (przesłanie dwukierunkowe)
  • MOVSX - przesłanie danej z rozszerzeniem bitem znaku
    • np. MOVSX EAX, BL-dana 8-bitowa z BL jest rozszerzana do 32 bitów w EAX
  • MOVZX - przesłanie z rozszerzeniem zerami
    • np. MOVZX EAX, AL - dana 8-bitowa z AL zostaje rozszerzona zerami do 32 bitów w EAX


Instrukcje arytmetyczne i logiczne



  • Jednoargumentowe
    • INC, DEC - inkrementacja. dekrementacja
    • NOT, NEG - negacja bitów, zmiana znaku
  • Dwuargumentowe
    • ADD, ADC - dodawanie zwykłe i z przeniesieniem wchodzącym
    • SUB, SBB- odejmowanie zwykłe i z pożyczką wchodzącą
    • CMP - porównanie - odejmowanie bez zapisu wyniku (tylko ustawienie znaczników
    • AND, OR, XOR - iloczyn, suma i różnica symetryczna (bitowe)
    • TEST - iloczyn logiczny bez zapisu wyniku, tylko ustawienie znaczników (podobnie do CMP)


Instrukcje przesunięć i rotacji



  • liczba pozycji przesunięcia zapisana jako stała w instrukcji lub zmienna - w rejestrze CL (brak ortogonalności)
  • SHL, SHR- przesunięcie w lewo/prawo z dopełnieniem zerami
  • SAR - przesunięcie „arytmetyczne" w prawo, z kopiowaniem bitu znaku
    • służy do realizacji dzielenia liczb ze znakiem przez potęgi liczby 2 
  • ROL, ROR - rotacja w lewo/prawo
  • RCL, RCR - rotacja w lewo/prawo poprzez bit przeniesienia
    • może służyć do realizacji przesunięć bardzo długich danych o jeden bit
  • SHLD, SHRD- przesunięcie dwóch słów w lewo/prawo z zapisem bardziej/mniej znaczącego słowa wyniku
    • pełne przesunięcie wymaga sekwencji SHLD. SHL lub SHRD. SHR


Instrukcje LEA



  • Load Effective Address - ładuj adres efektywny
  • Zapis jak dla instrukcji MOV z argumentem docelowym w rejestrze i źródłowym w pamięci
  • Ładuje ADRES do rejestru, nie wykonuje przesłania danych
  • Traktowana jak instrukcja przesłania - nie ustawia znaczników


LEA – zastosowanie



  • mnożenie przez 3, 4, 5, 8, 9
    • LEA EAX, [EBX*8]
    • LEA EAX, [EBX+EBX*4]
  • Dodawanie wieloargumentowe
    • LEA EAX, [EBX+ECX-30]


Mnożenie i dzielenie



  • Mnożenie
    • trzy postaci
      • jednoargumentowa - argumety domyślne, wynik 2x dłuższy od argumentów
      • dwuargurnentowa - rejestr, rejestr/pamięć – wynik o długości argumentów
      • trój argumentowa - rejestr, rejestr/pamięć, stała – wynik o długości argumentów
    • dwie formy
      • MUL - bez znaku (jedno- i dwu argumentowe)
      • IMUL-ze znakiem
  • Dzielenie
    • jedna postać - jednoargumentowa - argumenty domyślne
      • komplementarna do mnożenia jednoargumentowego
      • dzielna 2x dłuższa od dzielnika i ilorazu
    • dwie formy DIV - bez znaku. IDlV - ze znakiem


Mnożenie i dzielenie "jednoargumentowe"




  • Dzielenie liczb o jednakowej długości wymaga wcześniejszego rozszerzenia dzielnej
    • DIV - przez dopełnienie zerami, np. MOVEDX, 0
    • IDIV - przez rozszerzenie biten znaku - specjalne instrukcje bezargumentowe: CBW, CWD, CDQ
  • Instrukcje dzielenia generują pułapkę przy wystąpieniu nadmiaru


Instrukcje skoku



  • służą do przekazywania sterowania z jednego ciągłego fragmentu programu do drugiego
  • rodzaje
    • bezwarunkowe - wykonywane zawsze
    • warunkowe - wykonywane przy spełnieniu pewnego warunku, np. "skocz jeśli zawartość rejestru różna od zera" (w przeciwnym razie po instrukcji skoku jest wykonywana następna instrukcja w sekwencji)


Skoki



  • Ze stałym adresem docelowym - warunkowe i bezwarunkowe
    • JMP, CALL, Jcc
  • Powroty z procedur
    • RET
    • RET n - ze zdjęciem argumentów (np. konwencja wołania Pascal)
  • Ze zmiennym adresem docelowym - bezwarunkowe - JMP i CALL
    • JMP EAX
    • JMP [Table+ECX*4]


Instrukcje wspomagające języki wysokiego poziomu



  • RET n - powrót z procedury z przesunięciem wskaźnika stosu o n bajtów po zdjęciu śladu
    • potrzebna przy konwencji wołania stosowanej np w języku Pascal (argumenty usuwane przez procedurę wywoływaną)
  • ENTER 0, n - prolog procedury
    • zastępuje sekwencję PUSH EBP: MOV EBP. ESP: SUB ESP, n
    • przy pierwszym argumencie ≠ 0 może być użyta do konstrukcji prologu dla języka Pascal (zagłębianie leksykalne procedur)
  • LEAVE - epilog procedury
    • równoważna parze MOV ESP. EBP: POP EBP
  • BOUND r, m - sprawdzenie zakresu zmiennej
    • używana do sprawdzenia poprawności indeksu tablicy w języku Pascal poprzez porównanie z granicami przedziału dozwolonych wartości


Architektura MIPS32



  • Współczesna 32-bitowa wersja architektury MIPS, wprowadzonej w latach 80-tych XX wieku
  • Klasyczna prosta architektura RISC
  • Instrukcje o stałej długości - 32 bity
  • 32 rejestry stałopozycyjne
    • rejestr numer 0 - stałe 0
    • rejestr numer 31 - uniwersalny, również rejestr śladu dla instrukcji skoku ze śladem
  • 2 rejestry jednostki mnożąco-dzielącej - HI, LO
  • Licznik instrukcji - PC
  • 32 rejestry zmiennopozycyjne f0..f31
  • Brak sprzętowej realizacji stosu
    • operacje stosowe syntezowane z instrukcji przesłań i oddzielnej modyfikacji wskaźnika stosu


MIPS32 - rejestry




MIPS32 - formaty instrukcji




MIPS32 - instrukcje



  • Format R
    • instrukcje arytmetyczne i logiczne z argumentami w rejestrach
    • skok i skok ze śladem z adresem w rejestrze
  • Format I
    • instrukcje arytmetyczne i logiczne z argumentem natychmiastowym
    • instrukcje wymiany z pamięcią
    • skoki warunkowe (zasięg do 128 KB)
  • Format J
    • skok i skok ze śladem z długim adresem natychmiastowym

16-bitowa stała w formacie I służy, w zależności od instrukcji, jako argument natychmiastowy, przemieszczenie adresu danej w pamięci lub przemieszczenie skoku. Ponieważ instrukcje są zapisane w postaci słów 32-bitowych wyrównanych naturalnie, adres instrukcji jest zawsze podzielny przez 4. Przemieszczenie skoku jest traktowane jako przemieszczenie słowowe – jest ono przesuwane w lewo o dwa bity, co umożliwia uzyskanie zasięgu skoków w zakresie od -128 do +128 KB.


Architektura ARM



  • Architektura RISC, 32 bity
  • Wprowadzona wr. 1985 jako 16-bitowa
  • Bardzo rozpowszechniona w zastosowaniach wbudowanych
    • telefony komórkowe
    • urządzenia do transmisji danych (Bluetooth. osprzęt sieciowy)
  • 16 rejestrów 32-bitowych
    • dodatkowe rejestry systemowe
  • model operacji warunkowych z użyciem znaczników
  • warunkowe wykonanie niemal wszystkich instrukcji
  • dostępne operacje stosowe


ARM - rejestry



  • 16 rejestrów r0..15
    • r0. 12 - całkowicie uniwersalne
    • r 13 (SP) - wskaźnik stosu
    • r14 (LR) - rejestr śladu
    • r 15 (PC) - licznik instrukcji
  • rejestr stanu CPSR
    • zawiera bity znaczników N. Z. C, V oraz informacje systemowe


ARM - charakterystyka instrukcji



  • Pierwotnie instrukcje 32-bitowe
    • trójargumentowe
    • większość instrukcji warunkowa - instrukcje zawierają specyfikacje warunku wykonania
  • Współczesne procesory ARM mają drugi, alternatywny zestaw instrukcji - Thumb
    • instrukcje 16-bitowe. dwuargumentowe
    • ograniczony dostęp do części rejestrów
    • instrukcje warunkowe - tylko skoki