2010-09-27 5 views
6

Итак, мы изучаем архитектуру MIPS в школе, и мы внедряем архитектуру MIPS32. Я думал, что буду использовать GNU cross-binutils как ассемблер, но я получаю странный вывод при работе с инструкциями jal, j и jr. Кажется, что ассемблер вставляет инструкции в неправильные места. Я понятия не имею, почему это происходит, и я сомневаюсь, что ассемблер MIPS был бы сломан, поэтому я предполагаю, что это должно произойти.Weird MIPS ассемблер с инструкцией о прыжке (и ссылке)

Вот мой манекен файл сборки:

.section .text 
.globl __start 

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

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

Однако, когда я разборку я получаю этот выход:

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

Является ли это какой-то архитектурный причуда? Если да, то в чем причина этого?

EDIT: испытана добавлением некоторого NOP это только для щеколды ...

.section .text 
.globl __start 

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

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

, и это дает мне то, что кажется несколько правильно.

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 

Почему jal и j заменяют места последней инструкцией?

+0

Похоже на проблемы с обратным порядком байтов внутри компилятора (или дизассемблер), только на командном слое вместо байтов слоя ... странно ... – schnaader

ответ

8

MIPS имеет явные опасности для трубопроводов; всегда будет выполняться инструкция, следующая за инструкцией о ветке или прыжке (эта команда иногда называется слотом задержки ветвления). Если код действительно был собран именно так, как вы написали:

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

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

тогда add инструкция будет выполняться дважды во время, что jal происходит: один раз в слоте задержки, и один раз на следующем цикле, когда программа смена счетчика фактически вступила в силу.

По умолчанию ассемблера GNU реорганизует инструкции для вас: это ясно, что второй addi всегда должен быть выполнен, так что он может быть заменен на jal инструкции, так что addi перемещается в слот задержки. (В тех случаях, когда ассемблер не может сделать вывод, что это безопасно, он будет вставлять nop в слот с задержкой.)

Если вы не хотите, чтобы это переупорядочивание для вас, добавьте Директива

.set noreorder 

В верхней части исходного файла. В этом случае вы должны сами справиться с опасностями. Если вы сделаете это, я рекомендую аннотировать слоты задержки так, чтобы они выделялись - например, добавив дополнительное пространство (или два) отступа. Например:

.set noreorder 

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

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

я вижу. Благодарю. Наша реализация - один цикл, поэтому я буду использовать .set noreorder :). – Maister

+0

В качестве исторической заметки компиляторы традиционно просто заполняли слот задержки ветвления с помощью nop. Я не уверен, когда все изменилось, чтобы «просто позволить ассемблеру беспокоиться об этом». –