Vous pouvez consulter Wikipedia ou court résumé pour les étudiants . Tout le monde dit qu'il y a deux instructions pour la même chose. Mais personne ne dit pourquoi?
Les succursales permettent des conditions. Mais autoriser des conditions prend plus de bits dans l'instruction. Par conséquent, l'adresse d'une branche n'est que de 2 ^ 16 bits et vous permet uniquement de dériver 2 ^ 15 - 1 instructions vers l'arrière ou 2 ^ 15 instructions vers l'avant.
Un saut est inconditionnel et les bits enregistrés en omettant la condition peuvent être utilisés pour l'adresse. Un saut permet une adresse de 26 bits et peut donc sauter beaucoup plus loin dans le code qu'une branche. Au détriment de ne pas être conditionnel.
Les branches (b
) utilisent un déplacement relatif au PC tandis que les sauts (j
) utilisent des adresses absolues. La distinction est importante pour le code indépendant de la position. De plus, seuls les sauts peuvent être utilisés pour le transfert de contrôle indirect (jr
, en utilisant une valeur de registre).
Comme déjà mentionné, la branche a moins de bits, une plage plus courte et est relative. Jump a plus de bits et est absolu.
Prenez cet exemple
b l0
nop
beq $0,$1,l1
nop
j l2
nop
l0: .Word 0,0
l1: .Word 0,0
l2: .Word 0,0
et vous obtenez ceci
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>:
...
maintenant ce que les autres réponses n'ont peut-être pas mentionné, c'est que le branch inconditionnel est codé, au moins par l'assembleur gnu, comme une branche si égale, avec le même registre. Il n'y a pas de branche inconditionnelle dans les mips, il y a une branche si égale et une branche sinon égale d'après ce que je peux dire.
Vous voyez au-dessus du saut utilise un 0xB qui est l'adresse Word, 0xB * 4 = 0x2C l'adresse de la destination, où les conditions utilisent l'adressage relatif pc + (signe_offset * 4) où le pc = instruction_address + 4; Ou prenez instruction_address + 4 + (signe_offset * 4) pour obtenir l'adresse de destination.
L'utilisation de l'alias b pour la branche au lieu de j pour le saut créera un code indépendant de la position. Le saut ne sera pas obligé de se reconnecter si vous vous déplacez, car les sauts proches valent probablement mieux utiliser la branche au lieu du saut même s'il s'agit d'un alias. Si vous êtes un puriste, vous pouvez utiliser l'instruction réelle beq $ 0, $ 0, label ou choisir n'importe quel registre beq $ 4, $ 4, label. le registre 0 étant spécial et rapide peut être le meilleur choix.
Un saut et une branche inconditionnelle, dans MIPS, ne sont pas les mêmes.
Les instructions de branchement et de saut écrivent toutes les deux des données dans le registre du compteur de programmes de sorte qu'au prochain cycle d'extraction, une instruction différente sera extraite à la place de l'instruction suivante en ligne dans la mémoire du programme. En ce sens, ils effectuent le même type d'opération.
Là où ils diffèrent, c'est que les branches sont conditionnelles, elles ne changent la prochaine instruction à exécuter que si une certaine condition est remplie. Cela peut être illustré par la différence de code d'exécution dans une instruction if
ou en appelant une fonction.
if (a == 0) {
a = 1
}
setAtoOne()
L'instruction if
passe à l'instruction pour définir a = 1
seulement si a == 0
. La fonction passera à cette instruction malgré tout.
Dans ce cas, nous parlons d'une branche où la condition est toujours vraie. C'est juste une autre façon d'écrire
beq $zero, $zero, (int)offset
$ zero est toujours égal à $ zero, donc il se ramifie toujours au décalage spécifié. C'est comme ça si la déclaration
if (true) { a = 1 }
Il existe une autre différence entre les instructions de branchement et de saut. Les instructions de saut spécifient une adresse absolue sur laquelle le PC sera défini, tandis que les instructions de branchement compensent l'adresse dans le compteur de programme.
PC = 32-bit address # Jump
PC += 16-bits lower
En réalité, ce n'est pas strictement vrai. Nous écrivons Assembly avec des adresses absolues et des décalages, mais dans les sauts et les branches, il est compilé en un décalage. C'est pourquoi vous ne pouvez pas sauter ou vous connecter n'importe où dans la mémoire, attendez-vous à utiliser le saut pour enregistrer l'instruction jr
. Cela est dû à une conception fondamentale de MIPS, des instructions d'un mot de longueur fixe.
Toutes les instructions MIPS ont une longueur de 1 mot (soit 4 octets/32 bits). Ils contiennent un identifiant pour l'instruction (appelé op-code) qui est de 6 bits avec d'autres informations nécessaires pour exécuter l'instruction. Il peut s'agir de l'id de registres ou de valeurs "immédiates", essentiellement des entiers codés dans l'instruction.
Chaque octet en mémoire dans MIPS a une adresse entre 0x00000000
- 0xFFFFFFFF
. Pour accéder à l'un de ces octets, nous devons spécifier l'adresse. Si nous avions la chance de stocker l'adresse dans un registre, nous ne ferions que jr
et utiliserions l'adresse déjà enregistrée dans le registre. Mais nous ne le sommes pas.
Cela devient problématique, nous n'avons que 32 bits pour nos instructions et nous aurions besoin de tous ces bits pour spécifier l'adresse dans cette plage. Nous avons également dû abandonner 6 bits pour être utilisés par le processeur pour identifier l'instruction. Il nous reste maintenant 26 bits.
Ce qui est pire, c'est que lorsque nous nous branchons, nous avons besoin de 10 bits supplémentaires pour spécifier les deux registres que nous comparons pour notre condition. La solution est d'utiliser des décalages.
Disons que nous sommes à l'adresse 0x12345678
et nous exécutons un saut inconditionnel à la prochaine adresse en mémoire j 0x1234567c
. Ceci est le code d'assemblage et je vais montrer comment cela est traduit en code machine et exécuté.
D'abord on triche un peu. Nous savons que les instructions sont un mot (4 octets) et dans MIPS, il est spécifié qu'elles doivent être dans la limite de Word. Cela signifie que toutes les instructions ont des adresses distantes de 4 octets et cela signifie qu'elles se terminent toujours par 00 en représentation binaire. Génial, nous pouvons raser ces deux bits sans signification. Nous rasons également les 6 premiers, mais ne vous inquiétez pas, nous les récupérerons plus tard.
jump 0001 0010 0011 0100 0101 0110 0111 1100
jump 00010010 0011 0100 0101 0110 0111 11000000 1000 1000 1101 0001 0101 1001 1111 #in machine code # jump op = 0000 10
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
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
Nous savons prendre le résultat de ceci et OR avec notre instruction entier
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
Lequel est 0x1234567c
dans Hex et où nous voulons aller, maintenant nous sautons là-bas. C'est pourquoi vous ne pouvez pas sauter plus de 256 Mo (2 ^ 28 bits) de votre instruction actuelle (sauf si vous sautez à la valeur d'un registre jr
)
La même idée de base est valable pour les branches, sauf que vous avez également comparé les 2 registres (qui nécessitent 10 bits), vous n'avez donc que 16 bits que vous pouvez utiliser pour compenser, d'où la raison pour laquelle vous ne pouvez pas aller aussi loin avec les branches.
Généralement, c'est très bien car nous utilisons principalement des branches dans une procédure, pour implémenter des boucles et effectuer des affectations conditionnelles.
Tout cela est une conséquence de la conception de l'architecture MIPS. Il aurait été tout à fait possible d'avoir des instructions où la seule différence entre les branches et les sauts aurait été les aspects conditionnels et où une branche "inconditionnelle" se serait comportée de la même manière qu'un saut inconditionnel.