clang:使用 O3 导出隐式实例化函数的符号
clang: export symbols of implicitly instantiated functions with O3
TL,DR: 即使 -O3 处于活动状态,我如何强制 clang 导出隐式实例化函数的符号?
让我们看下面的代码:
#include <iostream>
#include <llvm/Support/DynamicLibrary.h>
#include <llvm/ExecutionEngine/ExecutionEngine.h>
#include <llvm/ExecutionEngine/RTDyldMemoryManager.h>
template <typename T>
__attribute__((noinline))
int twice(const T& t) {
return t * 2;
}
int thrice(const int& t) {
return t * 3;
}
int main() {
std::cout << twice(5) << std::endl;
std::cout << thrice(5) << std::endl;
llvm::sys::DynamicLibrary::LoadLibraryPermanently(nullptr); // Make symbols from current process visible
std::cout << "address of twice: " << llvm::RTDyldMemoryManager::getSymbolAddressInProcess("__Z5twiceIiEiRKT_") << std::endl;
std::cout << "address of thrice: " << llvm::RTDyldMemoryManager::getSymbolAddressInProcess("__Z6thriceRKi") << std::endl;
}
有两个函数,两次和三次。第一个是模板化的,第二个不是。我首先定期给他们打电话,然后尝试使用 libLLVM 获取他们的地址。将其视为超级简化的 JIT 编译器的一部分(它有一个名称中的 mangler)。
使用 clang++ -O0 -I/usr/local/opt/llvm/include -L/usr/local/opt/llvm/lib/ jit.cpp -lLLVM
(OS X 上的 clang 版本 6.0.0),输出符合预期:
10
15
address of twice: 4350763184
address of thrice: 4350762224
如果启用优化,twice
的符号将不再导出,如 nm a.out | grep twice
:
所示
00000001000010b0 T __Z5twiceIiEiRKT_ (with -O0)
00000001000009c0 t __Z5twiceIiEiRKT_ (with -O3)
因此,libLLVM 不再找到函数:
10
15
address of twice: 0
address of thrice: 4315621072
使用 gcc,可以导出符号。
如果我显式实例化它,我可以让 clang 导出符号:
template int twice<int>(const int& t);
然而,这并不是一个真正的选择,因为我们不知道 JIT 引擎将调用哪些实例化。
我知道 ,但它只处理显式实例化。
添加属性used
,像这样:
template <typename T>
__attribute__((used))
int twice(const T& t) {
return t * 2;
}
这将强制 Clang 导出符号。
TL,DR: 即使 -O3 处于活动状态,我如何强制 clang 导出隐式实例化函数的符号?
让我们看下面的代码:
#include <iostream>
#include <llvm/Support/DynamicLibrary.h>
#include <llvm/ExecutionEngine/ExecutionEngine.h>
#include <llvm/ExecutionEngine/RTDyldMemoryManager.h>
template <typename T>
__attribute__((noinline))
int twice(const T& t) {
return t * 2;
}
int thrice(const int& t) {
return t * 3;
}
int main() {
std::cout << twice(5) << std::endl;
std::cout << thrice(5) << std::endl;
llvm::sys::DynamicLibrary::LoadLibraryPermanently(nullptr); // Make symbols from current process visible
std::cout << "address of twice: " << llvm::RTDyldMemoryManager::getSymbolAddressInProcess("__Z5twiceIiEiRKT_") << std::endl;
std::cout << "address of thrice: " << llvm::RTDyldMemoryManager::getSymbolAddressInProcess("__Z6thriceRKi") << std::endl;
}
有两个函数,两次和三次。第一个是模板化的,第二个不是。我首先定期给他们打电话,然后尝试使用 libLLVM 获取他们的地址。将其视为超级简化的 JIT 编译器的一部分(它有一个名称中的 mangler)。
使用 clang++ -O0 -I/usr/local/opt/llvm/include -L/usr/local/opt/llvm/lib/ jit.cpp -lLLVM
(OS X 上的 clang 版本 6.0.0),输出符合预期:
10
15
address of twice: 4350763184
address of thrice: 4350762224
如果启用优化,twice
的符号将不再导出,如 nm a.out | grep twice
:
00000001000010b0 T __Z5twiceIiEiRKT_ (with -O0)
00000001000009c0 t __Z5twiceIiEiRKT_ (with -O3)
因此,libLLVM 不再找到函数:
10
15
address of twice: 0
address of thrice: 4315621072
使用 gcc,可以导出符号。
如果我显式实例化它,我可以让 clang 导出符号:
template int twice<int>(const int& t);
然而,这并不是一个真正的选择,因为我们不知道 JIT 引擎将调用哪些实例化。
我知道
添加属性used
,像这样:
template <typename T>
__attribute__((used))
int twice(const T& t) {
return t * 2;
}
这将强制 Clang 导出符号。