为什么我不能将 #1001 移动到 arm 上的 r5 中?

Why can't I move #1001 into r5 on arm?

我有一个 RPi4,我正在尝试用汇编编写一些代码来循环 1000 次。当我设置较少的循环次数时代码工作正常,但是当我尝试将它设置为 1001 时,gcc 说:

loop.s: Assembler messages:
loop.s:15: Error: invalid constant (3e9) after fixup

代码如下:

.data
ms3: .asciz "%d\n"
.text
.global main
.extern printf
main:
    push {ip, lr}
    mov r1, #0
    mov r5, #1001

loop1000:
    push {r1}
    ldr r0, =ms3
    bl printf
    pop {r1}
    add r1, #1
    cmp r1, r5
    bne loop1000
    pop {ip, pc}

汇编语言是由工具而非目标定义的,因此解决方案和指令的确切语法各不相同。你提到 gcc 这意味着 gnu 汇编程序,尽管 gcc 被提供汇编语言是另一种 gnu arm 汇编语言

对于 gnu 汇编程序,ldr = 伪指令将尝试使用最佳指令(如果可以的话),否则它将执行与 pc 相关的加载。如果你想要完全控制那么只使用 ldr = 标签的东西(显然它的初衷)

.cpu arm7tdmi
ldr r5,=1001
ldr r5,=0x00990000
ldr r5,=0x00990099
ldr r5,=0x90000009


.thumb
.cpu cortex-m0
ldr r5,=1001

.cpu cortex-m3
ldr r5,=1001
movw r5,#1001
ldr r5,=0x00990099
.align

Disassembly of section .text:

00000000 <.text>:
   0:   e59f5018    ldr r5, [pc, #24]   ; 20 <.text+0x20>
   4:   e3a05899    mov r5, #10027008   ; 0x990000
   8:   e59f5014    ldr r5, [pc, #20]   ; 24 <.text+0x24>
   c:   e3a05299    mov r5, #-1879048183    ; 0x90000009
  10:   4d03        ldr r5, [pc, #12]   ; (20 <.text+0x20>)
  12:   f240 35e9   movw    r5, #1001   ; 0x3e9
  16:   f240 35e9   movw    r5, #1001   ; 0x3e9
  1a:   f04f 1599   mov.w   r5, #10027161   ; 0x990099
  1e:   bf00        nop
  20:   000003e9    andeq   r0, r0, r9, ror #7
  24:   00990099    umullseq    r0, r9, r9, r0

从你的问题中间开始。

  10:   4d03        ldr r5, [pc, #12]   ; (20 <.text+0x20>)

1001 (0x3e9) 不适合 mov 立即数 thumb 指令的 8 位立即数,无循环。所以使用 ldr = 汇编程序创建了一个 pc 相对负载,它有利有弊。

thumb2 扩展仅在某些支持较大立即数的处理器上可用

  12:   f240 35e9   movw    r5, #1001   ; 0x3e9

它甚至可以做这样奇怪的事情。

  1a:   f04f 1599   mov.w   r5, #10027161   ; 0x990099

ldr = 和直接使用 movw 都产生了相同的指令(如预期的那样)。

  12:   f240 35e9   movw    r5, #1001   ; 0x3e9
  16:   f240 35e9   movw    r5, #1001   ; 0x3e9

评论中有些混乱(每个人都需要阅读文档,而不仅仅是 OP)

   0:   e59f5018    ldr r5, [pc, #24]   ; 20 <.text+0x20>
   4:   e3a05899    mov r5, #10027008   ; 0x990000
   8:   e59f5014    ldr r5, [pc, #20]   ; 24 <.text+0x24>
   c:   e3a05299    mov r5, #-1879048183    ; 0x90000009

arm 模式不能执行 0x00990099 操作,但它可以执行在偶数边界 0x00990000 和 0x90000009 上旋转的 8 个非零位,但不能执行 0x000001FE、0x102 等。

arm 使用 32 位指令,并且像 mips 和其他指令一样,在可能的立即数位方面受到限制,同时由于缺少更好的术语而为操作码留出空间。 thumb 是 16 位的,因此可立即使用的空间要少得多。 thumb2 扩展添加了额外的指令,这些指令需要 2x16 位,但通常不能使用 arm 编码,但由于某种原因没有使用你在 arm 指令中看到的相同的立即方案,所以你有这个反射和移位的东西,而不仅仅是一个移位

所有这些都在使用 writing/learning 汇编语言时应该放在身边的 arm 文档中。

汇编语言是由工具(汇编器)而不是目标定义的,因此 gnu 汇编器和 kiel 以及 ARMasm 和其他汇编器应该有不同的汇编语言(主要是在非指令领域)并且它们确实如此。对于任何其他目标(x86、mips 等),这是一条通用规则,通常没有标准化的汇编语言,当然对于主线指令集也不是。

说 gnu 汇编程序的 ldr rx,=label/address 技巧产生了最佳指令,但它的伪代码不是真正的指令,因此预计在某些系统上根本不支持它汇编程序和一些支持它的程序可能会从字面上实现 pc 相对负载而不是优化(在可能具有命令行选项以 enable/disable 优化的可能性范围内)。

你是为 thumb 构建的,而对于 thumb,你仅限于一个未移位的 8 位立即数。如果您的 cpu 碰巧也支持 thumb2,那么您可以告诉汇编程序该命令行或代码,它会生成优化的指令 and/or 您可以直接指定指令。如果不支持 thumb2 那么你可以直接制作一个 pc 相对负载

mov r5,hello
...
hello: .word 1001

或者使用 ldr 等于东西,或者使用多条指令 3 左移 8 orred 0xE9,诸如此类。

编辑

只为杰克...

.thumb

.cpu cortex-m0
ldr r5,=1001

.cpu cortex-m3
ldr r5,=1001

.align

arm-none-eabi-as --version
GNU assembler (GNU Binutils) 2.34
Copyright (C) 2020 Free Software Foundation, Inc.
This program is free software; you may redistribute it under the terms of
the GNU General Public License version 3 or later.
This program has absolutely no warranty.
This assembler was configured for a target of `arm-none-eabi'.

00000000 <.text>:
   0:   4d01        ldr r5, [pc, #4]    ; (8 <.text+0x8>)
   2:   f240 35e9   movw    r5, #1001   ; 0x3e9
   6:   bf00        nop
   8:   000003e9    andeq   r0, r0, r9, ror #7

对于 armv6m(以及 armv4t、armv5t、armv6、当前的 armv8ms),您不能使用 movw,这是 OP 错误消息所暗示的。

对于 armv7、armv7m,您可以,并且 ldr 指令会生成它,而不必根据您选择的立即数不断更改代码,如果您使用 gnu 汇编程序,那么 ldr equals 是最好的方法。

arm-none-eabi-gcc --version
arm-none-eabi-gcc (GCC) 9.3.0
Copyright (C) 2019 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.
00000000 <.text>:
   0:   4d01        ldr r5, [pc, #4]    ; (8 <.text+0x8>)
   2:   f240 35e9   movw    r5, #1001   ; 0x3e9
   6:   bf00        nop
   8:   000003e9    andeq   r0, r0, r9, ror #7

现在,虽然通过 gcc 提供汇编语言是另一种汇编语言,但当使用 ldr equals 时,它仍会按预期生成理想的指令。可以使用 movw 的地方它可以,不能使用的地方不可以,但让我们试试这个。

.thumb

.cpu cortex-m0
ldr r5,=1001

.cpu cortex-m3
movw r5,#1001

.align

没有投诉。相同的结果。

正在尝试您的建议:

.thumb

.cpu cortex-m0
movw r5,#1001

.cpu cortex-m3
movw r5,#1001

.align

arm-none-eabi-gcc so.s -c -o so.o
so.s: Assembler messages:
so.s:6: Error: selected processor does not support `movw r5,#1001' in Thumb mode

现在您必须重新编写代码。 movw 不是一个好的解决方案。

编辑 2(针对 OP)

底线,简短的回答...你收到该消息的原因是你无法生成具有该立即值的 thumb mov 立即指令,因为你会在 arm 文档中看到你没有那么多位.如果当您说 rapi 4 时,您的意思是 raspberry pi 4 是支持 aarch32 (armv7-a) 的 armv8,它支持 thumb2 扩展(post armv6-m 包括 movw)

.thumb
ldr r5,=1001
.align

使用 ldr equals 发现最优指令

arm-none-eabi-as -march=armv7a so.s -o so.o
arm-none-eabi-objdump -D so.o

so.o:     file format elf32-littlearm


Disassembly of section .text:

00000000 <.text>:
   0:   f240 35e9   movw    r5, #1001   ; 0x3e9

如果你愿意,可以直接使用它

.thumb
ldr r5,=1001
movw r5,#1001
.align

Disassembly of section .text:

00000000 <.text>:
   0:   f240 35e9   movw    r5, #1001   ; 0x3e9
   4:   f240 35e9   movw    r5, #1001   ; 0x3e9

如果这确实是 raspberry pi 4,那么您需要 armv7-ar 体系结构参考手册来涵盖 aarch32 内容和 armv8(不是 8m)体系结构参考手册来涵盖 aarch64 内容。还有一个不同的 gnu 工具链,因为它是一个完全不同的指令集(aarch64-whatever-whatever vs arm-whatever-whatever)。而且 aarch64 中还没有 thumb 指令。