Myślę, że szukam odpowiedzi na pytanie o ciekawostki. Próbuję zrozumieć, dlaczego architektura MIPS używa jawnej wartości „zero” w rejestrze, skoro można osiągnąć to samo, po prostu XOR'ując dowolny rejestr przeciwko sobie. Można powiedzieć, że operacja jest już dla ciebie wykonana; jednak nie mogę sobie wyobrazić sytuacji, w której użyłbyś wielu wartości „zerowych”. Czytam oryginalne prace Hennesseya, a to po prostu przypisuje zero bez faktycznego uzasadnienia.
Czy istnieje logiczny powód, dla którego binarne przypisanie zerowe jest zakodowane na stałe?
aktualizacja: W 8k pliku wykonywalnego z xc32-gcc dla rdzenia MIPS w PIC32MZ mam jedno wystąpienie „zero”.
add t3,t1,zero
faktyczna odpowiedź: Nagrodę przyznałem osobie, która posiadała informacje o MIPS i kodach warunków. Odpowiedź leży w architekturze MIPS dla warunków. Chociaż początkowo nie chciałem na to poświęcać czasu, sprawdziłem architekturę dla Opensparc , MIPS-V i OpenPOWER (ten dokument był wewnętrzny) i oto podsumowanie wyników. Rejestr R0 niezbędny do porównania na oddziałach ze względu na architekturę rurociągu.
- liczba całkowita porównuje z zero i rozgałęzieniem (bgez, bgtz, blez, bltz)
- liczba całkowita porównaj dwa rejestry i gałąź (beq, bne)
- liczba całkowita porównaj dwa rejestry i pułapkę (teq, tge, tlt, tne)
- liczba całkowita porównaj rejestr i natychmiastowa oraz pułapka (teqi, tgei, tlti, tnei)
Po prostu sprowadza się to do wyglądu sprzętu we wdrożeniu. W podręczniku MIPS-V na stronie 68 znajduje się nieograniczony cytat:
Gałęzie warunkowe zostały zaprojektowane w taki sposób, aby zawierały operacje porównania arytmetycznego między dwoma rejestrami (jak również w PA-RISC i Xtensa ISA), zamiast używania kodów warunków (x86, ARM, SPARC, PowerPC), lub aby porównywać tylko jeden rejestr z zerem ( Alpha, MIPS) lub dwa rejestry tylko dla równości (MIPS). Ten projekt był motywowany obserwacją, że połączona instrukcja porównania i rozgałęzienia ts do zwykłego potoku, pozwala uniknąć dodatkowego stanu kodu warunkowego lub użycia rejestru tymczasowego oraz zmniejsza statyczny rozmiar kodu i dynamiczny przebieg pobierania instrukcji. Inną kwestią jest to, że porównania z zerem wymagają nietrywialnego opóźnienia obwodu (szczególnie po przejściu na logikę statyczną w zaawansowanych procesach), a zatem są prawie tak drogie, jak w porównaniu z wielkością arytmetyczną. Kolejną zaletą skondensowanej instrukcji porównania i rozgałęzienia jest to, że gałęzie są obserwowane wcześniej w strumieniu instrukcji frontonu, a zatem można je przewidzieć wcześniej. Być może zaletą jest projekt z kodami warunków w przypadku, gdy wiele gałęzi można pobrać na podstawie tych samych kodów warunków, ale uważamy, że ten przypadek jest stosunkowo rzadki.
Dokument MIPS-V nie trafia w autora cytowanej sekcji. Dziękuję wszystkim za poświęcony czas i uwagę.
źródło
Odpowiedzi:
Rejestr zerowy na procesorach RISC jest użyteczny z dwóch powodów:
Jest to użyteczna stała
W zależności od ograniczeń ISA, nie możesz używać literału w niektórych instrukcjach kodowania, ale możesz być pewien, że możesz go użyć,
r0
aby uzyskać 0.Można go użyć do syntezy innych instrukcji
To chyba najważniejszy punkt. Jako projektant ISA możesz zamienić rejestr ogólnego przeznaczenia na rejestr zerowy, aby móc zsyntetyzować inne przydatne instrukcje. Syntezowanie instrukcji jest dobre, ponieważ mając mniej rzeczywistych instrukcji, potrzebujesz mniej bitów, aby zakodować operację w kodzie op, co zwalnia miejsce w przestrzeni kodowania instrukcji. Możesz użyć tej przestrzeni do np. Większych przesunięć adresu i / lub literałów.
Semantyka rejestru zerowego jest jak
/dev/zero
w systemach * nix: wszystko do niego zapisywane jest odrzucane, a ty zawsze odczytujesz 0.Zobaczmy kilka przykładów, w jaki sposób możemy tworzyć pseudo-instrukcje za pomocą
r0
rejestru zerowego:Przypadek MIPS
Przyjrzałem się bliżej zestawowi instrukcji MIPS. Istnieje garść pseudo-instrukcji, które wykorzystują
$zero
; wykorzystywane są głównie do oddziałów. Oto kilka przykładów tego, co znalazłem:Jeśli chodzi o to, dlaczego znalazłeś tylko jedną instancję
$zero
rejestru w swoim demontażu, być może to twój deasembler jest wystarczająco inteligentny, aby przekształcić znane sekwencje instrukcji w ich równoważne pseudo-instrukcje.Czy rejestr zerowy jest naprawdę przydatny?
Najwyraźniej ARM uważa, że posiadanie rejestru zerowego jest na tyle przydatne, że w jego (nieco) nowym rdzeniu ARMv8-A, który implementuje AArch64, jest teraz rejestr zerowy w trybie 64-bitowym; wcześniej nie było rejestru zerowego. (Rejestr jest jednak trochę wyjątkowy, w niektórych kontekstach kodowania jest rejestrem zerowym, w innych natomiast oznacza wskaźnik stosu )
źródło
slt
,slti
,sltu
).Większość implementacji ARM / POWER / SPARC ma ukryty rejestr RAZ
Można by pomyśleć, że ARM32, SPARC itp. Nie mają rejestru 0, ale w rzeczywistości mają! Na poziomie mikro-architektury większość inżynierów projektujących procesory dodaje rejestr 0, który może być niewidoczny dla oprogramowania (rejestr zerowy ARM jest niewidoczny) i wykorzystuje ten rejestr zerowy do usprawnienia dekodowania instrukcji.
Rozważ typowy współczesny projekt ARM32, który ma niewidoczny programowo rejestr, powiedzmy R16 podłączony do 0. Rozważ obciążenie ARM32, wiele przypadków instrukcji ładowania ARM32 mieści się w jednej z tych form (zignoruj indeksowanie przed postem, aby uprościć dyskusję ) ...
Wewnątrz procesora dekoduje się to do generała
przed wejściem w fazę wydania, w której odczytywane są rejestry. Zauważ, że rx reprezentuje rejestr, aby zapisać zaktualizowany adres. Oto kilka przykładów dekodowania:
Na poziomie obwodu wszystkie trzy obciążenia są w rzeczywistości tą samą instrukcją wewnętrzną, a łatwym sposobem na uzyskanie tego rodzaju ortogonalności jest utworzenie rejestru masy R16. Ponieważ R16 jest zawsze uziemiony, instrukcje te naturalnie dekodują poprawnie bez dodatkowej logiki. Odwzorowanie klasy instrukcji na pojedynczy format wewnętrzny bardzo pomaga w implementacjach superskalarnych, ponieważ zmniejsza złożoność logiki.
Innym powodem jest usprawniony sposób wyrzucania zapisów. Instrukcje można wyłączyć, po prostu ustawiając rejestr docelowy i flagi na R16. Nie ma potrzeby tworzenia żadnego innego sygnału sterującego, aby wyłączyć zapisywanie zwrotne itp.
Większość implementacji procesorów, niezależnie od architektury, kończy się na wczesnym etapie tworzenia modelu rejestru RAZ. Potok MIPS zasadniczo rozpoczyna się w punkcie, który w innych architekturach miałby kilka etapów.
MIPS dokonał właściwego wyboru
Dlatego też rejestr „odczyt zerowy” jest prawie obowiązkowy w każdej nowoczesnej implementacji procesora, a MIPS, dzięki czemu jest widoczny dla oprogramowania, jest zdecydowanie zaletą, biorąc pod uwagę, w jaki sposób usprawnia on wewnętrzną logikę dekodowania. Projektanci procesorów MIPS nie muszą dodawać dodatkowego rejestru RAZ, ponieważ 0 USD już jest na ziemi. Ponieważ RAZ jest dostępny dla asemblera, wiele instrukcji psuedo jest dostępnych dla MIPS i można to traktować jako wypychanie części logiki dekodowania do samego asemblera zamiast tworzenia dedykowanych formatów dla każdego typu instrukcji w celu ukrycia rejestru RAZ przed oprogramowaniem jak w przypadku innych architektur. Rejestr RAZ jest dobrym pomysłem i dlatego ARMv8 go skopiował.
Gdyby ARM32 miał rejestr 0 USD, logika dekodowania stałaby się prostsza, a architektura byłaby znacznie lepsza pod względem prędkości, powierzchni i mocy. Na przykład z trzech przedstawionych powyżej wersji LDR potrzebne byłyby tylko 2 formaty. Podobnie nie ma potrzeby rezerwowania logiki dekodowania dla instrukcji MOV i MVN. Również CMP / CMN / TST / TEQ stałyby się zbędne. Nie byłoby też potrzeby rozróżniania krótkiego (MUL) i długiego mnożenia (UMULL / SMULL), ponieważ krótkie mnożenie można uznać za długie mnożenie z wysokim rejestrem ustawionym na 0 USD itp.
Ponieważ MIPS został początkowo zaprojektowany przez mały zespół, prostota projektowania była ważna, dlatego też 0 USD zostało wyraźnie wybrane w duchu RISC. ARM32 zachowuje wiele tradycyjnych funkcji CISC na poziomie architektonicznym.
źródło
Disclamer: Naprawdę nie znam asemblera MIPS, ale rejestr o wartości 0 nie jest unikalny dla tej architektury i myślę, że jest on używany w taki sam sposób, jak w innych architekturach RISC, które znam.
XOR rejestrowanie rejestru w celu uzyskania 0 kosztuje jedną instrukcję, podczas gdy korzystanie ze wstępnie zdefiniowanego rejestru o wartości 0 nie będzie.
Na przykład
mov RX, RY
instrukcja jest często implementowana jakoadd RX, RY, R0
. Bez rejestru o wartości 0 będziesz musiał zaxor RZ, RZ
każdym razem, gdy chcesz użyćmov
.Innym przykładem jest
cmp
instrukcja i jej warianty (takie jak „porównaj i skacz”, „porównaj i przenieś” itp.), W którychcmp RX, R0
stosuje się testowanie liczb ujemnych.źródło
MOV Rx,Ry
jakoAND Rx,Ry,Ry
?mov RX, Imm
lubmov RX, mem[RY]
jeśli twój zestaw instrukcji obsługuje tylko jedną bezpośrednią wartość i pojedynczy dostęp do pamięci dla instrukcji.mov
jest złym przykładem; możesz zaimplementować go z natychmiastowym 0 zamiast rejestru zerowego. npori dst, src, 0
. Ale tak, potrzebujesz kodu operacji dla mov-instant, aby się zarejestrować, jeśli nie maszaddiu $dst, $zero, 1234
, na przykładlui
dla niższych 16 bitów zamiast dla górnych 16. I nie możesz użyćnor
anisub
zbudować jednego operandu not / neg .Wiązanie kilku prowadzi do ziemi na końcu banku rejestru jest tanie (tańsze niż uczynienie z niego pełnego rejestru).
Wykonanie rzeczywistego Xora wymaga trochę mocy i czasu, aby przełączyć bramę, a następnie zapisać ją w rejestrze, po co płacić ten koszt, gdy istniejąca wartość 0 może być łatwo dostępna.
Nowoczesne procesory mają również (ukryty) rejestr wartości 0, którego mogą używać w wyniku
xor eax eax
instrukcji poprzez zmianę nazwy rejestru.źródło
R0
nie jest uziemienie kilku przewodów, ale fakt, że musisz zarezerwować dla niego kod w każdej instrukcji dotyczącej rejestrów.std::memory_order_consume
) wymagają propagacji zależności przez XOR.lui
ale nie przesunięty w lewo o 16. Więc nadal możesz umieścić małą liczbę w rejestrze za pomocą jednej instrukcji. Dopuszczenie tylko zera z fałszywą zależnością byłoby szalone. (Normalne MIPS tworzy niezerowe wartości za pomocąaddiu $dst, $zero, 1234
lubori
, więc argument „koszt energii” załamuje się. Jeśli chcesz uniknąć uruchamiania ALU, możesz dołączyć kod operacji dla mov-natychmiastowej rejestracji zamiast oprogramowania ADD lub OR natychmiastowe zero.)