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>
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. On Wed, Jun 10, 2020 at 10:01 AM Mukul Rathi via llvm-dev < llvm-dev at lists.llvm.org> wrote:> 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_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 > > > > > > > _______________________________________________ > LLVM Developers mailing list > llvm-dev at lists.llvm.org > https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev >-------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20200610/d9e0a4b7/attachment.html>
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> On 10 Jun 2020, at 18:35, David Blaikie <dblaikie at gmail.com> wrote: > > 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. > > On Wed, Jun 10, 2020 at 10:01 AM Mukul Rathi via llvm-dev <llvm-dev at lists.llvm.org <mailto:llvm-dev at lists.llvm.org>> wrote: > 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 > > > > > > > _______________________________________________ > LLVM Developers mailing list > llvm-dev at lists.llvm.org <mailto:llvm-dev at lists.llvm.org> > https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev <https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev>-------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20200610/7b4654ed/attachment.html>