如何告诉 GCC 在将指针传递给具有只读访问权限的函数后不需要从内存中重新加载值

问题描述 投票:0回答:1

请考虑以下计划:

#include <stdio.h>

typedef char*(*g)(const char*)
__attribute__((__access__(__read_only__, 1)))
;

char *h(const char*)
__attribute__((__access__(__read_only__, 1)))
;

// not from string.h, declared here!
char *strdup(const char*);

int main() {
    
    g f = h; // change h to strdup to see the effect
    char c[2] = "0";
    f(c);
    
    printf("%c\n", c[0]);

    return 0;
}

godbolt.org

我想做的是说服 GCC 在调用

c[0]
时不需要从内存中加载
printf()
的值。

我也对适用于函数指针的解决方案感兴趣。

由于

const
很容易被抛弃,所以我对编译器不会使用这些信息进行优化已经不那么惊讶了。所以我尝试了 access 属性。但没有运气。

但是想象一下当我将

g f = h
更改为
g f = strdup
时我惊讶的表情。

赋值

h
时生成的代码如下:

call    h
movsbl  14(%rsp), %esi    // loaded from memory
movl    $.LC0, %edi
xorl    %eax, %eax
call    printf

虽然分配

strdup
时生成的代码也是我也希望看到的
h
:

call    strdup
movl    $48, %esi    // the "0" is directly written to register
movl    $.LC0, %edi  // because here GCC recognizes that the
xorl    %eax, %eax   // content did not change
call    printf

请注意,当我在该文件中使用

strdup
的自定义声明(而不是 string.h 中的声明)时,效果也可见。

编译器突然意识到函数调用不会改变

c
中的内容,并直接用
printf()
调用
0x48
,而不是从内存中加载值。

看起来编译器中存在一些针对

strdup
的硬编码优化。

问题:但是我如何说服 GCC 对我的函数进行相同的优化

h
(最好也针对通过函数指针调用的函数)?


更新:由于一些评论者对此感到困惑,该示例使用单个

char
而不是正确的字符串来说明效果,因此我相应地更改了示例。此外,我强调了主要问题。

更新2:将godbolt.org的结果复制到问题中,因为不是每个人都想点击链接并尝试一下。

c gcc optimization readonly-variable
1个回答
0
投票

这是因为编译器将

strdup
识别为特殊的库函数情况,因此基于此假设内联调用。而您的自定义函数具有外部链接并且无法内联。如果您提供函数定义以及有保证的内联,您将看到与
strdup
:

相同的行为
__attribute__((always_inline)) static inline char *h (const char* x)
{ 
  /* do something meaningful here */
}

由于定义在同一翻译单元中可见,我得到的

movl $48, %esi
与您对
strdup
所做的相同。据说这是因为当函数内联时,编译器可以知道函数未触及寄存器。

© www.soinside.com 2019 - 2024. All rights reserved.