我为amd64编写了这个小程序集文件。代码的作用对于这个问题并不重要。
.globl fib
fib: mov %edi,%ecx
xor %eax,%eax
jrcxz 1f
lea 1(%rax),%ebx
0: add %rbx,%rax
xchg %rax,%rbx
loop 0b
1: ret
然后我继续组装,然后在Solaris和Linux上反汇编。
$ as -o y.o -xarch=amd64 -V y.s
as: Sun Compiler Common 12.1 SunOS_i386 Patch 141858-04 2009/12/08
$ dis y.o
disassembly for y.o
section .text
0x0: 8b cf movl %edi,%ecx
0x2: 33 c0 xorl %eax,%eax
0x4: e3 0a jcxz +0xa <0x10>
0x6: 8d 58 01 leal 0x1(%rax),%ebx
0x9: 48 03 c3 addq %rbx,%rax
0xc: 48 93 xchgq %rbx,%rax
0xe: e2 f9 loop -0x7 <0x9>
0x10: c3 ret
$ as --64 -o y.o -V y.s
GNU assembler version 2.22.90 (x86_64-linux-gnu) using BFD version (GNU Binutils for Ubuntu) 2.22.90.20120924
$ objdump -d y.o
y.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <fib>:
0: 89 f9 mov %edi,%ecx
2: 31 c0 xor %eax,%eax
4: e3 0a jrcxz 10 <fib+0x10>
6: 8d 58 01 lea 0x1(%rax),%ebx
9: 48 01 d8 add %rbx,%rax
c: 48 93 xchg %rax,%rbx
e: e2 f9 loop 9 <fib+0x9>
10: c3 retq
生成的机器代码是如何不同的? Sun为8b cf
生成mov %edi,%ecx
,而气体为同一指令生成89 f9
。这是因为在x86下编码相同指令的各种方法还是这两种编码真的有区别吗?
答案 0 :(得分:6)
某些x86指令具有多个执行相同操作的编码。特别是,任何作用于两个寄存器的指令都可以交换寄存器,并反转指令中的方向位。
给定的汇编器/编译器选择哪一个只取决于工具作者选择的内容。
答案 1 :(得分:1)
您未指定mov
,xor
和add
操作的操作数大小。这造成了一些歧义。 GNU汇编程序手册i386 Mnemonics提到了这一点:
如果指令没有指定后缀,则尝试根据目标寄存器操作数(按惯例最后一个)填写缺少的后缀。 [...] 请注意,这与AT&amp; T Unix汇编程序不兼容,它假定缺少助记符后缀意味着长操作数大小。
这意味着GNU汇编器选择不同 - 它将选择具有指定目标操作数的R / M字节的操作码(因为目标大小是已知/隐含的),而AT&amp; T选择其中R / M的操作码M字节指定源操作数(因为暗示了操作数大小)。
我已经完成了那个实验并且在汇编源中给出了明确的操作数大小,并且它不会改变 GNU汇编器输出。但是,上面的文档的另一部分是
可以通过可选的助记符后缀指定不同的编码选项。当从一个寄存器移动到另一个寄存器时,`.s'后缀在编码时交换2个寄存器操作数。
哪一个可以使用;使用GNU as
的以下源代码创建了我从Solaris as
获得的操作码:
.globl fib
fib: movl.s %edi,%ecx
xorl.s %eax,%eax
jrcxz 1f
leal 1(%rax),%ebx
0: addq.s %rbx,%rax
xchgq %rax,%rbx
loop 0b
1: ret