如果某个TU中没有任何函数被另一个TU显式调用,LTO会优化掉其中的一些全局对象。
以下摘录试图描述相关的关键类和文件(请注意,此处仅为示例目的,有些地方可能不完全准确):
我有一个名为Registrar
的单例类,它维护了所有类型为Foo
的对象列表。为避免静态构造顺序错误,当第一个Foo
对象被构造时,我动态地构造此对象的实例。
// Registrar.hpp
class Registrar
{
public:
static Registrar * sRegistrar;
std::vector<Foo *> objectList;
Registrar() = default;
};
接下来,我们有
Foo
类。该类的实例如上所述向 Registrar
注册。// Foo.hpp
class Foo
{
public:
Foo()
{
if (Registrar::sRegistrar == nullptr)
Registrar::sRegistrar = new Registrar();
Registrar::sRegistrar->objectList.push_back(this);
}
};
< p > Foo
的实例是全局的,可以从多个文件中创建。在其中一个文件中,我们定义了另一个函数,它会从其他地方调用:
// file1.hpp
void someFunctionThatIsCalledExplicitly()
{
doSomething();
}
namespace
{
__attribute__((used, retain))
Foo f1;
}
但在另一个文件中,我们只创建了一个Foo
实例:
// file2.hpp
namespace
{
__attribute__((used, retain))
Foo f2;
}
我看到的是,尽管在所有声明
Foo
类时添加了__attribute__((used, retain))
,f1
并没有被优化掉,而f2
被优化掉了。我该如何防止LTO优化掉这些实例? 为什么属性没有起作用?
编辑: 我能够编写一个小示例以重现此问题。
1. main.cpp:
#include <iostream>
#include "Registrar.hpp"
#ifdef FORCE_LINKAGE
extern int i;
#endif
extern void someFunctionThatIsCalledExplicitly();
int main()
{
#ifdef FORCE_LINKAGE
i++;
#endif
someFunctionThatIsCalledExplicitly();
if (Registrar::sRegistrar == nullptr)
{
std::cout << "No instances of foo";
}
else
{
std::cout << Registrar::sRegistrar->objectList.size() << " instances of foo\n";
}
return 0;
}
- Foo.hpp
#pragma once
class Foo
{
public:
Foo();
};
- Foo.cpp:
#include "Foo.hpp"
#include "Registrar.hpp"
Foo::Foo()
{
if (Registrar::sRegistrar == nullptr)
{
Registrar::sRegistrar = new Registrar();
}
Registrar::sRegistrar->objectList.push_back(this);
}
- Registrar.hpp:
#pragma once
#include <vector>
#include "Foo.hpp"
class Registrar
{
public:
static Registrar * sRegistrar;
std::vector<Foo *> objectList;
Registrar() = default;
};
- Registrar.cpp:
#include "Registrar.hpp"
Registrar * Registrar::sRegistrar = nullptr;
- File1.cpp:
#include <iostream>
#include "Foo.hpp"
void someFunctionThatIsCalledExplicitly()
{
std::cout << "someFunctionThatIsCalledExplicitly() called\n";
}
namespace
{
__attribute__((used, retain))
Foo f1;
}
- File2.cpp:
#include "Foo.hpp"
#ifdef FORCE_LINKAGE
int i = 0;
#endif
namespace
{
__attribute__((used, retain))
Foo f2;
}
- Makefile:
CC = clang++
LIBTOOL = libtool
BUILDDIR = build
BINFILE = lto
BUILDFLAGS = -flto -std=c++17
LINKFLAGS = -flto
.PHONY: all
all: $(BUILDDIR) $(BINFILE)
.PHONY: force
force: def all
.PHONY: def
def:
$(eval BUILDFLAGS += -DFORCE_LINKAGE)
$(BINFILE): foo files
$(CC) -o $(BUILDDIR)/$@ $(LINKFLAGS) -L$(BUILDDIR) $(addprefix -l, $^)
foo: Foo.o main.o Registrar.o
$(LIBTOOL) $(STATIC) -o $(BUILDDIR)/lib$@.a $(addprefix $(BUILDDIR)/, $^)
files: File1.o File2.o
$(LIBTOOL) $(STATIC) -o $(BUILDDIR)/lib$@.a $(addprefix $(BUILDDIR)/, $^)
%.o: %.cpp
$(CC) $(BUILDFLAGS) -c -o $(addprefix $(BUILDDIR)/, $@) $<
.PHONY: $(BUILDDIR)
$(BUILDDIR):
mkdir -p $(BUILDDIR)
.PHONY: clean
clean:
rm -rf $(BUILDDIR)
我有两个变量,一个类似于上面的情况(我只看到1个实例),另一个通过声明一个全局变量来强制链接,在其他地方我会引用它(在这种情况下我看到两个实例):
$ make
$ ./build/lto
someFunctionThatIsCalledExplicitly() called
1 instances of foo
$ make force
$ ./build/lto
someFunctionThatIsCalledExplicitly() called
2 instances of foo
f2
的构造函数应该运行并且具有副作用。因此,它不应该被省略。我认为任何允许省略构造的规则,如RVO,在这里都不适用。 - TrentP