Bill Wendling
2012-Apr-09 05:40 UTC
[LLVMdev] Catching C++ exceptions, cleaning up, rethrowing
On Apr 8, 2012, at 8:47 AM, Paul J. Lucas wrote:> On Apr 8, 2012, at 4:20 AM, Bill Wendling wrote: > >> On Apr 4, 2012, at 9:32 PM, Paul J. Lucas wrote: >> >>> This all seems to work just fine. I can throw a C++ exception either in a C++ object's constructor or in an ordinary member function and the stack unwinds correctly (the object's destructors are called) and the exception is propagated back up the C++ code that called the JIT'd code. >>> >>> So is this the right way to do this? Am I missing anything? >> >> This looks like roughly what I would expect the code to be. I'd have to see the LLVM IR it generated to be sure. But it looks okay to me. (And if it's working for you, all the better. ;-) ) > > A snippet of the IR code is: >The code is good, except for one comment (see below).>> entry: >> %add_it = alloca %add_iterator, align 8 >> %y_it = alloca %singleton_iterator, align 8 >> %x_it = alloca %singleton_iterator, align 8 >> %y = alloca %item, align 8 >> %x = alloca %item, align 8 >> %exception_caught_flag = alloca i8, align 1 >> store i8 0, i8* %exception_caught_flag, align 1 >> %caught_result_storage = alloca %0, align 8 >> store %0 zeroinitializer, %0* %caught_result_storage, align 8 >> %0 = bitcast %item* %x to void* >> invoke void @thunk_item_M_new_i(void* %0, i32 1) >> to label %normal unwind label %unwind >> >> exit: ; preds = %unwind, %dtor >> %1 = phi %0 [ %5, %unwind ], [ %6, %dtor ] >> %2 = phi i8 [ 1, %unwind ], [ %7, %dtor ] >> %3 = icmp eq i8 %2, 0 >> br i1 %3, label %return, label %unwind_resume >> >> return: ; preds = %exit >> ret void >> >> unwind_resume: ; preds = %exit >> resume %0 %1 >> >> normal: ; preds = %entry >> %4 = bitcast %item* %y to void* >> invoke void @thunk_item_M_new_i(void* %4, i32 2) >> to label %normal1 unwind label %unwind2 >> >> unwind: ; preds = %entry >> %5 = landingpad %0 personality i32 (...)* @__gxx_personality_v0 >> cleanup >> store %0 %5, %0* %caught_result_storage, align 8What gets returned by the landingpad instruction (%0 here) is normally a structure. LLVM doesn't typically treat aggregates as first-class citizens. In particular, you shouldn't store the whole structure to memory like you do to %5. You can use 'extractvalue' to get the different elements of the structure. If you need to reconstitute the structure (for the 'resume' instruction), you can use the 'insertvalue' instruction. The code it produces is more verbose, but after optimizations, code-gen typically doesn't produce a lot of code for these sequences.>> store i8 1, i8* %exception_caught_flag, align 1 >> br label %exit >> >> dtor: ; preds = %unwind2, %dtor3 >> %6 = phi %0 [ %9, %unwind2 ], [ %10, %dtor3 ] >> %7 = phi i8 [ 1, %unwind2 ], [ %11, %dtor3 ] >> call void @thunk_item_M_delete(void* %0) >> br label %exit >> >> normal1: ; preds = %normal >> %8 = bitcast %singleton_iterator* %x_it to void* >> invoke void @thunk_singleton_iterator_M_new(void* %8, void* %0) >> to label %normal4 unwind label %unwind5 >> >> ; ... > > I generate the "exit", "return", and "unwind_resume" blocks first followed by a sequence of "normal", "unwind", and "dtor" blocks. A dtorN block calls dtorN-1 unless N=0 in which case it calls exit instead. > > Now I think my last part of the puzzle is the personality function. Running on a *nix system (Mac OS X), my code has: > >> Function* getPersonalityFunction( Module *m ) { >> if ( Function *const f = m->getFunction( "__gxx_personality_v0" ) ) >> return f; >> return Function::Create( >> FunctionType::get( Type::getInt32Ty( m->getContext() ), true ), >> Function::ExternalLinkage, >> "__gxx_personality_v0", >> m >> ); >> } > > for the personality function and it works. But I assume that works only for a *nix/g++ system. My code also needs to work when compiled on Windows using Visual Studio and I'm guessing that it needs a different personality function. Where do I get that? >That's not something I'm familiar with. You may be able to use the same personality function, but I know that Windows also uses SEH for its exception handling scheme. This is not supported by LLVM. My suggestion is to use clang to compile a program with exceptions on your Windows box and then use the personality function that it generates.> The code in ExceptionDemo.cpp doesn't say whether it's cross-platform. Is it? Would I instead need to copy/past all that code for the personality function? >The code in ExceptionDemo.cpp is creating and using its own personality function. In that way, it's cross-platform (though I haven't personally tried it on Windows). I would say that you don't need most of it. The personality function is supplied to you via the unwinding runtime. -bw
Paul J. Lucas
2012-Apr-09 15:50 UTC
[LLVMdev] Catching C++ exceptions, cleaning up, rethrowing
On Apr 8, 2012, at 10:40 PM, Bill Wendling wrote:> What gets returned by the landingpad instruction (%0 here) is normally a structure. LLVM doesn't typically treat aggregates as first-class citizens. In particular, you shouldn't store the whole structure to memory like you do to %5. You can use 'extractvalue' to get the different elements of the structure. If you need to reconstitute the structure (for the 'resume' instruction), you can use the 'insertvalue' instruction. The code it produces is more verbose, but after optimizations, code-gen typically doesn't produce a lot of code for these sequences.I copied that bit of code from ExceptionDemo.cpp (lines 1270-1290):> llvm::LandingPadInst *caughtResult > builder.CreateLandingPad(ourCaughtResultType, > personality, > numExceptionsToCatch, > "landingPad"); > // ... > > builder.CreateStore(caughtResult, caughtResultStorage);Then on line 1255:> builder.CreateResume(builder.CreateLoad(caughtResultStorage));Are you saying that code is wrong too? - Paul
Duncan Sands
2012-Apr-09 16:14 UTC
[LLVMdev] Catching C++ exceptions, cleaning up, rethrowing
Hi,>> What gets returned by the landingpad instruction (%0 here) is normally a structure. LLVM doesn't typically treat aggregates as first-class citizens.registers of aggregate type are legal and work. For example the dragonegg frontend represents complex numbers as registers with type { float, float }, and it works fine. However not many advanced optimizations are performed on them. In particular, you shouldn't store the whole structure to memory like you do to %5. It is fine to store them to memory and load them out again. The dragonegg frontend does this all the time for complex numbers. You can use 'extractvalue' to get the different elements of the structure. If you need to reconstitute the structure (for the 'resume' instruction), you can use the 'insertvalue' instruction. The code it produces is more verbose, but after optimizations, code-gen typically doesn't produce a lot of code for these sequences. Indeed using extractvalue and insertvalue work fine too. But it's not obligatory. Ciao, Duncan.> > I copied that bit of code from ExceptionDemo.cpp (lines 1270-1290): > >> llvm::LandingPadInst *caughtResult >> builder.CreateLandingPad(ourCaughtResultType, >> personality, >> numExceptionsToCatch, >> "landingPad"); >> // ... >> >> builder.CreateStore(caughtResult, caughtResultStorage); > > Then on line 1255: > >> builder.CreateResume(builder.CreateLoad(caughtResultStorage)); > > Are you saying that code is wrong too? > > - Paul > > > > _______________________________________________ > LLVM Developers mailing list > LLVMdev at cs.uiuc.edu http://llvm.cs.uiuc.edu > http://lists.cs.uiuc.edu/mailman/listinfo/llvmdev
Paul J. Lucas
2012-Apr-11 15:41 UTC
[LLVMdev] Catching C++ exceptions, cleaning up, rethrowing
On Apr 8, 2012, at 10:40 PM, Bill Wendling wrote:> On Apr 8, 2012, at 8:47 AM, Paul J. Lucas wrote: > >> But I assume [the personality function] works only for a *nix/g++ system. My code also needs to work when compiled on Windows using Visual Studio and I'm guessing that it needs a different personality function. Where do I get that? > > That's not something I'm familiar with. You may be able to use the same personality function, but I know that Windows also uses SEH for its exception handling scheme. This is not supported by LLVM. > > My suggestion is to use clang to compile a program with exceptions on your Windows box and then use the personality function that it generates.Assuming I do that and it works, wouldn't IR code generated on Windows then be incompatible with IR-code-compiled-to-machine-code on *nix (because it would use the wrong personality function)? Doing all the grunt work of emitting landingpads, resumes, having to get the right personality function, etc., seems a lot more work that my original proposal of catching C++ exceptions in my C thunk functions and calling a linked-list of destructors to run myself. This also has the benefit of not having to deal with platform-specific personality functions. I realize I'm a newb and there may be something subtle I'm not getting, but I have to ask: why is doing the work in IR "better"? - Paul
Jim Grosbach
2012-Apr-11 17:20 UTC
[LLVMdev] Catching C++ exceptions, cleaning up, rethrowing
On Apr 11, 2012, at 8:41 AM, Paul J. Lucas <paul at lucasmail.org> wrote:> On Apr 8, 2012, at 10:40 PM, Bill Wendling wrote: > >> On Apr 8, 2012, at 8:47 AM, Paul J. Lucas wrote: >> >>> But I assume [the personality function] works only for a *nix/g++ system. My code also needs to work when compiled on Windows using Visual Studio and I'm guessing that it needs a different personality function. Where do I get that? >> >> That's not something I'm familiar with. You may be able to use the same personality function, but I know that Windows also uses SEH for its exception handling scheme. This is not supported by LLVM. >> >> My suggestion is to use clang to compile a program with exceptions on your Windows box and then use the personality function that it generates. > > Assuming I do that and it works, wouldn't IR code generated on Windows then be incompatible with IR-code-compiled-to-machine-code on *nix (because it would use the wrong personality function)?IR is not portable.> > Doing all the grunt work of emitting landingpads, resumes, having to get the right personality function, etc., seems a lot more work that my original proposal of catching C++ exceptions in my C thunk functions and calling a linked-list of destructors to run myself. This also has the benefit of not having to deal with platform-specific personality functions. > > I realize I'm a newb and there may be something subtle I'm not getting, but I have to ask: why is doing the work in IR "better"? > > - Paul > > > _______________________________________________ > LLVM Developers mailing list > LLVMdev at cs.uiuc.edu http://llvm.cs.uiuc.edu > http://lists.cs.uiuc.edu/mailman/listinfo/llvmdev
Bill Wendling
2012-Apr-12 05:50 UTC
[LLVMdev] Catching C++ exceptions, cleaning up, rethrowing
On Apr 11, 2012, at 8:41 AM, Paul J. Lucas wrote:> On Apr 8, 2012, at 10:40 PM, Bill Wendling wrote: > >> On Apr 8, 2012, at 8:47 AM, Paul J. Lucas wrote: >> >>> But I assume [the personality function] works only for a *nix/g++ system. My code also needs to work when compiled on Windows using Visual Studio and I'm guessing that it needs a different personality function. Where do I get that? >> >> That's not something I'm familiar with. You may be able to use the same personality function, but I know that Windows also uses SEH for its exception handling scheme. This is not supported by LLVM. >> >> My suggestion is to use clang to compile a program with exceptions on your Windows box and then use the personality function that it generates. > > Assuming I do that and it works, wouldn't IR code generated on Windows then be incompatible with IR-code-compiled-to-machine-code on *nix (because it would use the wrong personality function)? >I suggested this only so that you can then copy what personality function declaration it uses. :-)> Doing all the grunt work of emitting landingpads, resumes, having to get the right personality function, etc., seems a lot more work that my original proposal of catching C++ exceptions in my C thunk functions and calling a linked-list of destructors to run myself. This also has the benefit of not having to deal with platform-specific personality functions. > > I realize I'm a newb and there may be something subtle I'm not getting, but I have to ask: why is doing the work in IR "better"? >What you seem to be proposing is code that's already handled via the personality function and the unwind library. I'm trying to save you from duplicating that effort. But I also can't stop you from going along with your scheme. Keep in mind that catching a C++ exception is a non-trivial matter. You would have to ensure that it has the same semantics as the existing DWARF implementation. E.g., running cleanups on all of the functions up the stack from where it was thrown to where it was caught (a two-phase process). And that's just the tip of the iceberg. It *sounds* like I'm asking you to do a lot, but that's the nature of IR. You should eventually find that it isn't a lot of work to implement. :-) -bw
Reasonably Related Threads
- [LLVMdev] Catching C++ exceptions, cleaning up, rethrowing
- [LLVMdev] Catching C++ exceptions, cleaning up, rethrowing
- [LLVMdev] Catching C++ exceptions, cleaning up, rethrowing
- [LLVMdev] Fwd: error while linking modules with exception handling demo code
- [LLVMdev] Catching C++ exceptions, cleaning up, rethrowing