[llvm-dev] Implementing a VTable in LLVM

Mukul Rathi via llvm-dev llvm-dev at lists.llvm.org
Tue Jun 9 15:52:18 PDT 2020


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 <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 <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 <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 <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 <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_cast<llvm::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






-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20200609/ef40b282/attachment-0001.html>


More information about the llvm-dev mailing list