Możesz zajrzeć do Wikipedia lub short summary for students. Wszyscy mówią, że istnieją dwie instrukcje dotyczące tej samej rzeczy. Ale nikt nie mówi dlaczego?Jaka jest różnica między bezwarunkowym odgałęzieniem a bezwarunkowym skokiem (instrukcje w MIPS)?
Odpowiedz
Odgałęzienia pozwalają na warunki. Ale dopuszczenie warunków zajmuje więcej bitów w instrukcji. Dlatego adres oddziału jest tylko 2^16 bitów i pozwala tylko rozgałęzić instrukcje 2^15 - 1 do tyłu lub 2^15 do przodu.
Skok jest bezwarunkowy, a bity zapisane przez pomijanie warunku mogą być użyte jako adres. Skok pozwala na 26-bitowy adres, a więc może skakać znacznie dalej w kodzie niż gałąź. Kosztem braku warunkowania.
Uwaga, może to być prawda w przypadku MIPS, ale nie w przypadku wszystkich mnemoników procesora. Z80 ma względne skoki i skoki warunkowe oraz warunkowe skoki względne. Gałąź to słowo nieistniejące w montażu Z80. – TheBlastOne
Więc, jeśli nie jestem zainteresowany stanem, skok jest zawsze lepszy? –
@HelloWorld Zazwyczaj? Tak. Ale zawsze? Nie. Jedna sytuacja, o której mogę myśleć to urządzenie z bardzo małą pamięcią. W takim przypadku nie musiałbyś daleko iść i chciałbyś zaoszczędzić jak najwięcej bitów. Więc bezwarunkowa gałąź byłaby bardziej odpowiednia. –
Odgałęzienia (b
) używają przemieszczenia względnego komputera podczas skoków (j
) używają adresów bezwzględnych. Rozróżnienie jest ważne dla kodu niezależnego od pozycji. Ponadto do przeskoków pośrednich można użyć tylko skoków (jr
, używając wartości rejestru).
Dokładniej, wszystkie instrukcje skoku i rozgałęzienia oprócz 'jr' używają przesunięć w kodzie maszynowym. – CervEd
Jak już wspomniano, oddział ma mniejszą liczbę bitów, krótszy zakres i jest względny. Skok ma więcej bitów i jest absolutny.
Weźmy ten przykład
b l0
nop
beq $0,$1,l1
nop
j l2
nop
l0: .word 0,0
l1: .word 0,0
l2: .word 0,0
i masz to
00000000 <l0-0x1c>:
0: 10000006 b 1c <l0>
4: 00000000 nop
8: 10010006 beq zero,at,24 <l1>
c: 00000000 nop
10: 0800000b j 2c <l2>
14: 00000000 nop
18: 00000000 nop
0000001c <l0>:
...
00000024 <l1>:
...
0000002c <l2>:
...
teraz co inne odpowiedzi mogą nie wspomnieli, że bezwarunkowe oddział jest kodowana, przynajmniej przez gnu asemblerze, jako gałąź, jeśli jest równa, z tym samym rejestrem. Nie ma bezwarunkowej gałęzi w mipsach, tam jest gałąź, jeśli jest równa, i gałąź, jeśli nie równa się temu, co mogę powiedzieć.
Widzisz powyżej skoku używa 0xB, który jest adresem słowa, 0xB * 4 = 0x2C adres miejsca przeznaczenia, gdzie warunki warunkowe używają względnego adresowania pc + (signed_offset * 4), gdzie pc = adres_podręcznika + 4; Lub weź adres_adresu + 4 + (signed_offset * 4), aby uzyskać adres docelowy.
Użycie aliasu b dla oddziału zamiast j dla przeskoku tworzy kod niezależny od pozycji. Skok nie będzie musiał ponownie łączyć, jeśli się poruszasz, bo w pobliżu skoków prawdopodobnie lepiej używać gałęzi zamiast skoku, mimo że jest to alias. Jeśli jesteś purystą, możesz użyć prawdziwej instrukcji beq $ 0, $ 0, label lub wybrać dowolny rejestr beq 4, 4 $, label. rejestracja 0 jest wyjątkowa i szybka może być lepszym wyborem.
Dzięki. Wiemy, że oddział jest pseudo instrukcją. Podoba mi się twoja próba wskazówka, że przy korzystaniu z jednej instrukcji można preferować twoją próbę. Ale myślę, że szybkość dostępu jest równa dla wszystkich rejestrów (cos CPU jest synchroniczna) – Val
może to spowodować przeciągnięcie lub zablokowanie dodać r1, r2, coś; beq r1, r1, gdzieś może działać inaczej, ponieważ może lub nie musi czekać na wcześniejsze instrukcje, aby zakończyć wykonywanie i zapis, niż dodać r1, r2, coś; beq r0, r0, gdzieś jako r0 nigdy nie jest napisane. Tylko zależy od rdzenia itd. Odosobniony tak, że jeden rejestr jest tak dobry jak inny dla celów prędkości odczytu. –
Skok i bezwarunkowa gałąź w MIPS to nie to samo.
Zarówno instrukcje rozgałęzień jak i skoku zapisują dane w rejestrze licznika programów, tak aby przy następnym cyklu pobierania pobrana została inna instrukcja zamiast następnej instrukcji w wierszu pamięci programu. W tym sensie przeprowadzają ten sam rodzaj operacji.
Tam, gdzie są one różne, gałęzie są warunkowe, zmieniają tylko następną instrukcję do wykonania, jeśli spełniony jest określony warunek. Można to zilustrować różnicą w kodzie wykonawczym w instrukcji if
lub przez wywołanie funkcji.
if (a == 0) {
a = 1
}
setAtoOne()
Oświadczenie if
skacze z instrukcją aby ustawić a = 1
tylko jeśli a = 0
. Funkcja przeskoczy do tej instrukcji niezależnie.
W tym przypadku mówimy o oddziale, w którym warunek jest zawsze prawdziwy. Jest to po prostu inny sposób pisania: 0 zero jest zawsze równe zero, więc zawsze rozgałęzia się do określonego przesunięcia. To jest tak, jeśli instrukcja
Istnieje inna różnica między instrukcjami rozgałęzienia i skoku. Instrukcje skoku określają adres bezwzględny, do którego zostanie ustawiony komputer, podczas gdy instrukcje rozgałęzione kompensują adres w liczniku programu.
PC = 32-bit address # Jump
PC += 16-bits lower
W rzeczywistości nie jest to ściśle prawda. Piszemy zespół z adresami bezwzględnymi i przesunięciami, ale w obu skokach i gałęziach jest on kompilowany do przesunięcia. Dlatego nie można przeskoczyć ani rozgałęzić się do dowolnego miejsca w pamięci, należy oczekiwać, że za pomocą skoku zostanie zarejestrowana instrukcja jr
. Wynika to z fundamentalnego projektu MIPS, instrukcji o stałej długości w jednym słowie.
Wszystkie instrukcje MIPS mają długość 1 słowa (tj. 4 bajty/32 bity). Zawierają one id do instrukcji (zwany op-code), który jest 6 bitów wraz z innymi informacjami potrzebnymi do wykonania instrukcji. Może to być identyfikator rejestrów lub "bezpośrednich" wartości, w zasadzie liczb całkowitych zakodowanych w instrukcji.
Każdy bajt w pamięci w MIPS ma adres między 0x00000000
- 0xFFFFFFFF
. Aby dostać się do jednego z tych bajtów, musimy podać adres. Jeśli mielibyśmy szczęście, że zapisaliśmy adres w rejestrze, wystarczyłoby tylko jr
i użyć adresu już zapisanego w rejestrze. Jednak nie jesteśmy.
To staje się problematyczne, mamy tylko 32 bity dla naszych instrukcji i potrzebowalibyśmy wszystkich tych bitów, aby określić adres w tym zakresie. Musieliśmy także zrezygnować z 6 bitów, które procesor użyje do zidentyfikowania instrukcji. Teraz pozostaje nam 26 bitów.
Co gorsze, kiedy rozgałęziamy się, potrzebujemy 10 dodatkowych bitów, aby określić dwa rejestry, które porównujemy dla naszego stanu. Rozwiązaniem jest użycie przesunięć.
Załóżmy, że jesteśmy pod adresem 0x12345678
i wykonujemy bezwarunkowy skok do następnego adresu w pamięci j 0x1234567c
. To jest kod zespołu, a ja pokażę, jak zostanie to przetłumaczone na kod maszynowy i wykonane.
Najpierw trochę oszukujemy. Wiemy, że instrukcje są jednym słowem (4 bajty), aw MIPS określono, że muszą znajdować się w granicach słowa. Oznacza to, że wszystkie instrukcje mają adresy oddalone o 4 bajty, co oznacza, że zawsze kończą się na 00 w postaci binarnej. Świetnie, możemy zgolić te dwa nic nie znaczące kawałki. Ogoliliśmy także pierwszych 6, ale nie martw się, odzyskamy je później.
jump 0001 0010 0011 0100 0101 0110 0111 1100
jump 00010010 0011 0100 0101 0110 0111 1100
0000 1000 1000 1101 0001 0101 1001 1111 #in machine code # jump op = 0000 10
When we execute this we take
00 1000 1101 0001 0101 1001 1111
0000 0000 1000 1101 0001 0101 1001 1111 # extend >> 6
0000 0010 0011 0100 0101 0110 0111 1100 # << 2
Then we AND the PC (where we're executing from) and 0xf0000000
0001 0010 0011 0100 0101 0110 0111 1000
1111 0000 0000 0000 0000 0000 0000 0000
AND
0001 0000 0000 0000 0000 0000 0000 0000
We know take the result of this and OR it with our instruction integer
0001 0000 0000 0000 0000 0000 0000 0000
0000 0010 0011 0100 0101 0110 0111 1100
OR
0001 0010 0011 0100 0101 0110 0111 1100
Which is 0x1234567c
w Hex i gdzie chcemy iść, teraz skaczemy tam. Dlatego nie można przeskoczyć dalej niż 256 MB (2^28 bitów) od aktualnej instrukcji (chyba że przeskoczysz do wartości rejestru jr
)
Ta sama podstawowa idea dotyczy oddziałów, z wyjątkiem tego, że teraz także mają porównywane 2 rejestry (które wymagają 10 bitów), więc masz tylko 16 bitów, których możesz użyć do przesunięcia, dlatego nie możesz przeskoczyć do gałęzi.
Ogólnie rzecz biorąc, jest to w porządku, ponieważ najczęściej używamy gałęzi w ramach procedury, aby zaimplementować pętle i wykonać przypisanie warunkowe.
Wszystko to jest konsekwencją projektu architektury MIPS. Byłoby zupełnie możliwe, aby mieć instrukcje, w których jedyną różnicą między gałęziami i skokami byłyby aspekty warunkowe i gdzie "bezwarunkowa" gałąź zachowałaby się tak samo jak bezwarunkowy skok.
pierwsza różnica to format: jeden to typ-R, a drugi to typ-J –