2010-09-27 9 views
6

Entonces, estamos estudiando la arquitectura MIPS en la escuela y estamos implementando una arquitectura MIPS32. Pensé que usaría GNU cross-binutils como ensamblador, pero obtengo resultados extraños cuando trato con las instrucciones jal, j y jr. El ensamblador parece insertar las instrucciones en los lugares incorrectos. No tengo idea de por qué sucede esto, y dudo que el ensamblador de MIPS esté tan roto, así que supongo que se supone que sucederá.Comportamiento extraño del ensamblador MIPS con instrucciones de salto (y enlace)

Aquí está mi archivo de ensamblaje dummy:

.section .text 
.globl __start 

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

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

Sin embargo, cuando desmonte consigo esta salida:

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 
    ... 

Es alguna peculiaridad arquitectónica? Si es así, ¿cuál es la razón detrás de esto?

EDIT: Probado la adición de algunos de nop sólo por el gusto ...

.section .text 
.globl __start 

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

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

y me da algo que parece algo correcto.

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 

¿Por qué son jal y j los lugares de intercambio con la última instrucción?

+0

se ve como un problema endian dentro del compilador (o el desensamblador), sólo en la capa de comandos en lugar de la capa de bytes ... extraño ... – schnaader

Respuesta

8

MIPS tiene riesgos explícitos en la tubería; la instrucción que sigue inmediatamente a una instrucción de bifurcación o salto siempre se ejecutará (esta instrucción a veces se denomina "ranura de retardo de bifurcación"). Si el código fue realmente montado exactamente como lo ha escrito:

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

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

entonces la instrucción add sería ejecutado dos veces alrededor del momento en que el jal sucede: una vez en el hueco de retardo, y una vez en el siguiente ciclo cuando el programa el cambio de contador ha tenido efecto.

Por defecto, el ensamblador GNU reordena las instrucciones para usted: está claro que la segunda addi siempre debe ser ejecutado, por lo que se pueden intercambiar con la instrucción jal, de manera que los addi se mueve en la ranura de retraso. (En los casos en que el ensamblador no puede deducir que es seguro hacerlo, insertará un nop en la ranura de retardo en su lugar.)

Si no desea que haga este reordenamiento, agregue el directiva

.set noreorder 

en la parte superior del archivo fuente. Debe lidiar con los riesgos usted mismo en este caso. Si hace esto, le recomiendo anotar las ranuras de demora para que se destaquen, p. agregando un espacio extra (o dos) de sangría. Por ejemplo:

.set noreorder 

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

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

veo. Gracias. Nuestra implementación es de ciclo único, así que usaré .set noreorder :). – Maister

+0

Como nota histórica, los compiladores tradicionalmente llenaban el espacio de retardo de rama con un nop. No estoy seguro de cuándo las cosas han cambiado a "simplemente deja que el ensamblador se preocupe por eso". –

Cuestiones relacionadas