Implementing a VTable in LLVM

Hi all,

I’ve been working on a Java-esque object-oriented language Bolt that targets LLVM IR. I’m agonisingly close to getting a virtual table working, and was hoping one of you could point out the gap in my understanding. I’ve linked the C++ code snippets relevant to the vtable below.

Example IR generated (partially displayed below): https://github.com/mukul-rathi/bolt/blob/vtable/examples/vtable/foo.ll
And the Bolt source code that compiles to it- https://github.com/mukul-rathi/bolt/blob/vtable/examples/vtable/foo.bolt

I currently have code (https://github.com/mukul-rathi/bolt/blob/vtable/src/llvm-backend/llvm_ir_codegen/class_codegen.cc#L57:L77)
that generates the following global Vtable for a class Foo:

%_VtableFoo = type { void (%Foo*, i32)* }

%Foo = type { %_VtableFoo*, %pthread_t*, i32, i32, i32, i32 }

@_VtableFoo = common global %_VtableFoo { void (%Foo*, i32)* @_Foo__setgi }

The code for instantiating the table seems to generate the correct IR (create an object, store a pointer to its vtable) - https://github.com/mukul-rathi/bolt/blob/vtable/src/llvm-backend/llvm_ir_codegen/expr_codegen.cc#L66:L89

entry:
%_var_y0 = alloca %Foo*
%0 = call i8* @malloc(i64 ptrtoint (%Foo* getelementptr (%Foo, %Foo* null, i64 1) to i64))
%1 = bitcast i8* %0 to %Foo*
%2 = getelementptr inbounds %Foo, %Foo* %1, i32 0, i32 0
store %_VtableFoo* @_VtableFoo, %_VtableFoo** %2

The issue is when it comes to actually calling the method. Up to this point, the code generates the expected IR (https://github.com/mukul-rathi/bolt/blob/vtable/src/llvm-backend/llvm_ir_codegen/expr_codegen.cc#L188:L202)

%5 = load %Foo*, %Foo** %_var_y0
%6 = getelementptr inbounds %Foo, %Foo* %5, i32 0, i32 0
%7 = load %_VtableFoo*, %_VtableFoo** %6
%8 = getelementptr inbounds %_VtableFoo, %_VtableFoo* %7, i32 0, i32 0
%9 = load void (%Foo*, i32), void (%Foo, i32)** %8

  • |

However, the builder->CreateLoad instruction corresponding to the line with %9 returns null:

llvm::Function *calleeMethod = llvm::dyn_castllvm::Function(builder->CreateLoad(calleeMethodPtr));

So if I then try to execute the following builder->CreateCall function I end up with a segmentation fault since calleeMethod is null:

builder->CreateCall(calleeMethod, argVals);

Under what circumstances would builder->CreateLoad return null? Is the global vTable’s memory not allocated correctly? Or have I been approaching this incorrectly - is there another method in the C++ API that I should be using for indirect function calls?

I’d really appreciate any assistance.

Thanks,

Mukul

I’d bet that CreateLoad is returning non-null but the dyn_cast is returning null.

llvm::Function represents a specific function known at compile-time. You have loaded a function pointer (void (%Foo*, i32)*) not a function itself. You shuold pass that (function pointer) to IRBuilder createCall - no need for the dyn_cast.

Thanks David, that makes sense, I’d hadn’t realised the function had to be known at compile-time.

It now works as expected, thank you!

Mukul