2010-09-27 29 views
6

Vì vậy, chúng tôi đang nghiên cứu kiến ​​trúc MIPS ở trường và chúng tôi đang triển khai kiến ​​trúc MIPS32. Tôi nghĩ rằng tôi muốn sử dụng GNU cross-binutils như assembler nhưng tôi nhận được sản lượng lạ khi giao dịch với các hướng dẫn jal, j và jr. Người lắp ráp dường như chèn các chỉ dẫn ở những nơi sai. Tôi không biết tại sao điều này lại xảy ra, và tôi nghi ngờ rằng người lắp ráp MIPS sẽ bị hỏng, vì vậy tôi cho rằng điều này được cho là xảy ra.Hành vi lắp ráp MIPS lạ với lệnh nhảy (và liên kết)

Đây là tập tin lắp ráp hình nộm của tôi:

.section .text 
.globl __start 

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

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

Tuy nhiên, khi tôi tháo rời tôi nhận được kết quả này:

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

Đây có phải là một số những đứa kiến ​​trúc? Nếu vậy, lý do đằng sau điều này là gì?

EDIT: Tested thêm một số của nop chỉ dành riêng cho các heck ...

.section .text 
.globl __start 

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

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

và nó mang lại cho tôi một cái gì đó có vẻ hơi đúng.

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 

Tại sao jal và j đổi địa điểm với hướng dẫn cuối cùng?

+0

Trông giống như một vấn đề cuối cùng bên trong trình biên dịch (hoặc trình tháo rời), chỉ trên lớp lệnh thay vì lớp byte ... lạ ... – schnaader

Trả lời

8

MIPS có mối nguy hiểm đường ống rõ ràng; lệnh ngay sau lệnh rẽ nhánh hoặc lệnh nhảy sẽ luôn được thực hiện (lệnh này đôi khi được gọi là "khe trễ nhánh"). Nếu mã của bạn đã thực sự lắp ráp chính xác như bạn đã viết nó:

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

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

thì hướng dẫn add sẽ được thực hiện hai lần trong khoảng thời gian mà các jal xảy ra: một lần trong khe chậm trễ, và một lần vào chu kỳ sau khi chương trình thay đổi truy cập đã thực sự có hiệu lực. Theo mặc định, trình biên tập GNU sắp xếp lại các chỉ dẫn cho bạn: rõ ràng là addi thứ hai phải luôn được thực thi, vì vậy nó có thể được hoán đổi với lệnh jal, để di chuyển addi vào khe trễ. (Trong trường hợp người lắp ráp không thể suy ra rằng nó là an toàn để làm điều này, nó sẽ chèn một nop vào khe trễ thay thế.)

Nếu bạn không muốn nó làm điều này sắp xếp lại cho bạn, thêm chỉ thị

.set noreorder 

ở đầu tệp nguồn của bạn. Bạn phải đối phó với các mối nguy hiểm cho mình trong trường hợp này. Nếu bạn làm điều này, tôi khuyên bạn nên chú thích các vị trí trì hoãn để chúng nổi bật - ví dụ: bằng cách thêm một khoảng trống thừa (hoặc hai) của thụt đầu dòng. Ví dụ:

.set noreorder 

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

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

Tôi hiểu. Cảm ơn. Việc triển khai của chúng tôi là một chu kỳ, vì vậy tôi sẽ sử dụng .set noreorder :). – Maister

+0

Là một lưu ý lịch sử, các trình biên dịch theo truyền thống chỉ cần lấp đầy khe trễ nhánh với một nop. Tôi không chắc khi mọi thứ đã thay đổi thành "chỉ để cho người lắp ráp lo lắng về nó". –