Thomas Schaub
Thomas Schaub

Reputation: 418

Create LLVM bytecode from C++ classes

I'm writing a compiler for a special purpose language in LLVM. I want to add bindings for a library that is already written in C++. My idea is to compile the library to LLVM bytecode (using clang -emit-llvm -S abc.c) and link it during compilation. This works well for code like

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

But parts of the library are written like

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

Which results in empty bytecode files. I know that I can fix this by separating the implementation:

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

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

But that would be a lot of tedious work. Is there any way to create useful bytecode from my library sources as they are? Or any other way to make the library available in my compiler?

Upvotes: 7

Views: 1561

Answers (2)

Thomas Schaub
Thomas Schaub

Reputation: 418

An alternative which works if one only wants to use a few library functions: Create a wrapper for everything that you use.

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

// and so on

This way you don't have to modify your library, but it's definitely some extra typing.

Upvotes: 0

sehe
sehe

Reputation: 393769

You could see whether clang honours external linkage for explicit template instantiations. This might apply to non-templates, but otherwise you could 'force it' to work for templates.

Simple synopsis:

lib1.h

template <typename T=int>
struct ATemplate { T f() { return 123; } };

add a file lib1_instantiate.cpp

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

This should instantiate the named templates with external linkage.

If you're stuck with a non-template class, and the trick above doesn't work for that, you might wrap it like so:

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

If you're out of luck you'll have to 'use' the inline members for them to get external linkage. A common trick is to use the address of these members (you won't need to implement delegating stuff):

instantiate.cpp:

static void force_use_A()
{
    void* unused = (void*) &A::f;
}

However

  1. the conversion to (void*) invokes undefined behaviour (you can't compile that with -pedantic -Werror on gcc)
  2. for overloads you'll have to specify ugly casts to disambiguate them

HTH

Upvotes: 6

Related Questions