从C++类创建LLVM字节码

7

我正在使用LLVM编写特定目的语言的编译器。我想为一个已经用C++编写的库添加绑定。我的想法是使用clang -emit-llvm -S abc.c将库编译为LLVM字节码,并在编译期间链接它。这对于像下面这样的代码非常有效:

// lib.c
int f() {
    return 123;
}

但是图书馆的某些部分写得像

// A.cc
class A {
    public:
        int f() { return 123; }
};

这会导致空的字节码文件。我知道可以通过分离实现来解决这个问题:
// A.cc
class A {
    public:
        int f();
};

int A::f() {
    return 123;
}

但是这将是许多枯燥无味的工作。有没有办法从我的库源代码中创建有用的字节码?或者有其他方法使该库在我的编译器中可用?


你的第一个例子是源文件,而第二个是头文件。也许这就是问题所在?你尝试过将所有头文件包含在某个虚拟源文件中并编译吗? - Björn Pollex
@Space_C0wb0y:实际上这两者之间没有任何区别,它们都会被处理并编译成结果。 - Arafangion
你是对的Space_c0wb0y,如果我编译.hpp文件,我会得到一个预处理头文件(我想这有点糟糕)。然而,如果我把代码放在一个名为A.cc的文件中,clang就会产生所描述的输出。我会修改这个例子。 - Thomas Schaub
2个回答

6
你可以查看clang是否尊重显式模板实例化的外部链接性。这可能适用于非模板,但对于模板,你可以“强制”它工作。
简单概述: lib1.h
template <typename T=int>
struct ATemplate { T f() { return 123; } };

添加一个文件lib1_instantiate.cpp

#include "lib1.h"
template struct ATemplate<int>;
template struct ATemplate<unsigned int>;
template struct ATemplate<long>; // etc.

这应该实例化具有外部链接的命名模板。

如果您使用的是非模板类,并且上述方法对其无效,则可以进行如下包装:

instantiate.cpp:

namespace hidden_details
{
    template <class libtype> struct instantiator : public libtype 
    // derives... just do something that requires a complete type (not a forward!)
    { };
}

template struct hidden_details::instantiator<A>;

如果你不幸的话,你需要“使用”这些内联成员来获得外部链接。一个常见的技巧是使用这些成员的地址(你不需要实现委托的东西):
instantiate.cpp:
static void force_use_A()
{
    void* unused = (void*) &A::f;
}

然而

  1. 将其转换为(void*)会导致未定义行为(在gcc上无法使用-pedantic -Werror编译此代码)
  2. 对于重载函数,您需要指定丑陋的转换来消除歧义

希望能够帮到你


第一个版本运行良好,但是为什么你要实例化A三次? - Thomas Schaub
不知道你所说的第一个版本是什么意思。此外,我实例化了三个不同的模板实例;它们是不同的!关键是,我的第一个示例与你问题中的A结构不同;我想演示如何使用模板类完成它。(在我的答案中将使用不同的结构名称进行消歧义) - sehe
我所说的第一个版本是指第一种选择(即具有模板实例化的那个)。你的结构体的新名称澄清了事情,我原以为你所做的是对我的示例进行精确翻译。谢谢! - Thomas Schaub

0
一个替代方案是,如果只想使用几个库函数:为你使用的一切创建一个包装器。
// wrapper.cc
A* A_create() {
    return new A();
}

// and so on

这样你就不必修改你的库,但肯定需要多打一些字。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接