Glibc函数的GCC,-flto,-fno-builtin和自定义函数实现

时间:2018-12-23 15:12:30

标签: c++ c gcc compilation linkage

我正在观察带有GCC标志-fltojemalloc / tcmalloc的意外行为(至少我找不到解释)。一旦使用了-flto,并且我与上述库malloc / calloc链接,并且没有用je/tc malloc实现替换好友,则调用glibc实现。删除-flto标志后,一切都会按预期进行。我试图将-fno-builtin / -fno-builtin-*-flto一起使用,但仍然没有选择je/tc malloc的实现。

-flto机制如何工作?为什么二进制文件不选择新的实现?

-fno-builtin无法解析的外部失败的情况下,它如何与printf链接?

EDIT001:
GCC 7.3
示例代码

int main()
{
    auto p = malloc(1024);
    free(p);
    return 0;
}

编译:

  

/ usr / bin / c ++ -O2 -g -DNDEBUG -flto -std = gnu ++ 14 -o   CMakeFiles / flto.dir / main.cpp.o -c   /home/user/Development/CPPJunk/flto/main.cpp

链接:

  

/ usr / bin / c ++ -O2 -g -DNDEBUG -flto CMakeFiles / flto.dir / main.cpp.o   -o flto -L / home / user / Development / jemalloc -Wl,-rpath,/ home / user / Development / jemalloc -ljemalloc

EDIT002:
更合适的示例代码

#include <cstdlib>

int main()
{
    auto p = malloc(1024);
    if (p) {
        free(p);
    }

    auto p1 = new int;
    if (p1) {
        delete p1;
    }

    auto p2 = new int[32];
    if (p2) {
        delete[] p2;
    }
    return 0;
}

1 个答案:

答案 0 :(得分:3)

首先,您的示例代码是错误的。仔细阅读C11标准n1570。当您想使用 standard malloc时,应该使用#include <stdlib.h>

在C ++ 11中(读为n3337malloc被皱了皱眉,不应该被使用(首选new)。如果您仍想在C ++中使用std::malloc,则应该使用#include <cstdlib>(在GCC中内部包含<stdlib.h>

然后,您的示例代码几乎是C代码(一旦将auto替换为void*),而不是C ++。根据{{​​3}},可能是optimized(一旦包含<stdlib.h>,甚至是 -flto但只有-O3规则,将其更改为空的main。 (我什至写了一份公共报告as-if,其中有第1.4.2节在几页中解释了优化是如何发生的。)

为了优化mallocfree,GCC在__attribute__(malloc)的声明(在<stdlib.h>内)中使用了一些malloc bismon-chariot-doc.pdf。 / p>

  

-flto机械如何工作?

LTO在function attribute中进行了解释。

通过在“编译”和“链接”时使用实际上(类似GCC internals §25和/或类似GIMPLE的代码)的内部表示(实际上,链接步骤变为另一个具有整个程序优化功能的编译,因此您的代码在实践中会被“编译”两次。

在实践中,

LTO 始终应该(在实践中)与一些优化标志(例如-O2甚至是-O3)一起使用,同时在链接时使用。因此,您应该使用g++ -flto -O2编译并链接(在至少-flto和<<的情况下使用-O2 应当在编译时和链接时使用完全相同的优化标记。

更准确地说,-flto还在目标文件中嵌入了源代码的某些内部(类似于SSA)表示,并且也“在链接时”使用(特别是对于{{3} }和GIMPLE在重新链接您的 entire 程序并重新使用其GIMPLE时再次发生。实际上,GCC包含一些LTO前端和称为lto1的编译器(除了C ++前端和称为cc1plus的编译器),而lto1是(当您 link g++ -flto -O2的em>来重新处理这些GIMPLE表示形式。

libjemalloc可能有其自己的标头,并且可能具有inline(或不可插入)功能。然后,当从其源代码编译该库时,还需要使用-flto -O2(以便其Gimple存储在该库中)

最后,通常的malloc被调用的事实独立于-flto。这是一个链接器问题,而不是编译器问题。您可以尝试静态链接-ljemalloc(然后最好使用gcc -flto -O2来构建该库;如果不这样构建,就不会在{{1}上获得LTO优化) })。

您也可以将malloc传递给编译和链接命令,以了解-v在做什么。您甚至可以通过g++来要求-Wl,--verbose(以ld开头)冗长。

请注意,LTO(及其使用的内部表示形式)是特定于编译器和版本的。 optimizationinlining之间的内部(Gimple&SSA)表示稍有不同(在Clang中,非常有所不同,因此当然不兼容)。动态链接器GCC 7不了解LTO。

PS。您可以安装g++软件包并在代码中添加libjemalloc-dev。另请参见GCC 8手册页。可能已配置或修补了#include <jemalloc/jemalloc.h>,以定义一些libjemalloc符号来代替je_malloc。这样一来,对于LTO来说,在代码中使用malloc会更简单(避免几个je_malloc ld-linux(8)符号之间的冲突)。要了解有关共享库中符号的更多信息,请阅读Drepper的jemalloc(3)论文。当然,您应该期望LTO改变链接的行为!