我正在尝试将一个值移入寄存器,但是当使用
as -o test.o test.s
组装程序时,我收到消息:
test.s: Assembler messages:
test.s:6: Error: immediate cannot be moved by a single instruction
节目:
.equ SIZE, 2000000000
.global _start
_start:
mov x1, SIZE
uname -a
产生:
Linux ip-172-31-10-254 5.19.0-1025-aws #26~22.04.1-Ubuntu SMP Mon Apr 24 01:58:03 UTC 2023 aarch64 aarch64 aarch64 GNU/Linux
从错误消息和Error:immediate无法移动单个我推断错误消息与值太大而无法存储在寄存器中有关,但是
2000000000
比2^64
小得多。我认为有些东西配置错误。
我该如何解决这个问题?
显然支持杰斯特的评论。
某些 Arm 汇编语言支持 =label 语法,作为将标签地址获取到寄存器中的一种方式,从而允许链接器为您解析它。具有 PC 相对负载(来自池)的最坏情况。
那么立即数=0x1234呢?一些汇编语言支持这种技巧。
Gnu 汇编器支持这一点,如果你使用 ldr 它会优化(正如 Jester 也指出的那样,32 位)。 (一些arm汇编语言仅生成pc相对负载)。
ldr x1,=0
ldr x1,=1
ldr x1,=2
ldr x1,=3
ldr x1,=0x12345678
ldr x1,=0x123456789ABC
Disassembly of section .text:
0000000000000000 <.text>:
0: 580000c1 ldr x1, 18 <.text+0x18>
4: 580000e1 ldr x1, 20 <.text+0x20>
8: 58000101 ldr x1, 28 <.text+0x28>
c: 58000121 ldr x1, 30 <.text+0x30>
10: 58000141 ldr x1, 38 <.text+0x38>
14: 58000161 ldr x1, 40 <.text+0x40>
...
20: 00000001
24: 00000000
28: 00000002
2c: 00000000
30: 00000003
34: 00000000
38: 12345678
3c: 00000000
40: 56789abc
44: 00001234
32位优化
ldr r0,=0
ldr r0,=1
ldr r0,=2
ldr r0,=3
ldr r0,=0x11000000
ldr r0,=0x00110000
ldr r0,=0x00001100
ldr r0,=0x00000011
ldr r0,=0x12345678
00000000 <.text>:
0: e3a00000 mov r0, #0
4: e3a00001 mov r0, #1
8: e3a00002 mov r0, #2
c: e3a00003 mov r0, #3
10: e3a00411 mov r0, #285212672 ; 0x11000000
14: e3a00811 mov r0, #1114112 ; 0x110000
18: e3a00c11 mov r0, #4352 ; 0x1100
1c: e3a00011 mov r0, #17
20: e51f0004 ldr r0, [pc, #-4] ; 24 <.text+0x24>
24: 12345678
我猜测 aarch64 gnu 汇编器将标签视为 64 位,这并不意味着它们无法将其解析为 mov 指令中的立即数。
ldr x1,=0
ldr x1,=1
ldr x1,=2
ldr x1,=3
ldr x1,=0x12345678
mov x1,0
mov x1,1
mov x1,2
mov x1,3
mov w1,0
mov w1,1
mov w1,2
mov w1,3
b .
0000000000000000 <.text>:
0: 580001c1 ldr x1, 38 <.text+0x38>
4: 580001e1 ldr x1, 40 <.text+0x40>
8: 58000201 ldr x1, 48 <.text+0x48>
c: 58000221 ldr x1, 50 <.text+0x50>
10: 58000241 ldr x1, 58 <.text+0x58>
14: d2800001 mov x1, #0x0 // #0
18: d2800021 mov x1, #0x1 // #1
1c: d2800041 mov x1, #0x2 // #2
20: d2800061 mov x1, #0x3 // #3
24: 52800001 mov w1, #0x0 // #0
28: 52800021 mov w1, #0x1 // #1
2c: 52800041 mov w1, #0x2 // #2
30: 52800061 mov w1, #0x3 // #3
34: 14000000 b 34 <.text+0x34>
...
40: 00000001
44: 00000000
48: 00000002
4c: 00000000
50: 00000003
54: 00000000
58: 12345678
5c: 00000000
在指令文档中可以看到有一个16位立即数和一些移位选项。
mov x1,0x1234
mov x1,0xABCD
mov x1,0xABCD0000
mov x1,0xABCD00000000
mov x1,0xABCD000000000000
ldr x1,=2000000000
b .
Disassembly of section .text:
0000000000000000 <.text>:
0: d2824681 mov x1, #0x1234 // #4660
4: d29579a1 mov x1, #0xabcd // #43981
8: d2b579a1 mov x1, #0xabcd0000 // #2882338816
c: d2d579a1 mov x1, #0xabcd00000000 // #188896956645376
10: d2f579a1 mov x1, #0xabcd000000000000 // #-6067193122998190080
14: 58000061 ldr x1, 20 <.text+0x20>
18: 14000000 b 18 <.text+0x18>
1c: 00000000
20: 77359400
24: 00000000
您的值 0x0000000077359400 不符合有效的模式。所以它必须是相对于电脑的负载。
如果您碰巧知道/记住/编写指令集规则的代码,那么您可以使用 mov 来表示简单的值,例如 1,2,3,4...Arm 使用术语别名(其他人可能会说伪指令),但是 mov正如您所看到的,上面的情况实际上是几个不同的指令(movz 表示移位的指令)。
Jester 完全在评论中回答了这个问题,我只是对此进行扩展并调查有关优化的评论。我想看看自己是否缺乏优化。为上面的评论点赞。
我认为根据文档这对您有用,我自己还没有在硬件上尝试过。
.equ SIZE, 2000000000
mov x1, SIZE&0xFFFF0000
movk x1,SIZE&0x0000FFFF
Disassembly of section .text:
0000000000000000 <.text>:
0: d2aee6a1 mov x1, #0x77350000 // #1999962112
4: f2928001 movk x1, #0x9400
这可能比负载快,也可能不快(并且占用的空间更少)。