2016-05-17 34 views
5

To pytanie jest w kontekście pisania kompilatora C dla 16-bitowego procesora homebrew.Co jest bardziej przydatne na poziomie zespołu, 64 rejestrów lub trzech instrukcji operandu?

Mam 12 bitów operanda dla instrukcji ALU (takich jak ADD, SUB, AND, itp.).

Mogę podać trzy operandy z 16 rejestrów lub dwóch operandów z 64 rejestrów.

np.

SUB A <- B - C (registers r0-r15) 

vs

SUB A <- A - B (registers r0-r63) 

szesnaście rejestry, z instrukcją trzy argumentu, bardziej użyteczne niż 64 rejestrów z instrukcjami dwóch operandów, aby kompilatory C i ich autorów?

+0

Już po raz pierwszy (w pakiecie x86 przykro tylko, że wiem). Większość programów, które wprowadziłem przez takie rzeczy jak IDA, zwykle używa rejestrów Eax przez Edx, więc jest ich 4. Wtedy masz Ebp i Esp, więc 6. Eip nie potrzebuje ALU. Eflags (znowu nie ma potrzeby ALU) ESI i EDI tworzą 8. Tak więc od pierwszej chwili myślę, że większość programów nie używa więcej niż 16 rejestrów. Być może brakuje mi kilku, ale myślę, że dobrym pierwszym testem poczytalności służącym do określenia tego byłoby sprawdzenie, co gcc kompiluje i znajduje, czy używa nawet więcej niż 16 rejestrów w ALU. – arduic

+0

Gdzie zamierzasz zakodować tryb adresowania? –

+1

@WeatherVane To RISC - ładuje i przechowuje jawne operacje z własnym kodem operacyjnym. To homebrew-cpu - jedyne tryby adresowania dla ładunków i sklepów to 8 bitowe natychmiastowe przesunięcia od zera, z komputera lub z innego rejestru. – fadedbee

Odpowiedz

4

16 rejestrów z nieniszczącymi instrukcjami 3-operandowymi jest prawdopodobnie lepsza.

Należy jednak również rozważyć zrobienie czegoś ciekawego z tymi bitami instrukcji. W przypadku homebrew prawdopodobnie nie dbasz o rezerwowanie jakichkolwiek przyszłych rozszerzeń i nie chcesz dodawać mnóstwa dodatkowych opcodes (like PPC does).

ARM przyjmuje interesujące podejście polegające na tym, że jeden operand do każdej instrukcji przechodzi przez the barrel shifter, więc każda instrukcja jest instrukcją "shift-and-whatever" za darmo. Jest to obsługiwane nawet w trybie "kciuka", gdzie najczęściej używane instrukcje to tylko 16 bitów.(W trybie ARM ma tradycyjną RISC 32bit stały rozmiar instrukcji. Dedykuje to 4 z tych bitów opiera wykonania dla każdej instrukcji.)


Pamiętam badanie dotyczące zysków perf od podwojenie liczby rejestrów w sposób teoretyczna architektura, dla SPECinta lub czegoś takiego. 8-> 16 było może 5 lub 10%, 16-> 32 tylko kilka%, a 32-> 64 było jeszcze mniejsze.

Więc 16 rejestrów całkowitych jest "wystarczającą" przez większość czasu, chyba że pracujesz z int32_t dużo, ponieważ każda taka wartość zajmie dwa 16 bitowe rejestry. x86-64 ma tylko 16 rejestrów GP, a większość funkcji może dość dobrze utrzymywać ich stan na żywo w rejestrach. Nawet w pętlach, które wywołują wywołania funkcji, w ABI jest wystarczająco dużo rejestrów zachowujących wywołania, które często się nie pojawiają w pętli.

Zyski w rozmiarze kodu i liczbie instrukcji z 3-operandowych instrukcji będą większe niż przy zapisywaniu sporadycznego rozlewania/ponownego ładowania. Wyjście gcc musi przez cały czas być mov i używać lea jako nieniszczącego add/shift.


Jeśli chcesz zoptymalizować CPU dla oprogramowania potoku ukryć opóźnienia obciążenia pamięci (which is simpler than full out-of-order execution), więcej rejestry są świetne, esp. jeśli nie masz zmiany nazwy rejestru. Jednak nie jestem pewien, jak dobre kompilatory są na static instruction scheduling. Nie jest to już gorący temat, ponieważ wszystkie wydajne procesory są nieczynne. (OTOH, wiele programów, których ludzie faktycznie używają, działa na procesorach ARM w kolejności w smartfonach.) Nie mam doświadczenia, próbując zmusić kompilatory do optymalizacji pod kątem procesorów w kolejności, więc IDK, jak opłacalne jest poleganie na że.

Jeśli twój procesor jest tak prosty, że nie może zrobić nic więcej, gdy ładunek jest w trakcie lotu, to prawdopodobnie nie ma znaczenia. (To się robi naprawdę ręcznie faliste bo nie wiem wystarczająco dużo o co jest praktyczne dla prostej konstrukcji. Nawet „proste” w zamówienie nowoczesne procesory są potokowych.)


64 rejestrów jest miejsce „zbyt wiele "terytorium, gdzie zapisywanie/przywracanie ich zajmuje dużo kodu. Ilość pamięci jest prawdopodobnie nadal pomijalna, ale ponieważ nie możesz zapętlić rejestrów, potrzebujesz 64 instrukcji.


Jeśli projektowaniu ISA od podstaw, spojrzeć Agner Fog's CRISC proposal i wynikającą z dyskusji. Twoje cele są bardzo różne (wysoka wydajność/energooszczędny 64-bitowy procesor w porównaniu z prostym 16-bitowym), więc twoje ISA będą oczywiście bardzo różne. Jednak dyskusja może sprawić, że pomyślisz o rzeczach, których nie rozważałeś lub o pomysłach, które chcesz wypróbować.

+0

Bardzo interesujący jest fakt, że mgła oddaje swoją wiedzę do koncepcji architektonicznej. Bądź miły, jeśli potrafi sformalizować go do punktu, w którym można zrealizować symulatory, np. MMIX Knutha. Wraz z rejestrami cache/debug/fault itp. Wydaje się, że brakuje w nim ostatecznego dokumentu ... –

+0

@BrettHale: Nie przeglądałem bieżącej wersji wniosku. Jednym z najnowszych postów w wątku dyskusji było to, że Agner pracuje nad wsparciem dla asemblera i symulatora tego i podobnych rzeczy, ale nie ma zbyt wiele czasu, aby poświęcić się tej pracy. x86 może nie trwać wiecznie i byłoby naprawdę potrzebne, gdyby przejęła architektura "open source" z wektorami zaprojektowanymi od samego początku. –

2

Jeśli chodzi o liczbę rejestrów, generalnie myślę, że większość C może się skompilować do dobrze wydajnego kodu maszynowego, gdy dostępnych jest tylko 16 rejestrów ogólnego przeznaczenia (takich jak AMD64). Jednak może być korzystne posiadanie kilku rejestrów dedykowanych dla argumentów funkcji i niektórych oznaczonych jako zmienne - co oznacza, że ​​mogą być używane wewnątrz dowolnej funkcji, ale mogą zostać przepełnione przez dowolną wywołaną funkcję. Zwiększenie do 32 rejestrów może być korzystne, ale wątpię, aby wiele się poprawiło, gdybyś miał 64 rejestry ogólnego przeznaczenia dla zwykłego 16-bitowego procesora. Będziesz musiał jednak zachować oryginalną zawartość większości rejestrów, które będziesz używał w swojej funkcji C, do stosu. Ograniczenie funkcji do używania tylko 7 rejestrów jednocześnie (zamiast 37) może nadal być bardziej wydajne (stos) dla kompilatora C, nawet jeśli dostępnych jest znacznie więcej rejestrów.

Wiele zależy od C calling convention, której będziesz używać. Które rejestry mają być używane do przekazywania wartości od osoby dzwoniącej do kanclerza, które rejestry należy uważać za niestabilne, jaki jest koszt pchania/wyskakiwania ze stosu itp. Możesz wygrać więcej, korzystając z Register Window do zarządzania rejestrami i używanie stosu przez wywołania funkcji. Na przykład Sun Sparc ma okno rejestru 8 całkowicie "lokalnych" rejestrów, 8 rejestrów, które są współdzielone z wywołującym i 8 rejestrów, które będą współdzielone z każdą funkcją wywołującą. (Ponadto można zaadresować 8 globalnych rejestrów.) W ten sposób nie musisz się martwić o wypychanie do stosu, zawsze będzie pojedyncze naciśnięcie 16 rejestrów dla każdego wywołania funkcji jednocześnie do zmiany wskaźnika wykonawczego i 16 Zarejestruj pop za każdy zwrot. Intel ia64 ma coś podobnego, ale z konfigurowalnym rozmiarem okna rejestru.

Jednak tylko SUB C,A,B ma tylko niewielką przewagę nad SUB A,B, gdy zachowywanie wyników pośrednich jest bardzo ważne (trzeba go często konserwować), a prosty rejestr w celu zarejestrowania kopii jest znacznie droższy. Wydaje się to mało prawdopodobne w większości przypadków.

Czy będziesz używać osobnych rejestrów pływających lub stałych punktów?