小明同学正在学习笨叔的《arm64体系结构与编程》,发现简单的mov指令还真不简单。下面两条mov指令,为啥一条可以,另外一条不行呢?
下面这条指令,可以编译通过。
mov x0, 0xffff0000ffff
而这条指令,只是前面少了一个f,为啥就编译不过呢?
mov x0, 0xfff0000ffff
编译出错:
aarch64-linux-gnu-gcc -g -Iinclude -MMD -c src/asm_test.S -o build/asm_test_s.o src/asm_test.S: Assembler messages: src/asm_test.S:326: Error: immediate cannot be moved by a single instruction
小明同学百思不骑姐?
其实mov指令是arm64指令集最简单也是不简单的指令,简单的原因是它可以用来搬移16位的立即数,这是它最原始的设计。但是呢,它也可以搬移超过16位的立即数,只不过,这个操作16位的立即数必须是16位立即数左移0,16,32,48位而成。例如,下面这条指令。
mov,x0, 0x12340000
这条指令是可以编译的,因为他是16位立即数0x1234,左移了16位。
我们可以通过aarch64-linux-gnu-objdump命令来反汇编来查看上面的mov指令究竟是做了那些事情?
aarch64-linux-gnu-objdump -s -d -M no-aliases build/asm_test_s.o
下面是反汇编的代码。
000000000000020c <my_test1>: 20c: d2a24680 movz x0, #0x1234, lsl #16 210: d65f03c0 ret
我们可以看到上面的mov指令等同于一条movz指令,而且是把0x1234这个立即数,左移了16位,得到了0x12340000.
如果我们把上面的mov指令,去掉一个0.
mov,x0, 0x1234000
你会发现编译不过了,编译器告诉你“immediate cannot be moved by a single instruction”。
我们可以看一下armv8.6手册的第C6.2.187章里描述了mov指令,它内部使用movz指令来实现的。
MOV <Xd>, #<imm> 等同于: MOVZ <Xd>, #<imm16>, LSL #<shift>
好,我们来反汇编小明同学遇到的问题。下面是反汇编的结果。
000000000000020c <my_test1>: 20c: b2003fe0 orr x0, xzr, #0xffff0000ffff 210: d65f03c0 ret
从反汇编结果,我们可以知道上述的mov指令等同于一条orr指令。
我们再来看armv8.6手册的第C6.2.188章,讲了mov指令用于bitmask立即数的情况。
MOV <Wd|WSP>, #<imm> 等同于: ORR <Wd|WSP>, WZR, #<imm>
所以,我们可以看到,arm64指令集里的最简单的mov指令,是不是不简单呢?您会用mov指令了吗?笨叔总结一下mov指令的几个使用场景。
-
搬移16位立即数。
-
搬移16位立即数左移16,32,48位后形成的立即数。
-
可以搬移bitmask的立即数到寄存器里。
可能你会问了,如果我的汇编代码里想搬移一个大于16位的立即数,咋办呢?
其实,我们可以使用ldr伪指令。
ldr x0,=0x123456789