无法用 LDR 替换 MOVW / MOVT



我想用单个ldr指令替换这个movw / movt组合:

movw r0, #0x3800
movt r0, #0x4002
|
V
ldr r0, =0x40023800

但是,在这个更改之后,代码就不再工作了。既然这是我唯一改变的部分,那一定是我的错。我的印象是,这两个代码段是相同的。

装配代码在顶部有以下说明

.syntax unified
.cpu cortex-m4
.thumb

并使用GNU Arm嵌入式工具链与以下命令进行编译:

arm-none-eabi-as -mcpu=cortex-m4 code.s -c -o code.o
arm-none-eabi-gcc -T link.ld -nostartfiles -o result code.o

如果重要的话,代码的其余部分与此页面(底部)托管的代码相同。该代码在带有Arm Cortex-M4内核的STM32F411CEU6上运行。

为什么这个更改会破坏我的代码?有没有另一种将32位立即数写入寄存器的单行方法?


在汇编程序上运行objdump的结果显示了这一点(注意<main>的开始和<loop>:的结束

Disassembly of section .text:
00000000 <main>:
0:   4810        ldr r0, [pc, #64]   ; (44 <.loop+0x2a>)
2:   f240 0101   movw    r1, #1
[removed]
18:   6001        str r1, [r0, #0]
0000001a <.loop>:
1a:   f240 0100   movw    r1, #0
[removed]
44:   40023800    andmi   r3, r2, r0, lsl #16

首先,汇编语言是特定于汇编程序(armasm、gas等)的,而不是目标程序(cortex-m4),它们可以而且经常是跨工具不兼容的语言。Ldr通常被用作伪指令,这意味着汇编程序决定为您使用什么指令,而不是您要求的指令。

.cpu cortex-m4
.thumb
ldr r0,=0x12345678
ldr r1,=0x00000003
ldr r2,=0xFFFFFFF5
nop
nop
b .

组装和拆卸

00000000 <.text>:
0:   4803        ldr r0, [pc, #12]   ; (10 <.text+0x10>)
2:   f04f 0103   mov.w   r1, #3
6:   f06f 020a   mvn.w   r2, #10
a:   46c0        nop         ; (mov r8, r8)
c:   46c0        nop         ; (mov r8, r8)
e:   e7fe        b.n e <.text+0xe>
10:   12345678    eorsne  r5, r4, #120, 12    ; 0x7800000

首先使用gnu汇编程序支持ldr r0,=语法,不要期望所有arm/thumb汇编程序都支持该语法。其次,对于gnu汇编程序(也许还有另一个答案中指出的其他程序),如果该工具可以创建一个实际上不执行ldr的优化,那么它会的。

我们可以看到,对于第二个和第三个常量,汇编程序使用了非加载指令,该常量嵌入为立即数。

对于0x12345678值,你根本无法将32位立即数放入32位(或16位)指令中,你必须进行加载,它通过找到一个池来放入常量,并使pc对其进行相对加载来完成。

汇编语言绝对不适合编译器,只有一些编译器使用汇编语言。如果没有汇编语言,我们就不会有当前或新的处理器供人类用于处理器开发和测试。因此,非人类的asm意味着没有处理器。我们将无法引导高级编程语言,因此将没有编程语言。不会有编译器,因为你需要通过汇编语言精通指令集的人(即使编译器没有编译到asm)才能成功创建编译器,所以不会有编译器——因为其他原因还没有。如果人工指令集级别的编程消失,处理器和所有副作用都会消失。每一代人中都有许多人必须传递火炬,寻找并教导他人。

我很幸运(好吧,这是计划好的),工作没有在这里完成,这个呢:

.cpu cortex-m4
.thumb
ldr r0,=0x12345678
ldr r1,=0x00000003
ldr r2,=0xFFFFFFF5
nop
b .
Disassembly of section .text:
00000000 <.text>:
0:   4803        ldr r0, [pc, #12]   ; (10 <.text+0x10>)
2:   f04f 0103   mov.w   r1, #3
6:   f06f 020a   mvn.w   r2, #10
a:   46c0        nop         ; (mov r8, r8)
c:   e7fe        b.n c <.text+0xc>
e:   56780000    ldrbtpl r0, [r8], -r0
12:   Address 0x0000000000000012 is out of bounds.

常量被放置在一个不与单词对齐的边界上。它可能已经成功组装,但ldr是一个未对齐的传输,这可能会导致异常,代码无法工作。

快速修复:

.cpu cortex-m4
.thumb
ldr r0,=0x12345678
ldr r1,=0x00000003
ldr r2,=0xFFFFFFF5
nop
b .
.align

Disassembly of section .text:
00000000 <.text>:
0:   4803        ldr r0, [pc, #12]   ; (10 <.text+0x10>)
2:   f04f 0103   mov.w   r1, #3
6:   f06f 020a   mvn.w   r2, #10
a:   46c0        nop         ; (mov r8, r8)
c:   e7fe        b.n c <.text+0xc>
e:   bf00        nop
10:   12345678    eorsne  r5, r4, #120, 12    ; 0x7800000

这起到了作用。我们仍然在假设资金池的去向,我们可以尝试强制它

.cpu cortex-m4
.thumb
one:
ldr r0,=0x12345678
ldr r1,=0x00000003
ldr r2,=0xFFFFFFF5
b .
.align
two:
ldr r0,=0x11223344
b .
.align
Disassembly of section .text:
00000000 <one>:
0:   4803        ldr r0, [pc, #12]   ; (10 <two+0x4>)
2:   f04f 0103   mov.w   r1, #3
6:   f06f 020a   mvn.w   r2, #10
a:   e7fe        b.n a <one+0xa>
0000000c <two>:
c:   4801        ldr r0, [pc, #4]    ; (14 <two+0x8>)
e:   e7fe        b.n e <two+0x2>
10:   12345678    eorsne  r5, r4, #120, 12    ; 0x7800000
14:   11223344            ; <UNDEFINED> instruction: 0x11223344

.cpu cortex-m4
.thumb
one:
ldr r0,=0x12345678
ldr r1,=0x00000003
ldr r2,=0xFFFFFFF5
b .
.align
.ltorg
two:
ldr r0,=0x11223344
b .
.align
00000000 <one>:
0:   4802        ldr r0, [pc, #8]    ; (c <one+0xc>)
2:   f04f 0103   mov.w   r1, #3
6:   f06f 020a   mvn.w   r2, #10
a:   e7fe        b.n a <one+0xa>
c:   12345678    eorsne  r5, r4, #120, 12    ; 0x7800000
00000010 <two>:
10:   4800        ldr r0, [pc, #0]    ; (14 <two+0x4>)
12:   e7fe        b.n 12 <two+0x2>
14:   11223344            ; <UNDEFINED> instruction: 0x11223344

电脑的相对负载是正向的,所以我们没有完全控制这个:

.cpu cortex-m4
.thumb
one:
ldr r0,=0x12345678
ldr r1,=0x00000003
ldr r2,=0xFFFFFFF5
nop
b .
two:
ldr r0,=0x11223344
b .
.align
.ltorg

00000000 <one>:
0:   4804        ldr r0, [pc, #16]   ; (14 <two+0x6>)
2:   f04f 0103   mov.w   r1, #3
6:   f06f 020a   mvn.w   r2, #10
a:   46c0        nop         ; (mov r8, r8)
c:   e7fe        b.n c <one+0xc>
0000000e <two>:
e:   4802        ldr r0, [pc, #8]    ; (18 <two+0xa>)
10:   e7fe        b.n 10 <two+0x2>
12:   bf00        nop
14:   12345678    eorsne  r5, r4, #120, 12    ; 0x7800000
18:   11223344            ; <UNDEFINED> instruction: 0x11223344

我们不需要在2之前对齐,2可以降落在非32位边界上,所以我们有时会在那里保存半字。align(汇编语言是特定于汇编程序的,而不是目标程序,这是gnu汇编程序支持的语言,并为该目标程序做了特定的事情)允许它在4字节的边界上对齐,这样ldr就不会出错,而.l.org实际上并没有改变这里的内容,只是表明这是我们想要的。你还必须了解电脑的相对负载可以工作多远,每个指令集(arm、mips、x86等)的范围都不尽相同,所以你不能在一个大项目结束时只采用池的方式。

为什么这不一定对你有用?我们需要看到并理解——你只是简单地更改了汇编语言并重新汇编,还是侵入了二进制指令?后者有很多问题,包括指令大小以及如何将项填充到池中。如果它只是代码,并且您对其进行了组装,那么它很可能是未对齐的,并且您可能会遇到未对齐的访问错误。

但是ldr-rd,=可以覆盖所有可能的比特模式,movw/movt也可以。但是gnu汇编程序上的ldr rd,=如果可以的话会进行优化,否则它需要池,并且池需要为它做好准备。如果你手动创建movw/movt,你只需要这两条指令,而不需要池。

你需要做一个更完整的例子;不再工作";方法

ldr是一条伪指令,可转换为从池中加载。

LDR Rd,=const伪指令生成最有效的加载任何32位数字的单个指令。你可以使用这个生成超出范围的常量的伪指令MOV和MVN指令。LDR伪指令生成指定立即值的最有效单一指令:

如果立即值可以用单个MOV或MVN指令构造,则汇编程序会生成适当的指令。如果立即值不能用单个MOV或MVN指令构造,则汇编程序:将值放入文字池(嵌入代码中的内存的一部分,用于保存常数值)。生成一条LDR指令,该指令具有从字面值池读取常量的PC相对地址。例如:

LDR      rn, [pc, #offset to literal pool]
; load register n with one word
; from the address [pc + offset]

您必须确保在汇编程序生成的LDR指令的范围内有一个文字池。

https://www.keil.com/support/man/docs/armasm/armasm_dom1359731147386.htm

最新更新