在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
行发出了常规(stpcpy)()
(libc调用)。由于mempcpy
builtin code in the gcc codebase made最初让我相信,我原本希望gcc将其降低为stpcpy
。
命令行: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,我无法使用gcc创建与clang一样的代码。
答案 0 :(得分:3)
我不知道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
。我只是尝试了这个并查看了assemlby。使用memcpy
来电memcpy
。使用__builtin_memcpy
直接构建内存副本。但是,如果您将大小设置为大于8192,则会调用memcpy
函数。它与使用-std=gnu99
相同。
答案 1 :(得分: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
如果原型不同,则无法确定函数是内置替换的候选函数。它是许多问题之一的隐式声明介绍。
答案 2 :(得分:1)
这可以通过定义与您的函数同名的宏来完成,即使在使用-fno-builtin
或-ffreestanding
进行编译时也可以使用,如果您想避免在其余函数上使用内置函数。< / p>
例如:
#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