从C++类创建LLVM字节码

Create LLVM bytecode from C++ classes

本文关键字:字节 LLVM 创建 C++      更新时间:2023-10-16

我正在LLVM中为一种特殊目的语言编写编译器。我想为已经用C++编写的库添加绑定。我的想法是将库编译为LLVM字节码(使用clang -emit-llvm -S abc.c),并在编译过程中链接它。这适用于像这样的代码

// 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;
}

但那将是一项乏味的工作。有什么方法可以从我的库源创建有用的字节码吗?或者以任何其他方式使库在我的编译器中可用?

您可以看到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-Weror编译)
  2. 对于重载,您必须指定难看的强制转换来消除它们的歧义

HTH

如果只想使用几个库函数,则可以使用另一种方法:为您使用的所有函数创建一个包装器。

// wrapper.cc
A* A_create() {
    return new A();
}
// and so on

这样你就不必修改你的库,但这肯定是一些额外的类型。