【问题标题】:GCC with -fno-builtin does not seem to work带有 -fno-builtin 的 GCC 似乎不起作用
【发布时间】:2014-08-12 19:30:55
【问题描述】:

我想比较一下 GCC 内置函数 memcpy 和来自 libc 的函数。但是,-fno-builtin 或 -fno-builtin-memcpy 的所有迭代似乎都被忽略了。

//g++ -O3 foo.cpp -S or
//g++ -O3 -fno-builtin foo.cpp -S
#include <string.h>
int main() {
    volatile int n = 1000;
    //int n = 1000;
    float *x = new float[1000];
    float *y = new float[1000];
    memcpy(y,x,sizeof(float)*n);
    //__builtin_memcpy(y,x,sizeof(float)*n);    
}

我发现如果上面源代码中的n 不是易失性的,那么它会内联内置代码。但是,当n 变为易失性时,它会调用函数__memcpy_chk,它是memcpy with buffer overflow checking 的一个版本。如果n 是不稳定的,我改为调用__builtin_memcpy,那么它调用memcpy。

所以到目前为止,我的结论是,只有在编译时知道n 并且-fno-builtin 无用时才会生成内置代码。我正在使用 GCC 4.8.2。

-fno-builtin 过时了吗?即使在编译时知道n,有没有办法让GCC从C库调用memcpy?

【问题讨论】:

  • 我刚刚注意到,在这个问题和您的 cmets 中,您拼错了 -fno-builtin 几种不同的方式。检查以确保这不会影响您的结果。正确的拼写是“b u i l t i n”。
  • @Zack,感谢您找到拼写错误。我希望我把它们都修好了。我确实有点太草率了(当我应该在我的测试示例中使用 malloc 时,我也使用了new - 在我自己的代码中,无论如何我都在使用_mm_malloc)。但是如果我使用任何拼写错误,GCC 会抱怨。但是,如果您想自己检查,请将上面的代码(删除 volatile)放入 gcc.godbolt.org 并更改为 GCC 4.8 或 4.9,添加 -fno-builtin 并查看汇编代码。
  • @Zack,确保你也添加了-O3。

标签: c++ gcc memcpy


【解决方案1】:

-fno-builtin 和 -fno-builtin-memcpy 都具有您对 gcc 4.9.1 的预期效果。这可能只是 gcc 4.8.2 中的一个错误;这种特殊的选项组合并未广泛使用。 -ffreestanding 是一个相关的开关,4.8.2 可能会有你想要的效果。

请注意,编译器有权将您的程序优化到

int main() { return 0; }

当在没有-fno-builtin(-memcpy) 或-ffreestanding 的情况下调用时,即使n 是volatile,因为它可以(原则上)证明程序作为一个整体没有可观察到的副作用,或者它的行为未定义. (当n 是not volatile 时,不可能有 UB;如果n 在读取时超出了[0, 1000] 范围,并且volatile 告诉编译器它可以,则会发生 UB' t 假设 n 具有程序写入的值。)

【讨论】:

  • 我试过-ffreestanding。它没有任何区别。我必须安装 gcc 4.9.1 来验证你的答案。
  • 我查看了 http://gcc.godbolt.org/ 的程序集输出,我可以确认 -fno-builtin 在 GCC 4.9 中按预期工作,而不是在 GCC 4.8.1 中。
  • 我检查了 GCC 4.4 到 4.9,只有 4.9 是正确的。
  • ICC 也是正确的。当我通过-fno-builtin 时使用memcpy 而没有它使用_intel_fast_memcpy。
  • 为了清楚起见,编译器并没有将代码优化掉。看汇编代码就清楚了。
【解决方案2】:

注意:因为您正在编译 C++ 代码,所以我不能 100% 确定这是否适用。

C 标准要求所有库函数(除非另有明确说明)都有地址,并且可以是 &amp; 地址运算符的操作数。这是因为它允许将一些/大多数函数实现为函数宏,但在某些情况下仍应像实际变量/函数一样运行。为了避免它的宏版本,您只需要在 memcpy 令牌和 ( 令牌之间添加一些东西(正如@Zach 指出的那样,空格是不够的):

(memcpy)(y, x, ...)

这会强制使用实际函数,应该避免任何类型的内置宏定义。


-O3 优化也有可能(阅读:可能)扫描某些函数调用(例如 memcpy)并用内置调用替换它们,而不管-fno-builtin。

【讨论】:

  • memcpy 的宏版本仍将在memcpy 和( 之间使用空格进行扩展。但是,在标记 memcpy 周围加上括号确实如您所描述的那样。
  • 另外,我可以权威地说,-fno-builtin 应该在所有优化级别上执行 OP 所期望的。 gcc.gnu.org/onlinedocs/gcc-4.8.1/gcc/…
  • 我用 GCC 和 g++ 尝试了你的建议(这段代码在 C 中编译得很好,所以我不确定 C++ 标记是否更好),它们没有任何区别。好像-O3 胜过一切。
  • @DrewMcGowen 您正在考虑功能宏定义,而不是使用。 N1570(最接近 C2011 的近似值,无需付费):6.10.3p3 加上 6.10.3p{9,10} 中的语法规则一起要求功能宏是定义的,两者之间没有空格宏名和左括号。 (1/2)
  • @DrewMcGowen 但是,6.10.3p10 中的散文(特别是开头的句子“类函数宏名称的每个后续实例后跟一个(作为下一个预处理标记"(强调我的:空格不是预处理标记))清楚地(好吧,如果你习惯于标准的话)表明功能宏被扩展,即使有任何数量的空格宏名称和( 之间的任何类型。 (2/2)
【解决方案3】:

您的问题很可能是 glibc,而不是 gcc。您没有指定,但您可能使用的是 Ubuntu,它默认定义 -D_FORTIFY_SOURCE=2。这会提示 glibc 标头提供 memcpy 的内联定义,该定义转发到 __memcpy_chk。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-04-17
    • 1970-01-01
    • 2011-10-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-14
    相关资源
    最近更新 更多