为何mipsel-linux-gnu-as未将sw指令填入跳转延迟槽?
MIPS延迟槽优化问题解析
环境信息
使用的编译器版本:
mipsel-linux-gnu-gcc (GCC) 12.2.0 Copyright (C) 2022 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
汇编源代码
.global __start __start: lw $10,array lw $11,array+4 add $12, $10, $11 sw $12,array+8 j __start array: .word 1 .word 2 .word 0
汇编与链接命令
mipsel-linux-gnu-as -mips2 asm/start.asm -o obj/start.asm.o mipsel-linux-gnu-ld -o final obj/start.asm.o -Tlinker_script.ld
链接脚本内容
MEMORY { SRAM (rwx) : ORIGIN = 0x100000, LENGTH = 0x60000 } SECTIONS { .text : { *(.text) } > SRAM .data : { *(.data) } > SRAM .bss : { *(.data) } > SRAM }
.text段反汇编结果
mipsel-linux-gnu-objdump -j .text -D final final: file format elf32-tradlittlemips Disassembly of section .text: 00100000 <__start>: 100000: 3c0a0010 lui t2,0x10 100004: 8d4a0024 lw t2,36(t2) 100008: 3c0b0010 lui t3,0x10 10000c: 8d6b0028 lw t3,40(t3) 100010: 014b6020 add t4,t2,t3 100014: 3c010010 lui at,0x10 100018: ac2c002c sw t4,44(at) 10001c: 08040000 j 100000 <__start> 100020: 00000000 nop 00100024 <array>: 100024: 00000001 0x1 100028: 00000002 srl zero,zero,0x0 10002c: 00000000 nop
问题解答
你的理解完全正确,sw指令确实可以放到j的延迟槽中。汇编器填充nop不是伪指令的限制,而是因为mipsel-linux-gnu-as(GNU汇编器)的默认行为——它只做最基础的汇编工作,不会自动调整指令顺序做延迟槽优化。
GNU汇编器是"直通式"工具,严格按照你写的指令顺序生成机器码,只会在必须填充的时候(比如跳转指令的延迟槽)放nop,不会主动帮你调度指令。要实现延迟槽优化,有两种办法:
- 手动调整代码顺序:把
sw放到j指令的后面(也就是延迟槽的位置),代码改成这样:
这样汇编器就会把.global __start __start: lw $10,array lw $11,array+4 add $12, $10, $11 j __start sw $12,array+8 # 这行作为延迟槽指令 array: .word 1 .word 2 .word 0sw当作延迟槽指令执行,不会再填充nop。 - 用gcc带优化编译:如果是写C代码,直接用
gcc -O2编译,编译器会自动做指令调度优化延迟槽;如果是写汇编,用gcc -c -O2编译汇编文件,gcc的汇编前端会帮你完成指令调度。
另外需要确认:这个场景下把sw放到延迟槽是安全的——MIPS延迟槽指令在跳转指令取出时就会执行,而sw只依赖add的结果,和跳转目标没有任何依赖关系,不会产生错误。
内容的提问来源于stack exchange,提问作者Cheiron
相关产品推荐
相关产品推荐

