assembly arm编译程序的非优化行为

ymzxtsji  于 2023-02-19  发布在  其他
关注(0)|答案(2)|浏览(126)

在我的源代码中,我看到arm编译器的一个奇怪的行为,它对一个字符串进行了冗余的迭代,这是不必要的。我在这里展示了一个最小的例子来说明这一点,并在下面提出了我的问题

#include <string.h>
#define MIN(x, y) (((x) < (y)) ? (x) : (y))

int MAX_FILE_NAME = 2500;
int F(char *file){
    int file_len = MIN(strlen(file), MAX_FILE_NAME - 1);
    return file_len;
}
int main(void) {
    F(__FILE__);
    return 0 ;
}

编译:

arm-none-eabi-gcc -nostdlib -Xlinker -Map="m7_experiments.map" -Xlinker --cref -Xlinker --gc-sections -Xlinker -print-memory-usage -mcpu=cortex-m7 -mfpu=fpv5-sp-d16 -mfloat-abi=hard -mthumb -T "m7_experiments_Debug.ld" -o "m7_experiments.axf"  ./src/cr_startup_cm7.o ./src/crp.o ./src/flashconfig.o ./src/m7_experiments.o

导致:

Dump of assembler code for function F:
   0x00000104 <+0>:     push    {r4, lr}
   0x00000106 <+2>:     mov     r4, r0
   0x00000108 <+4>:     bl      0x13c <strlen>
   0x0000010c <+8>:     mov     r2, r0
   0x0000010e <+10>:    ldr     r3, [pc, #20]   ; (0x124 <F+32>)
   0x00000110 <+12>:    ldr     r0, [r3, #0]
   0x00000112 <+14>:    subs    r0, #1
   0x00000114 <+16>:    cmp     r2, r0
   0x00000116 <+18>:    bcc.n   0x11a <F+22>
   0x00000118 <+20>:    pop     {r4, pc}
   0x0000011a <+22>:    mov     r0, r4
   0x0000011c <+24>:    bl      0x13c <strlen>
   0x00000120 <+28>:    b.n     0x118 <F+20>
   0x00000122 <+30>:    nop
   0x00000124 <+32>:    lsls    r0, r3, #6
   0x00000126 <+34>:    movs    r0, r0

请注意,在文件长度比定义的长度短的情况下,不是从$r2中获取它的长度,而是再次计算它,使运行时间恶化到2* 文件长度。这似乎是不必要的。有什么方法来证明这种情况下编译器的行为吗?我很想知道。

agxfikkp

agxfikkp1#

这是多余的,但这是因为你的代码,而不是编译器,宏将扩展为:

// x = strlen(file)
// y = MAX_FILE_NAME - 1
(((strlen(file)) < (MAX_FILE_NAME - 1)) ? (strlen(file)) : (MAX_FILE_NAME - 1))

请记住,预处理器本质上只是一个美化过的复制和粘贴机器,您将调用strlen两次。

size_t file_len = strlen(file);
file_len = MIN(file_len, MAX_FILE_NAME - 1);
s5a0g9ez

s5a0g9ez2#

在这种情况下,有没有什么方法可以证明编译器的行为是正确的?我很想知道。
编译器在谨慎行事。
在更高级别的优化中,编译器使用strlen()的内部知识,并且"知道" strlen(file)将通过第二次调用返回相同的值。
考虑:

int file_len = MIN(rand(), MAX_FILE_NAME - 1);

即使启用了优化,MIN()也可能不会返回最小值,因为如果第一次调用rand()的值更小,它应该第二次调用rand()
考虑:

int file_len = MIN(some_user_funciton(file), MAX_FILE_NAME - 1);

编译器可能对some_user_funciton(file)没有什么线索,因此在需要时第二次调用some_user_funciton(file)

相关问题