2010-09-27 34 views
6

Donc, nous étudions l'architecture MIPS à l'école et nous implémentons une architecture MIPS32. Je pensais utiliser GNU cross-binutils en tant qu'assembleur, mais je deviens bizarre en traitant les instructions jal, j et jr. L'assembleur semble insérer les instructions aux mauvais endroits. Je ne sais pas pourquoi cela se produit, et je doute que l'assembleur MIPS soit aussi cassé, alors je suppose que c'est supposé arriver.Comportement étrange de l'assembleur MIPS avec instructions de saut (et de liaison)

Voici mon fichier de montage factice:

.section .text 
.globl __start 

__start: 
    addi $a0, $0, 100 
    addi $a1, $0, 200 
    jal test 

test: 
    add $v0, $a0, $a1 
    jr $ra 

Cependant, quand je démonte je reçois cette sortie:

Disassembly of section .text: 

00000000 <__start>: 
    0: 20040064 addi a0,zero,100 
    4: 0c000003 jal c <test> <--- Why is jal coming before addi? 
    8: 200500c8 addi a1,zero,200 

0000000c <test>: 
    c: 03e00008 jr ra <--- Why is jr coming before add? 
    10: 00851020 add v0,a0,a1 
    ... 

Est-ce une bizarrerie architecturale? Si oui, quelle en est la raison?

EDIT: Testé en ajoutant quelques NOP est juste pour le diable ...

.section .text 
.globl __start 

__start: 
    addi $a0, $0, 100 
    addi $a1, $0, 200 
    nop 
    jal test 

test: 
    add $v0, $a0, $a1 
    nop 
    jr $ra 

et il me donne quelque chose qui semble un peu correct.

Disassembly of section .text: 

00000000 <__start>: 
    0: 20040064 addi a0,zero,100 
    4: 200500c8 addi a1,zero,200 
    8: 0c000004 jal 10 <test> 
    c: 00000000 nop 

00000010 <test>: 
    10: 00851020 add v0,a0,a1 
    14: 03e00008 jr ra 
    18: 00000000 nop 
    1c: 00000000 nop 

Pourquoi jal et j échangent-ils des places avec la dernière instruction?

+0

On dirait un problème de endian à l'intérieur du compilateur (ou le désassembleur), juste sur la couche de commande au lieu de la couche d'octets ... étrange ... – schnaader

Répondre

8

MIPS présente des dangers de pipeline explicites; l'instruction qui suit immédiatement une instruction de branchement ou de saut sera toujours exécutée (cette instruction est parfois appelée "slot de délai de branchement"). Si votre code a été vraiment assemblé exactement comme vous l'avez écrit:

__start: 
    addi $a0, $0, 100 
    addi $a1, $0, 200 
    jal test 

test: 
    add $v0, $a0, $a1 
    jr $ra 

alors l'instruction add serait exécuté deux fois au moment que le jal arrive: une fois dans la fente de retard, et une fois sur le cycle suivant lorsque le programme le contre-changement a effectivement pris effet.

Par défaut, l'assembleur GNU réordonne les instructions pour vous: il est clair que la seconde addi doit toujours être exécutée, il peut donc être échangé avec l'instruction jal, de sorte que les addi se déplace dans la fente de retard. (Dans les cas où l'assembleur ne peut pas en déduire qu'il est sûr de le faire, il insérera un nop dans l'intervalle de délai à la place.)

Si vous ne voulez pas que cela soit fait pour vous, directive

.set noreorder 

en haut de votre fichier source. Vous devez vous occuper des risques vous-même dans ce cas. Si vous faites cela, je recommande d'annoter les créneaux de retard afin qu'ils se détachent - par exemple. en ajoutant un espace supplémentaire (ou deux) d'indentation. Par exemple:

.set noreorder 

__start: 
    addi $a0, $0, 100 
    jal test 
    addi $a1, $0, 200 

test: 
    add $v0, $a0, $a1 
    jr $ra 
    nop 
+0

je vois. Merci. Notre implémentation est à cycle unique, donc je vais utiliser .set noreorder :). – Maister

+0

Comme une note historique, les compilateurs ont traditionnellement juste rempli le slot de retard de branche avec un nop. Je ne suis pas sûr quand les choses ont changé pour "laisser l'assembleur s'inquiéter à ce sujet". –