assembly - ARM 程序集 : auto-increment register on store

标签 assembly arm addressing-mode

是否可以使用 [Rn]! 自动递增 STR 上寄存器的基址? ?我已经浏览了文档,但无法找到明确的答案,主要是因为 LDR 和 STR 都提供了命令语法 - 理论上它应该适用于两者,但我找不到任何 auto 示例- 在商店上递增(加载工作正常)。

我制作了一个小程序,它将两个数字存储在一个向量中。完成后 out 的内容应该是 {1, 2}但是存储会覆盖第一个字节,就好像自动增量不起作用一样。

#include <stdio.h>

int main()
{
        int out[]={0, 0};
        asm volatile (
        "mov    r0, #1          \n\t"
        "str    r0, [%0]!       \n\t"
        "add    r0, r0, #1      \n\t"
        "str    r0, [%0]        \n\t"
        :: "r"(out)
        : "r0" );
        printf("%d %d\n", out[0], out[1]);
        return 0;
}

编辑:
虽然答案对于常规加载和存储是正确的,但我发现优化器混淆了向量指令(例如 vldm/vstm)的自动增量。例如,下面的程序
#include <stdio.h>

int main()
{
        volatile int *in = new int[16];
        volatile int *out = new int[16];

        for (int i=0;i<16;i++) in[i] = i;

        asm volatile (
        "vldm   %0!, {d0-d3}            \n\t"
        "vldm   %0,  {d4-d7}            \n\t"
        "vstm   %1!, {d0-d3}            \n\t"
        "vstm   %1,  {d4-d7}            \n\t"
        :: "r"(in), "r"(out)
        : "memory" );

        for (int i=0;i<16;i++) printf("%d\n", out[i]);
        return 0;
}

编译与
g++ -O2 -march=armv7-a -mfpu=neon main.cpp -o main

将在最后 8 个变量的输出上产生乱码,因为优化器保持递增的变量并将其用于 printf。换句话说,out[i]实际上是 out[i+8] ,所以前 8 个打印值是向量中的最后 8 个,其余是超出范围的内存位置。

我尝试了 volatile 的不同组合整个代码中的关键字,但只有当我使用 -O0 编译时行为才会改变标志或者如果我使用 volatile 向量而不是指针和新的,比如
volatile int out[16];

最佳答案

对于存储和加载,您可以这样做:

ldr r0,[r1],#4
str r0,[r2],#4

无论你在最后放什么,在这种情况下是 4,都会在寄存器用于地址之后但在指令完成之前添加到基址寄存器(ldr 示例中的 r1 和 str 示例中的 r2),它非常像
unsigned int a,*b,*c;
...
a = *b++;
*c++ = a;

编辑,您需要查看反汇编以了解发生了什么(如果有)。我正在使用最新的代码源,或者现在只是来自导师图形工具链的源代码。

arm-none-linux-gnueabi-gcc (Sourcery CodeBench Lite 2011.09-70) 4.6.1
#include <stdio.h>
int main ()
{
        int out[]={0, 0};
        asm volatile (
        "mov    r0, #1          \n\t"
        "str    r0, [%0], #4       \n\t"
        "add    r0, r0, #1      \n\t"
        "str    r0, [%0]        \n\t"
        :: "r"(out)
        : "r0" );
        printf("%d %d\n", out[0], out[1]);
        return 0;
}


arm-none-linux-gnueabi-gcc str.c -O2  -o str.elf

arm-none-linux-gnueabi-objdump -D str.elf > str.list


00008380 <main>:
    8380:   e92d4010    push    {r4, lr}
    8384:   e3a04000    mov r4, #0
    8388:   e24dd008    sub sp, sp, #8
    838c:   e58d4000    str r4, [sp]
    8390:   e58d4004    str r4, [sp, #4]
    8394:   e1a0300d    mov r3, sp
    8398:   e3a00001    mov r0, #1
    839c:   e4830004    str r0, [r3], #4
    83a0:   e2800001    add r0, r0, #1
    83a4:   e5830000    str r0, [r3]
    83a8:   e59f0014    ldr r0, [pc, #20]   ; 83c4 <main+0x44>
    83ac:   e1a01004    mov r1, r4
    83b0:   e1a02004    mov r2, r4
    83b4:   ebffffe5    bl  8350 <_init+0x20>
    83b8:   e1a00004    mov r0, r4
    83bc:   e28dd008    add sp, sp, #8
    83c0:   e8bd8010    pop {r4, pc}
    83c4:   0000854c    andeq   r8, r0, ip, asr #10

所以
sub sp, sp, #8

是分配两个本地ints out[0]和out[1]
mov r4,#0
str r4,[sp]
str r4,[sp,#4]

是因为它们被初始化为零,然后是内联程序集
8398:   e3a00001    mov r0, #1
839c:   e4830004    str r0, [r3], #4
83a0:   e2800001    add r0, r0, #1
83a4:   e5830000    str r0, [r3]

然后是printf:
83a8:   e59f0014    ldr r0, [pc, #20]   ; 83c4 <main+0x44>
83ac:   e1a01004    mov r1, r4
83b0:   e1a02004    mov r2, r4
83b4:   ebffffe5    bl  8350 <_init+0x20>

现在很清楚为什么它不起作用。你没有被声明为 volatile 。您没有让代码返回 ram 来获取 printf 的 out[0] 和 out[1] 的值,编译器知道 r4 包含 out[0] 和 out[1] 的值,有这个函数中的代码太少了,它不必驱逐 r4 并重用它,所以它使用 r4 作为 printf。

如果您将其更改为 volatile
    volatile int out[]={0, 0};

然后你应该得到想要的结果:
83a8:   e59f0014    ldr r0, [pc, #20]   ; 83c4 <main+0x44>
83ac:   e59d1000    ldr r1, [sp]
83b0:   e59d2004    ldr r2, [sp, #4]
83b4:   ebffffe5    bl  8350 <_init+0x20>

printf 的准备工作从 ram 中读取。

关于assembly - ARM 程序集 : auto-increment register on store,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9102113/

相关文章:

assembly - 如何在 MIPS 中通过 $a0 传递 float ?

assembly - IRQ 编号冲突

architecture - 将 armv6/armv7 架构更改为 armv6 对我的 iPad 应用程序有何影响?会有性能/稳定性损失吗?

c++ - 类(class)成员的顺序会影响访问速度吗?

assembly - NDK r8c 针对有关 "Cortex-A8 erratum"的 asm 对象发出警告 - 我应该担心吗?

c++ - 如何在 x86 程序集中移动两个 float 相乘的结果?

linux - 如何在ARM上为虚拟内存选择静态IO内存映射

c - 如何在实现分析时处理溢出

assembly - 为什么 x86 16 位寻址模式没有比例因子,而 32 位版本有?

程序集:此程序中 movl data_items(,%edi,4), %eax 的目的是什么