dlaczego architektury procesorów używają rejestru flag (zalety?)

15

Niektóre procesory mają rejestr flag (ARM, x86, ...), inne nie (MIPS, ...). Jaka jest zaleta posiadania instrukcji CMP do aktualizacji rejestru flag, a następnie instrukcji gałęzi zamiast używania rejestru zerowego i gałęzi warunkowych do sprawdzania znaku, przepełnienia itp.?

modelowy świat
źródło

Odpowiedzi:

11

We współczesnych mikroarchitekturach ze zmianą nazw rejestrów koszt wdrożenia flag lub nie flag jest dość podobny. Główną różnicą, o której mogę myśleć, jest to, że niektóre flagi wskazują cechy wartości (czy wartość jest ujemna? Czy wartość wynosi zero? Czy wartość ma parzystą czy nieparzystą parzystość?), Podczas gdy niektóre reprezentują zdarzenie, które wystąpiło podczas poprzedniej operacji (czy instrukcja add miała wykonanie lub przepełnienie?) Doprowadziło to do niezbyt idealnej sytuacji w MIPS, gdy chcieliśmy symulować 64-bitowe dodawanie w architekturze 32-bitowej (lub 128-bitowe dodawanie w Architektura 64-bitowa.) Na większości architektur z flagą carry jest coś specjalnegoadd-with-carryinstrukcja, która zawiera flagę carry z poprzedniej instrukcji add. To sprawia, że ​​symulowanie arytmetyki wieloprecyzyjnej jest stosunkowo niedrogie na wielu architekturach z rejestrami flag.

Z drugiej strony, testowanie rejestru N-bitowego na zero lub niezerowe jest w rzeczywistości zaskakująco drogie. Aby przetestować N-bitowy rejestr na zero, należy wykonać N-bitową operację NOR, która wymaga obliczenia poziomów logiki . W architekturach z rejestrami flag dodatkowa logika do obliczania wartości zerowej / niezerowej na końcu etapu ALU może spowodować wolniejsze działanie zegara (lub zmusić ALU do wykonania dwóch operacji cyklu). Myślę, że z tego powodu niektóre architektury, podobnie jak SPARC, miały dwie wersje każdej operacji arytmetycznej, jedną, która ustawiała flagi, a drugą nie.O(logN.)

Ale MIPS niczego tu nie zapisuje. Właśnie przenieśli problem gdzieś indziej. Na MIPS jest branch-on-equalinstrukcja. Oznacza to, że instrukcja rozgałęzienia musi faktycznie posiadać etap ALU (w tym coś w rodzaju xoroperacji bitowej , po której następuje norredukcja do pojedynczego bitu równego / nierównego) przed ustaleniem, w którą stronę idzie gałąź.

Architektura DEC Alpha próbowała rozdzielić różnicę za pomocą lewy. DEC Alpha nie ma rejestrów flag, ale także nie ma branch-on-equalinstrukcji. Zamiast tego wszystkie instrukcje oddziału patrzą na stan jednego rejestru ogólnego przeznaczenia. Tam jest branch-on-zero, branch-on-not-zero, branch-on-less-than-zero, itd. Sztuką jest to, że można dać każdy ogólnego przeznaczenia zarejestrować dodatkowy bit 65. informujący, czy pozostałe 64 bity są zerowe lub nie. To bardziej przypomina rejestrowanie flag: wszystkie instrukcje rozgałęzienia patrzą na pojedynczy bit (który jest już obliczony), aby podjąć decyzję, ale teraz wróciłeś do zastanawiania się, jak obliczyć ten dodatkowy bit wskaźnika zero podczas normalnego ALU cykl. (I nadal nie można wykonywać arytmetyki wieloprecyzyjnej, patrząc tylko na flagę carry z poprzedniej operacji).

Wędrująca logika
źródło
2
Operacje związane z ustawieniami innymi niż CC były (z tego, co rozumiem) optymalizacją kompilatora , pozwalając kompilatorowi na wcześniejsze zaplanowanie instrukcji ustawiania CC bez blokowania wartości przez ostatnie instrukcje. PowerPC750 umieścił rejestry warunków (8 rejestrów 4-bitowych) bliżej przedniej części, tak że pobrana gałąź trafiająca do pamięci podręcznej instrukcji rozgałęzienia docelowego i posiadająca warunek wystarczająco wcześnie mogłaby rozwiązać przejętą gałąź bez kary. (CRISP AT&T również wykorzystywał wczesną rozdzielczość gałęzi.) Mała ilość i specjalizacja CC sprawia, że ​​jest to bardziej praktyczne.
Paul A. Clayton
Szczegół: wszystkie obliczenia flag nie są sobie równe. Wyobraź sobie, że twój procesor ma tradycyjne flagi NZVC. Jeśli wszystkie instrukcje ALU mogą zaktualizować flagi, musisz umieścić generowanie flagi za sumatorem / odejmorem i kilkoma multiplekserami. Flaga ujemna jest łatwa, to tylko MSB, podczas gdy flaga zerowa jest droga i zależy od wszystkiego. Teraz, jeśli ograniczysz flagi do instrukcji porównania (i testu bitowego), flagi zerowe można obliczyć z równoległymi XOR na operandach źródłowych, bez oczekiwania na wynik odejmowania. Obliczanie flagi Z po dodaniu jest prawie bezużyteczne.
TEMLIB
7

1 Z perspektywy ISA

  1. Posiadanie instrukcji testowych, które ustawiają tylko flagi, jest tylko sposobem na zmniejszenie ciśnienia rejestrowego w architekturach głodujących rejestr. Jeśli masz wystarczającą liczbę rejestrów, po prostu zmodyfikuj jeden z nich i zignoruj ​​wynik. Sztuczka polegająca na tym, że rejestr 0 z wartością wejściową 0 jest po prostu sztuczką kodującą wygodną, ​​gdy masz wystarczającą liczbę rejestrów, że ustalenie jednego z nich na 0 jest lepsze niż zwiększenie liczby instrukcji. Wygodne jest także użycie go jako celu (zmniejsza liczbę fałszywych zależności).

  2. Kodowanie ponownie. Jeśli kodujesz warunek w skokach, będziesz miał skoki z 3 operandami (dwoma do porównania i celem skoku), z których dwa chcesz być natychmiastowymi wartościami, jednym, który chcesz być tak duży, jak możliwe (skoki mają często swój własny format kodowania, dzięki czemu cel może użyć jak największej liczby bitów). Lub upuszczasz możliwości.

  3. Korzystanie z flag daje więcej możliwości ich ustawienia. Nie tylko operacje porównania mogą ustawiać flagi, ale cokolwiek chcesz. (Z zastrzeżeniem, że im więcej operacji ustawiasz flagi, tym bardziej musisz uważać, aby ostatnia operacja, która ustawiła flagi, była tą, której potrzebujesz). Jeśli masz flagi, jesteś w stanie przetestować liczbę warunków (często 16) pomnożoną przez liczbę instrukcji, które są w stanie ustawić flagi (jeśli nie używasz flag, kończy się tak wiele skoków warunkowych, jak mieć rzeczy do przetestowania lub są rzeczy, których nie pozwala się tak łatwo testować (na przykład nosić lub przepełniać).

2 Z perspektywy implementatora

  1. Testowanie flag jest łatwe i może być wykonane szybko. Im bardziej złożony jest Twój test, tym większy będzie on miał wpływ na czas cyklu (lub strukturę rurociągu, jeśli jesteś w trybie potokowym). Jest to szczególnie prawdziwe w przypadku prostszych implementacji, gdy dojdziesz do wysokiej klasy procesora wykorzystującego wszystkie sztuczki z książki, efekt jest dość minimalny.

  2. Posiadanie flag oznacza, że ​​wiele instrukcji ma wiele wyników (naturalny wynik i każda ze zmodyfikowanych flag). A w przypadku POV z mikroarchitekturą wiele wyników jest złych (musisz śledzić ich powiązanie). Kiedy masz tylko jeden zestaw flag, które wprowadzają zależności (niepotrzebne, jeśli flaga nie jest następnie używana), musisz poradzić sobie w ten czy inny sposób. Ponownie jest to szczególnie prawdziwe w przypadku prostszych implementacji, gdy dojdziesz do wysokiej klasy procesora wykorzystującego wszystkie sztuczki z książki, dodatkowe trudności są zmniejszone przez resztę procesora.

AProgrammer
źródło
2

Na maszynie 32-bitowej instrukcja „add-with-carry” stosowana jako część sekwencji precyzji addycji z wieloma precyzjami musi zaakceptować argumenty o wartości 65 bitów i obliczyć sumę 33 bitów. Specyfikacje rejestru źródłowego określą, skąd powinny pochodzić 64 bity operandu, a specyfikacja rejestru docelowego powie, gdzie powinny pójść dolne 32 bity wyniku, ale co zrobić z operandem „dodaj jeden dodatkowy” lub górnym bitem wyniku? Umożliwienie określenia w ramach instrukcji, skąd powinien pochodzić dodatkowy argument i gdzie powinien przejść dodatkowy bit wyniku, byłoby umiarkowanie użyteczne, ale ogólnie nie byłoby tak przydatne, aby uzasadnić dodatkowe pole w kodzie operacji. Posiadanie stałej „lokalizacji” do obsługi flagi przenoszenia może być nieco niewygodne z perspektywy planowania instrukcji, ale „

Gdyby ktoś próbował zaprojektować zestaw instrukcji, aby umożliwić arytmetykę z wieloma precyzjami, ale każda instrukcja była ograniczona do dwóch 32-bitowych operandów i jednego 32-bitowego operandu docelowego, można zaimplementować 64-bitowe „dodawanie” w czterech instrukcjach: „set r5 do 1, jeśli r0 + r2 niesie lub zero w przeciwnym razie; oblicz r4 = r1 + r3; oblicz r5 = r4 + r5; oblicz r4 = r0 + r2 ", ale przekroczenie tego wymagałoby trzech instrukcji dla każdego dodatkowego słowa. Posiadanie flagi przeniesienia dostępnej jako dodatkowe źródło i miejsce docelowe zmniejsza koszt do jednej instrukcji na słowo.

Zwróć uwagę, że posiadanie bitu instrukcji kontrolującego, czy instrukcja aktualizuje rejestr flagi, może ułatwiać wykonywanie poza kolejnością, ponieważ instrukcje, które używają lub modyfikują bity flagi, muszą utrzymywać swoją sekwencję względem siebie, ale instrukcje, które nie mogą swobodnie się przestawiać. Biorąc pod uwagę sekwencję:

ldr  r0,[r1]
add  r0,r0,r2
eors r4,r5,r6

jednostka wykonawcza mogłaby dość łatwo rozpoznać, że trzecia instrukcja mogła zostać wykonana bez konieczności oczekiwania na odczyt danych [r1], ale gdyby druga instrukcja była adds r0,r0,r2możliwa, byłoby to możliwe tylko wtedy, gdyby jednostka wykonawcza mogła zapewnić, że do tego czasu cokolwiek spróbuje użyć flagi, flaga zerowa utrzyma wartość ustaloną w trzeciej instrukcji, ale flaga przenoszenia zatrzyma wartość w drugiej instrukcji.

supercat
źródło
1
„bit kontroli instrukcji, czy instrukcja aktualizuje rejestr flag”: Dostępne na przykład w PowerPC, SPARC.
TEMLIB
MIPS używa „r5 = r1 + r2; ustaw r6, jeżeli r6 jest mniejsze niż r1; r7 = r3 + r4; r5 = R5 + R6;”. Niektóre rozszerzenia SIMD mogą używać porównań, które ustawiają wszystkie bity na zero lub jeden (tj. Zero lub -1 dwójka uzupełniają liczbę całkowitą), aby znaleźć przeniesienie i odjęcie, aby zastosować przeniesienie.
Paul A. Clayton,
@ PaulA.Clayton: Myślę, że miałeś na myśli „jeśli r5 jest mniejsze niż r1”. Jak MIPS poradzi sobie z dłuższą matematyką? Czy wymagałoby to trzech, więcej niż trzech lub mniej niż trzech instrukcji na słowo?
supercat,
@supercat Tak, powinno to być „ustawienie r6, jeśli r5 jest mniejsze niż r1”!
Paul A. Clayton,
@ PaulA.Clayton: Jak byś poszedł o dodaniu np. Dwóch 64-wyrazowych (2048-bitowych) liczb w 32-bitowym MIPS? Czy jest jakiś skuteczny sposób na obsługę wejść i wyjść ze środkowych etapów?
supercat
0

Prosta odpowiedź ... szybka, tania operacja pamięci, która nie wymaga absolutnie żadnego użycia wewnętrznej magistrali oprócz samej instrukcji. Może być używany jako bool stosu bez stosu lub bitu procesu, bez pamięci.

SkipBerne
źródło
1
Ta odpowiedź jest dość szczegółowa. Długie odpowiedzi niekoniecznie są wymagane, ale coś bardziej rozbudowanego stanowiłoby wyraźną poprawę.
David Richerby
ustawianie flagi lub porównywanie wartości flagi jest pojedynczą instrukcją bez żadnych innych informacji w postaci argumentów, które byłyby zawarte w kodzie asemblera. flagi są również wynikiem działania lub testu uprocesora i można je skutecznie wykorzystać do rozgałęzienia. to rzeczywisty bit, który jest przełączany lub ustawiany, gdy dwie wartości są porównywane w rejestrach.
SkipBerne