使gcc / clang识别内置函数

时间:2014-09-04 14:40:21

标签: c gcc clang posix built-in

在neovim项目中,我们使用了一些标准但未在所有目标平台上实现的功能。值得注意的是,stpcpy以及很快mempcpyCurrently 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,xstpcpystpcpy完全相同?

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一样的代码。

3 个答案:

答案 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