使 gcc/clang 将函数识别为内置函数
Posted
技术标签:
【中文标题】使 gcc/clang 将函数识别为内置函数【英文标题】:Make gcc/clang recognize function as builtin 【发布时间】:2014-09-04 14:40:20 【问题描述】:在 neovim 项目中,我们使用了一些标准功能,但并未在所有目标平台上实现。值得注意的是,stpcpy
,很快还有mempcpy
。 Currently we're solving that by supplying and using our own x
variants of these functions.
一个例子:
char *xstpcpy(char *restrict dst, const char *restrict src)
FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
const size_t len = strlen(src);
return (char *)memcpy(dst, src, len + 1) + len;
然而,这仍然不是完全最优的,因为一些编译器,比如 gcc,知道这些函数的标准版本是做什么的,并且在给定足够的上下文时可以生成更好的代码:gcc code for stpcpy builtin。
我已经考虑在它们周围放置#ifdef
守卫,只有当它们没有被定义时才应该由我们提供,并且我们应该开始使用常规名称(stpcpy
而不是xstpcpy
)。但在这一点上,这将是一个更具侵入性的变化。我的问题是我是否可以告知 gcc xstpcpy
与 stpcpy
完全相同?
P.S.:一个相关问题:是否有一个标志,例如-std=c99
,它强制 gcc/clang 无论如何都会发出对标准函数的调用?我似乎记得这样的事情,但现在找不到参考。如果-std=c99
确实禁用了内置扩展,我想知道如何在保持-std=c99
的同时启用内置扩展。
编辑:由于一切似乎都有些模糊,我一直在尝试一些事情。首先是代码:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
const char str[] = "we have stpcpy";
printf("static\n");
char p1[256];
char p2[256];
char *end1 = stpcpy(p1, str);
char *end2 = (stpcpy)(p2, str);
printf("static using stpcpy?\np1 = %s (end = %p)\np2 = %s (end = %p)\n",
p1, end1, p2, end2);
return 0;
结果(我在 OSX 上,但 godbolt 表明它在 linux 上类似):
命令行:gcc-4.9 -O3 -save-temps -march=native stpcpy.c -o stpcpy
gcc 4.9 似乎发出了对stpcpy_chk
的调用来代替stpcpy()
行,并发出常规_stpcpy
(libc 调用)来代替(stpcpy)()
行。我本来希望 gcc 将其降低为 mempcpy
,因为 stpcpy
builtin code in the gcc codebase made 最初让我相信。
命令行:clang -O3 -save-temps -march=native stpcpy.c -o stpcpy
(XCode clang 3.4)
Clang 或多或少具有我对 gcc 的期望。它完全优化了对stpcpy
的调用。像这样创建 asm:
leaq -258(%rbp), %rdx
movabsq $34182044572742432, %rax ## imm = 0x79706370747320
movq %rax, -265(%rbp)
movabsq $2334402142592329079, %rcx ## imm = 0x2065766168206577
movq %rcx, -272(%rbp)
而不是调用_stpcpy
。
我想知道我是否可以让 gcc-4.9 做我想做的事。使用具有不同版本的godbolt,我无法像clang那样使用gcc创建类似的代码。
【问题讨论】:
@technosaurus:我想知道-fno-builtin
的反面。
@technosaurus-fno-builtin
仅适用于 GCC 4.9.x ***.com/questions/25272576/…
-fno-builtin
存在的时间比 4.9 还要长... -fbuiltin(如果存在的话)是默认行为,除非指定了 -fno-builtin
或 -ffreestanding
。使用 -std=*** 更改其中任何一个都没有意义,因为该行为是实现定义的,而不是标准。尽管我不想推荐它,但 autotools 就是为这种情况而设计的。
@technosaurus,您阅读链接了吗?您是否尝试使用 -fno-builtin
与例如GCC 4.8.1 并查看程序集?
@Zboson gcc.godbolt.org 使用 g++
,而不是 gcc
并且 -fno-builtin 失败的任何版本都是该版本中的错误(另请注意,4.8 是第一个使用的版本C ++,因此预计会出现新错误)自动工具通常会为编译器错误添加解决方法(这就是为什么配置脚本最终可能是实际代码大小的 100 倍)...如果您想确保使用库函数(强制不使用内置函数)你可以像 (function_name)(parameters,...)
那样调用它而不是 function_name(parameters,...)
...注意函数名周围的括号。
【参考方案1】:
我不知道 stpcpy
的内置函数是如何工作的,但对于 memcpy
,它要求大小是编译时间常数并且小于或等于 8192 字节。如果您的代码满足这两个要求(并且您不使用-fno-builtin
),GCC 将使用内置的memcpy
。我不知道有什么方法可以强制它使用 builtin
以获得更大的尺寸。
要禁用内置函数,您可以使用-fno-builtin
。但是,-fno-builtin
only seems to work for GCC 4.9.x。
编辑:
要将内置函数与-std=c99
一起使用,请使用__builtin_memcpy
。我刚试过这个,看了看汇编。使用memcpy
调用memcpy
。使用__builtin_memcpy
直接构建内存副本。但是,如果您输入的大小大于 8192,它会调用 memcpy
函数。和-std=gnu99
一样。
【讨论】:
stpcpy
的内置函数降级为mempcpy
的内置函数,因此会非常相似。我并不是要求总是强制使用内置函数,而是要求在适当的时候将我们的自定义函数替换为内置函数。 (例如:xstpcpy
应该成为适当的内置 stpcpy
如果strlen(s) < 8192
)。我还在某处读过内置在某些标准模式下被禁用的地方。 如果是这种情况,我想禁用该功能,但保留标准模式。
不,这将强制在所有情况下都使用内置函数。我希望它只在编译器推断它是有益的情况下使用。就像普通案例(strlen
、memcpy
、stpcpy
、...),但对于“我们的”版本(xstpcpy
、...)。
@Aktau,不,__builtin_memcpy
的限制和以前一样。
我对 gcc 4.8.3 上的 -fno-builtin
没有任何问题
@keltar,它不适用于 GCC 4.8.1 及更低版本。您可以通过gcc.godbolt.org 自行查看。确保大小是编译时间常数并且小于 8192。【参考方案2】:
内置函数不是用户定义的。
你可以使用例如
#ifdef HAVE_STPCPY
#define xstpcpy stpcpy
#else
char *xstpcpy(char *restrict dst, const char *restrict src);
#endif
在你的头文件中,前提是你有HAVE_*
宏定义为configure
。这将允许编译器在合理的情况下使用内置函数。
至于-std=c99
- C99 没有stpcpy
,它是glibc 特有的功能。您可能使用隐式声明对其进行了测试。 gcc
如果原型不同,则无法确定函数是内置替换的候选函数。这是隐式声明引入的许多问题之一。
【讨论】:
这是我想到的第一个解决方案,我认为我们可能不得不采用它,即使它有点难看。我最初想避免在配置步骤中定义“HAVE_”。顺便说一句,我们实际上是用-std=gnu99
编译的,但我们稍后也需要MSVC 兼容。但我想重要的是:即使指定了-std=c99
,gcc 是否将stpcpy
作为内置函数处理?
@Aktua 为什么不添加-S
编译并查看程序集以回答您的问题?
快速测试说“不,除非 -D_GNU_SOURCE 也被定义”,这是有道理的 - 如果没有这个宏,你就不能安全地使用 glibc 特定的功能。
@keltar 如果您在 stpcpy 周围添加括号,则库函数将始终被调用...#define xstpcpy (stpcpy)
参见:***.com/a/25272962/1162141
@technosaurus 这似乎主要是 gcc 的事情,clang 不尊重这一点(请参阅我的编辑)。【参考方案3】:
这可以通过定义一个与您的函数同名的宏来完成,如果您想避免在其余函数中使用内置函数,即使在使用 -fno-builtin
或 -ffreestanding
编译时也可以工作。
例如:
#define strlen __builtin_strlen
//or
#define strlen(...) __builtin_strlen(__VA_ARGS__)
注意:如果你把它命名为my_strlen()
,你可以给strlen添加一个弱别名,让strlen被一个实际的strlen()
函数(如果存在)覆盖
如果该值可以在编译时计算,它将被缩减为一个常量。 如果它不能减少到一个常数,那么它会:
用自己的优化版本替换调用 回退到您的版本strlen
确实有一个内置替换(repne scazb 变体),但我不确定是否有办法(除了将其从编译器中删除)无需代码替换即可获得常量检查通过
编辑:添加宏以检查内置函数
#ifdef __clang__
#define HAS(...) __has_builtin(__VA_ARGS__)
#elif defined __GNUC__ //assume gcc ... (where the list came from)
#define HAS(...) 1
#else
#define HAS(...) 0
#endif
#if HAS(__builtin_stpcpy)
#define stpcpy __builtin_stpcpy
#endif
【讨论】:
"注意:如果将其命名为 my_strlen(),则可以为 strlen 添加一个弱别名,以允许 strlen 被实际的 strlen() 函数(如果存在)覆盖"。我一直在考虑这个,但找不到 MSVC 是否支持这个。遗憾的是,这将是一项要求。 @Aktau 原始问题中没有提到,但显然它确实有类似的东西:***.com/a/11529277/1162141以上是关于使 gcc/clang 将函数识别为内置函数的主要内容,如果未能解决你的问题,请参考以下文章