//===-- ExceptionDemo.cpp - An example using llvm Exceptions --------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // Demo program which implements an example LLVM exception implementation, and // shows several test cases including the handling of foreign exceptions. // It is run with type info types arguments to throw. A test will // be run for each given type info type. While type info types with the value // of -1 will trigger a foreign C++ exception to be thrown; type info types // <= 6 and >= 1 will cause the associated generated exceptions to be thrown // and caught by generated test functions; and type info types > 6 // will result in exceptions which pass through to the test harness. All other // type info types are not supported and could cause a crash. In all cases, // the "finally" blocks of every generated test functions will executed // regardless of whether or not that test function ignores or catches the // thrown exception. // // examples: // // ExceptionDemo // // causes a usage to be printed to stderr // // ExceptionDemo 2 3 7 -1 // // results in the following cases: // - Value 2 causes an exception with a type info type of 2 to be // thrown and caught by an inner generated test function. // - Value 3 causes an exception with a type info type of 3 to be // thrown and caught by an outer generated test function. // - Value 7 causes an exception with a type info type of 7 to be // thrown and NOT be caught by any generated function. // - Value -1 causes a foreign C++ exception to be thrown and not be // caught by any generated function // // Cases -1 and 7 are caught by a C++ test harness where the validity of // of a C++ catch(...) clause catching a generated exception with a // type info type of 7 is explained by: example in rules 1.6.4 in // http://mentorembedded.github.com/cxx-abi/abi-eh.html (v1.22) // // This code uses code from the llvm compiler-rt project and the llvm // Kaleidoscope project. // //===----------------------------------------------------------------------===// #include "llvm/ADT/STLExtras.h" #include "llvm/IR/Verifier.h" #include "llvm/ExecutionEngine/MCJIT.h" #include "llvm/ExecutionEngine/SectionMemoryManager.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/Intrinsics.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/LegacyPassManager.h" #include "llvm/IR/Module.h" #include "llvm/Support/Dwarf.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Target/TargetOptions.h" #include "llvm/Transforms/Scalar.h" // FIXME: Although all systems tested with (Linux, OS X), do not need this // header file included. A user on ubuntu reported, undefined symbols // for stderr, and fprintf, and the addition of this include fixed the // issue for them. Given that LLVM's best practices include the goal // of reducing the number of redundant header files included, the // correct solution would be to find out why these symbols are not // defined for the system in question, and fix the issue by finding out // which LLVM header file, if any, would include these symbols. #include <cstdio> #include <sstream> #include <stdexcept> #include <inttypes.h> #ifndef USE_GLOBAL_STR_CONSTS #define USE_GLOBAL_STR_CONSTS true #endif // System C++ ABI unwind types from: // http://mentorembedded.github.com/cxx-abi/abi-eh.html (v1.22) extern "C" { typedef enum { _URC_NO_REASON = 0, _URC_FOREIGN_EXCEPTION_CAUGHT = 1, _URC_FATAL_PHASE2_ERROR = 2, _URC_FATAL_PHASE1_ERROR = 3, _URC_NORMAL_STOP = 4, _URC_END_OF_STACK = 5, _URC_HANDLER_FOUND = 6, _URC_INSTALL_CONTEXT = 7, _URC_CONTINUE_UNWIND = 8 } _Unwind_Reason_Code; typedef enum { _UA_SEARCH_PHASE = 1, _UA_CLEANUP_PHASE = 2, _UA_HANDLER_FRAME = 4, _UA_FORCE_UNWIND = 8, _UA_END_OF_STACK = 16 } _Unwind_Action; struct _Unwind_Exception; typedef void (*_Unwind_Exception_Cleanup_Fn) (_Unwind_Reason_Code, struct _Unwind_Exception *); struct _Unwind_Exception { uint64_t exception_class; _Unwind_Exception_Cleanup_Fn exception_cleanup; uintptr_t private_1; uintptr_t private_2; // @@@ The IA-64 ABI says that this structure must be double-word aligned. // Taking that literally does not make much sense generically. Instead // we provide the maximum alignment required by any type for the machine. } __attribute__((__aligned__)); struct _Unwind_Context; typedef struct _Unwind_Context *_Unwind_Context_t; extern const uint8_t *_Unwind_GetLanguageSpecificData (_Unwind_Context_t c); extern uintptr_t _Unwind_GetGR (_Unwind_Context_t c, int i); extern void _Unwind_SetGR (_Unwind_Context_t c, int i, uintptr_t n); extern void _Unwind_SetIP (_Unwind_Context_t, uintptr_t new_value); extern uintptr_t _Unwind_GetIP (_Unwind_Context_t context); extern uintptr_t _Unwind_GetRegionStart (_Unwind_Context_t context); } // extern "C" // // Example types // /// This is our simplistic type info struct OurExceptionType_t { /// type info type int type; }; /// This is our Exception class which relies on a negative offset to calculate /// pointers to its instances from pointers to its unwindException member. /// /// Note: The above unwind.h defines struct _Unwind_Exception to be aligned /// on a double word boundary. This is necessary to match the standard: /// http://mentorembedded.github.com/cxx-abi/abi-eh.html struct OurBaseException_t { struct OurExceptionType_t type; // Note: This is properly aligned in unwind.h struct _Unwind_Exception unwindException; }; // Note: Not needed since we are C++ typedef struct OurBaseException_t OurException; typedef struct _Unwind_Exception OurUnwindException; // // Various globals used to support typeinfo and generatted exceptions in // general // static std::map<std::string, llvm::Value*> namedValues; int64_t ourBaseFromUnwindOffset; const unsigned char ourBaseExcpClassChars[] = {'o', 'b', 'j', '\0', 'b', 'a', 's', '\0'}; static uint64_t ourBaseExceptionClass = 0; static std::vector<std::string> ourTypeInfoNames; static std::map<int, std::string> ourTypeInfoNamesIndex; static llvm::StructType *ourTypeInfoType; static llvm::StructType *ourCaughtResultType; static llvm::StructType *ourExceptionType; static llvm::StructType *ourUnwindExceptionType; static llvm::ConstantInt *ourExceptionNotThrownState; static llvm::ConstantInt *ourExceptionThrownState; static llvm::ConstantInt *ourExceptionCaughtState; typedef std::vector<std::string> ArgNames; typedef std::vector<llvm::Type*> ArgTypes; // // Code Generation Utilities // /// Utility used to create a function, both declarations and definitions /// @param module for module instance /// @param retType function return type /// @param theArgTypes function's ordered argument types /// @param theArgNames function's ordered arguments needed if use of this /// function corresponds to a function definition. Use empty /// aggregate for function declarations. /// @param functName function name /// @param linkage function linkage /// @param declarationOnly for function declarations /// @param isVarArg function uses vararg arguments /// @returns function instance llvm::Function *createFunction(llvm::Module &module, llvm::Type *retType, const ArgTypes &theArgTypes, const ArgNames &theArgNames, const std::string &functName, llvm::GlobalValue::LinkageTypes linkage, bool declarationOnly, bool isVarArg) { llvm::FunctionType *functType = llvm::FunctionType::get(retType, theArgTypes, isVarArg); llvm::Function *ret = llvm::Function::Create(functType, linkage, functName, &module); if (!ret || declarationOnly) return(ret); namedValues.clear(); unsigned i = 0; for (llvm::Function::arg_iterator argIndex = ret->arg_begin(); i != theArgNames.size(); ++argIndex, ++i) { argIndex->setName(theArgNames[i]); namedValues[theArgNames[i]] = argIndex; } return(ret); } /// Create an alloca instruction in the entry block of /// the parent function. This is used for mutable variables etc. /// @param function parent instance /// @param varName stack variable name /// @param type stack variable type /// @param initWith optional constant initialization value /// @returns AllocaInst instance static llvm::AllocaInst *createEntryBlockAlloca(llvm::Function &function, const std::string &varName, llvm::Type *type, llvm::Constant *initWith = 0) { llvm::BasicBlock &block = function.getEntryBlock(); llvm::IRBuilder<> tmp(&block, block.begin()); llvm::AllocaInst *ret = tmp.CreateAlloca(type, 0, varName.c_str()); if (initWith) tmp.CreateStore(initWith, ret); return(ret); } // // Code Generation Utilities End // // // Runtime C Library functions // // Note: using an extern "C" block so that static functions can be used extern "C" { // Note: Better ways to decide on bit width // /// Prints a 32 bit number, according to the format, to stderr. /// @param intToPrint integer to print /// @param format printf like format to use when printing void print32Int(int intToPrint, const char *format) { if (format) { // Note: No NULL check fprintf(stderr, format, intToPrint); } else { // Note: No NULL check fprintf(stderr, "::print32Int(...):NULL arg.\n"); } } // Note: Better ways to decide on bit width // /// Prints a 64 bit number, according to the format, to stderr. /// @param intToPrint integer to print /// @param format printf like format to use when printing void print64Int(long int intToPrint, const char *format) { if (format) { // Note: No NULL check fprintf(stderr, format, intToPrint); } else { // Note: No NULL check fprintf(stderr, "::print64Int(...):NULL arg.\n"); } } /// Prints a C string to stderr /// @param toPrint string to print void printStr(char *toPrint) { if (toPrint) { fprintf(stderr, "%s", toPrint); } else { fprintf(stderr, "::printStr(...):NULL arg.\n"); } } /// Deletes the true previously allocated exception whose address /// is calculated from the supplied OurBaseException_t::unwindException /// member address. Handles (ignores), NULL pointers. /// @param expToDelete exception to delete void deleteOurException(OurUnwindException *expToDelete) { #ifdef DEBUG fprintf(stderr, "deleteOurException(...).\n"); #endif if (expToDelete && (expToDelete->exception_class == ourBaseExceptionClass)) { free(((char*) expToDelete) + ourBaseFromUnwindOffset); } } /// This function is the struct _Unwind_Exception API mandated delete function /// used by foreign exception handlers when deleting our exception /// (OurException), instances. /// @param reason See @link http://mentorembedded.github.com/cxx-abi/abi-eh.html /// @unlink /// @param expToDelete exception instance to delete void deleteFromUnwindOurException(_Unwind_Reason_Code reason, OurUnwindException *expToDelete) { #ifdef DEBUG fprintf(stderr, "deleteFromUnwindOurException(...).\n"); #endif deleteOurException(expToDelete); } /// Creates (allocates on the heap), an exception (OurException instance), /// of the supplied type info type. /// @param type type info type OurUnwindException *createOurException(int type) { size_t size = sizeof(OurException); OurException *ret = (OurException*) memset(malloc(size), 0, size); (ret->type).type = type; (ret->unwindException).exception_class = ourBaseExceptionClass; (ret->unwindException).exception_cleanup = deleteFromUnwindOurException; return(&(ret->unwindException)); } /// Read a uleb128 encoded value and advance pointer /// See Variable Length Data in: /// @link http://dwarfstd.org/Dwarf3.pdf @unlink /// @param data reference variable holding memory pointer to decode from /// @returns decoded value static uintptr_t readULEB128(const uint8_t **data) { uintptr_t result = 0; uintptr_t shift = 0; unsigned char byte; const uint8_t *p = *data; do { byte = *p++; result |= (byte & 0x7f) << shift; shift += 7; } while (byte & 0x80); *data = p; return result; } /// Read a sleb128 encoded value and advance pointer /// See Variable Length Data in: /// @link http://dwarfstd.org/Dwarf3.pdf @unlink /// @param data reference variable holding memory pointer to decode from /// @returns decoded value static uintptr_t readSLEB128(const uint8_t **data) { uintptr_t result = 0; uintptr_t shift = 0; unsigned char byte; const uint8_t *p = *data; do { byte = *p++; result |= (byte & 0x7f) << shift; shift += 7; } while (byte & 0x80); *data = p; if ((byte & 0x40) && (shift < (sizeof(result) << 3))) { result |= (~0 << shift); } return result; } unsigned getEncodingSize(uint8_t Encoding) { if (Encoding == llvm::dwarf::DW_EH_PE_omit) return 0; switch (Encoding & 0x0F) { case llvm::dwarf::DW_EH_PE_absptr: return sizeof(uintptr_t); case llvm::dwarf::DW_EH_PE_udata2: return sizeof(uint16_t); case llvm::dwarf::DW_EH_PE_udata4: return sizeof(uint32_t); case llvm::dwarf::DW_EH_PE_udata8: return sizeof(uint64_t); case llvm::dwarf::DW_EH_PE_sdata2: return sizeof(int16_t); case llvm::dwarf::DW_EH_PE_sdata4: return sizeof(int32_t); case llvm::dwarf::DW_EH_PE_sdata8: return sizeof(int64_t); default: // not supported abort(); } } /// Read a pointer encoded value and advance pointer /// See Variable Length Data in: /// @link http://dwarfstd.org/Dwarf3.pdf @unlink /// @param data reference variable holding memory pointer to decode from /// @param encoding dwarf encoding type /// @returns decoded value static uintptr_t readEncodedPointer(const uint8_t **data, uint8_t encoding) { uintptr_t result = 0; const uint8_t *p = *data; if (encoding == llvm::dwarf::DW_EH_PE_omit) return(result); // first get value switch (encoding & 0x0F) { case llvm::dwarf::DW_EH_PE_absptr: result = *((uintptr_t*)p); p += sizeof(uintptr_t); break; case llvm::dwarf::DW_EH_PE_uleb128: result = readULEB128(&p); break; // Note: This case has not been tested case llvm::dwarf::DW_EH_PE_sleb128: result = readSLEB128(&p); break; case llvm::dwarf::DW_EH_PE_udata2: result = *((uint16_t*)p); p += sizeof(uint16_t); break; case llvm::dwarf::DW_EH_PE_udata4: result = *((uint32_t*)p); p += sizeof(uint32_t); break; case llvm::dwarf::DW_EH_PE_udata8: result = *((uint64_t*)p); p += sizeof(uint64_t); break; case llvm::dwarf::DW_EH_PE_sdata2: result = *((int16_t*)p); p += sizeof(int16_t); break; case llvm::dwarf::DW_EH_PE_sdata4: result = *((int32_t*)p); p += sizeof(int32_t); break; case llvm::dwarf::DW_EH_PE_sdata8: result = *((int64_t*)p); p += sizeof(int64_t); break; default: // not supported abort(); break; } // then add relative offset switch (encoding & 0x70) { case llvm::dwarf::DW_EH_PE_absptr: // do nothing break; case llvm::dwarf::DW_EH_PE_pcrel: result += (uintptr_t)(*data); break; case llvm::dwarf::DW_EH_PE_textrel: case llvm::dwarf::DW_EH_PE_datarel: case llvm::dwarf::DW_EH_PE_funcrel: case llvm::dwarf::DW_EH_PE_aligned: default: // not supported abort(); break; } // then apply indirection if (encoding & llvm::dwarf::DW_EH_PE_indirect) { result = *((uintptr_t*)result); } *data = p; return result; } /// Deals with Dwarf actions matching our type infos /// (OurExceptionType_t instances). Returns whether or not a dwarf emitted /// action matches the supplied exception type. If such a match succeeds, /// the resultAction argument will be set with > 0 index value. Only /// corresponding llvm.eh.selector type info arguments, cleanup arguments /// are supported. Filters are not supported. /// See Variable Length Data in: /// @link http://dwarfstd.org/Dwarf3.pdf @unlink /// Also see @link http://mentorembedded.github.com/cxx-abi/abi-eh.html @unlink /// @param resultAction reference variable which will be set with result /// @param classInfo our array of type info pointers (to globals) /// @param actionEntry index into above type info array or 0 (clean up). /// We do not support filters. /// @param exceptionClass exception class (_Unwind_Exception::exception_class) /// of thrown exception. /// @param exceptionObject thrown _Unwind_Exception instance. /// @returns whether or not a type info was found. False is returned if only /// a cleanup was found static bool handleActionValue(int64_t *resultAction, uint8_t TTypeEncoding, const uint8_t *ClassInfo, uintptr_t actionEntry, uint64_t exceptionClass, struct _Unwind_Exception *exceptionObject) { bool ret = false; if (!resultAction || !exceptionObject || (exceptionClass != ourBaseExceptionClass)) return(ret); struct OurBaseException_t *excp = (struct OurBaseException_t*) (((char*) exceptionObject) + ourBaseFromUnwindOffset); struct OurExceptionType_t *excpType = &(excp->type); int type = excpType->type; #ifdef DEBUG fprintf(stderr, "handleActionValue(...): exceptionObject = <%p>, " "excp = <%p>.\n", (void*)exceptionObject, (void*)excp); #endif const uint8_t *actionPos = (uint8_t*) actionEntry, *tempActionPos; int64_t typeOffset = 0, actionOffset; for (int i = 0; true; ++i) { // Each emitted dwarf action corresponds to a 2 tuple of // type info address offset, and action offset to the next // emitted action. typeOffset = readSLEB128(&actionPos); tempActionPos = actionPos; actionOffset = readSLEB128(&tempActionPos); #ifdef DEBUG fprintf(stderr, "handleActionValue(...):typeOffset: <%" PRIi64 ">, " "actionOffset: <%" PRIi64 ">.\n", typeOffset, actionOffset); #endif assert((typeOffset >= 0) && "handleActionValue(...):filters are not supported."); // Note: A typeOffset == 0 implies that a cleanup llvm.eh.selector // argument has been matched. if (typeOffset > 0) { #ifdef DEBUG fprintf(stderr, "handleActionValue(...):actionValue <%d> found.\n", i); #endif unsigned EncSize = getEncodingSize(TTypeEncoding); const uint8_t *EntryP = ClassInfo - typeOffset * EncSize; uintptr_t P = readEncodedPointer(&EntryP, TTypeEncoding); struct OurExceptionType_t *ThisClassInfo = reinterpret_cast<struct OurExceptionType_t *>(P); if (ThisClassInfo->type == type) { *resultAction = i + 1; ret = true; break; } } #ifdef DEBUG fprintf(stderr, "handleActionValue(...):actionValue not found.\n"); #endif if (!actionOffset) break; actionPos += actionOffset; } return(ret); } /// Deals with the Language specific data portion of the emitted dwarf code. /// See @link http://mentorembedded.github.com/cxx-abi/abi-eh.html @unlink /// @param version unsupported (ignored), unwind version /// @param lsda language specific data area /// @param _Unwind_Action actions minimally supported unwind stage /// (forced specifically not supported) /// @param exceptionClass exception class (_Unwind_Exception::exception_class) /// of thrown exception. /// @param exceptionObject thrown _Unwind_Exception instance. /// @param context unwind system context /// @returns minimally supported unwinding control indicator static _Unwind_Reason_Code handleLsda(int version, const uint8_t *lsda, _Unwind_Action actions, uint64_t exceptionClass, struct _Unwind_Exception *exceptionObject, _Unwind_Context_t context) { _Unwind_Reason_Code ret = _URC_CONTINUE_UNWIND; if (!lsda) return(ret); #ifdef DEBUG fprintf(stderr, "handleLsda(...):lsda is non-zero.\n"); #endif // Get the current instruction pointer and offset it before next // instruction in the current frame which threw the exception. uintptr_t pc = _Unwind_GetIP(context)-1; // Get beginning current frame's code (as defined by the // emitted dwarf code) uintptr_t funcStart = _Unwind_GetRegionStart(context); uintptr_t pcOffset = pc - funcStart; const uint8_t *ClassInfo = NULL; // Note: See JITDwarfEmitter::EmitExceptionTable(...) for corresponding // dwarf emission // Parse LSDA header. uint8_t lpStartEncoding = *lsda++; if (lpStartEncoding != llvm::dwarf::DW_EH_PE_omit) { readEncodedPointer(&lsda, lpStartEncoding); } uint8_t ttypeEncoding = *lsda++; uintptr_t classInfoOffset; if (ttypeEncoding != llvm::dwarf::DW_EH_PE_omit) { // Calculate type info locations in emitted dwarf code which // were flagged by type info arguments to llvm.eh.selector // intrinsic classInfoOffset = readULEB128(&lsda); ClassInfo = lsda + classInfoOffset; } // Walk call-site table looking for range that // includes current PC. uint8_t callSiteEncoding = *lsda++; uint32_t callSiteTableLength = readULEB128(&lsda); const uint8_t *callSiteTableStart = lsda; const uint8_t *callSiteTableEnd = callSiteTableStart + callSiteTableLength; const uint8_t *actionTableStart = callSiteTableEnd; const uint8_t *callSitePtr = callSiteTableStart; while (callSitePtr < callSiteTableEnd) { uintptr_t start = readEncodedPointer(&callSitePtr, callSiteEncoding); uintptr_t length = readEncodedPointer(&callSitePtr, callSiteEncoding); uintptr_t landingPad = readEncodedPointer(&callSitePtr, callSiteEncoding); // Note: Action value uintptr_t actionEntry = readULEB128(&callSitePtr); if (exceptionClass != ourBaseExceptionClass) { // We have been notified of a foreign exception being thrown, // and we therefore need to execute cleanup landing pads actionEntry = 0; } if (landingPad == 0) { #ifdef DEBUG fprintf(stderr, "handleLsda(...): No landing pad found.\n"); #endif continue; // no landing pad for this entry } if (actionEntry) { actionEntry += ((uintptr_t) actionTableStart) - 1; } else { #ifdef DEBUG fprintf(stderr, "handleLsda(...):No action table found.\n"); #endif } bool exceptionMatched = false; if ((start <= pcOffset) && (pcOffset < (start + length))) { #ifdef DEBUG fprintf(stderr, "handleLsda(...): Landing pad found.\n"); #endif int64_t actionValue = 0; if (actionEntry) { exceptionMatched = handleActionValue(&actionValue, ttypeEncoding, ClassInfo, actionEntry, exceptionClass, exceptionObject); } if (!(actions & _UA_SEARCH_PHASE)) { #ifdef DEBUG fprintf(stderr, "handleLsda(...): installed landing pad " "context.\n"); #endif // Found landing pad for the PC. // Set Instruction Pointer to so we re-enter function // at landing pad. The landing pad is created by the // compiler to take two parameters in registers. _Unwind_SetGR(context, __builtin_eh_return_data_regno(0), (uintptr_t)exceptionObject); // Note: this virtual register directly corresponds // to the return of the llvm.eh.selector intrinsic if (!actionEntry || !exceptionMatched) { // We indicate cleanup only _Unwind_SetGR(context, __builtin_eh_return_data_regno(1), 0); } else { // Matched type info index of llvm.eh.selector intrinsic // passed here. _Unwind_SetGR(context, __builtin_eh_return_data_regno(1), actionValue); } // To execute landing pad set here _Unwind_SetIP(context, funcStart + landingPad); ret = _URC_INSTALL_CONTEXT; } else if (exceptionMatched) { #ifdef DEBUG fprintf(stderr, "handleLsda(...): setting handler found.\n"); #endif ret = _URC_HANDLER_FOUND; } else { // Note: Only non-clean up handlers are marked as // found. Otherwise the clean up handlers will be // re-found and executed during the clean up // phase. #ifdef DEBUG fprintf(stderr, "handleLsda(...): cleanup handler found.\n"); #endif } break; } } return(ret); } /// This is the personality function which is embedded (dwarf emitted), in the /// dwarf unwind info block. Again see: JITDwarfEmitter.cpp. /// See @link http://mentorembedded.github.com/cxx-abi/abi-eh.html @unlink /// @param version unsupported (ignored), unwind version /// @param _Unwind_Action actions minimally supported unwind stage /// (forced specifically not supported) /// @param exceptionClass exception class (_Unwind_Exception::exception_class) /// of thrown exception. /// @param exceptionObject thrown _Unwind_Exception instance. /// @param context unwind system context /// @returns minimally supported unwinding control indicator _Unwind_Reason_Code ourPersonality(int version, _Unwind_Action actions, uint64_t exceptionClass, struct _Unwind_Exception *exceptionObject, _Unwind_Context_t context) { #ifdef DEBUG fprintf(stderr, "We are in ourPersonality(...):actions is <%d>.\n", actions); if (actions & _UA_SEARCH_PHASE) { fprintf(stderr, "ourPersonality(...):In search phase.\n"); } else { fprintf(stderr, "ourPersonality(...):In non-search phase.\n"); } #endif const uint8_t *lsda = _Unwind_GetLanguageSpecificData(context); #ifdef DEBUG fprintf(stderr, "ourPersonality(...):lsda = <%p>.\n", (void*)lsda); #endif // The real work of the personality function is captured here return(handleLsda(version, lsda, actions, exceptionClass, exceptionObject, context)); } /// Generates our _Unwind_Exception class from a given character array. /// thereby handling arbitrary lengths (not in standard), and handling /// embedded \0s. /// See @link http://mentorembedded.github.com/cxx-abi/abi-eh.html @unlink /// @param classChars char array to encode. NULL values not checkedf /// @param classCharsSize number of chars in classChars. Value is not checked. /// @returns class value uint64_t genClass(const unsigned char classChars[], size_t classCharsSize) { uint64_t ret = classChars[0]; for (unsigned i = 1; i < classCharsSize; ++i) { ret <<= 8; ret += classChars[i]; } return(ret); } } // extern "C" // // Runtime C Library functions End // // // Code generation functions // /// Generates code to print given constant string /// @param context llvm context /// @param module code for module instance /// @param builder builder instance /// @param toPrint string to print /// @param useGlobal A value of true (default) indicates a GlobalValue is /// generated, and is used to hold the constant string. A value of /// false indicates that the constant string will be stored on the /// stack. void generateStringPrint(llvm::LLVMContext &context, llvm::Module &module, llvm::IRBuilder<> &builder, std::string toPrint, bool useGlobal = true) { llvm::Function *printFunct = module.getFunction("printStr"); llvm::Value *stringVar; llvm::Constant *stringConstant = llvm::ConstantDataArray::getString(context, toPrint); if (useGlobal) { // Note: Does not work without allocation stringVar = new llvm::GlobalVariable(module, stringConstant->getType(), true, llvm::GlobalValue::PrivateLinkage, stringConstant, ""); } else { stringVar = builder.CreateAlloca(stringConstant->getType()); builder.CreateStore(stringConstant, stringVar); } llvm::Value *cast = builder.CreatePointerCast(stringVar, builder.getInt8PtrTy()); builder.CreateCall(printFunct, cast); } /// Generates code to print given runtime integer according to constant /// string format, and a given print function. /// @param context llvm context /// @param module code for module instance /// @param builder builder instance /// @param printFunct function used to "print" integer /// @param toPrint string to print /// @param format printf like formating string for print /// @param useGlobal A value of true (default) indicates a GlobalValue is /// generated, and is used to hold the constant string. A value of /// false indicates that the constant string will be stored on the /// stack. void generateIntegerPrint(llvm::LLVMContext &context, llvm::Module &module, llvm::IRBuilder<> &builder, llvm::Function &printFunct, llvm::Value &toPrint, std::string format, bool useGlobal = true) { llvm::Constant *stringConstant = llvm::ConstantDataArray::getString(context, format); llvm::Value *stringVar; if (useGlobal) { // Note: Does not seem to work without allocation stringVar = new llvm::GlobalVariable(module, stringConstant->getType(), true, llvm::GlobalValue::PrivateLinkage, stringConstant, ""); } else { stringVar = builder.CreateAlloca(stringConstant->getType()); builder.CreateStore(stringConstant, stringVar); } llvm::Value *cast = builder.CreateBitCast(stringVar, builder.getInt8PtrTy()); builder.CreateCall(&printFunct, {&toPrint, cast}); } /// Generates code to handle finally block type semantics: always runs /// regardless of whether a thrown exception is passing through or the /// parent function is simply exiting. In addition to printing some state /// to stderr, this code will resume the exception handling--runs the /// unwind resume block, if the exception has not been previously caught /// by a catch clause, and will otherwise execute the end block (terminator /// block). In addition this function creates the corresponding function's /// stack storage for the exception pointer and catch flag status. /// @param context llvm context /// @param module code for module instance /// @param builder builder instance /// @param toAddTo parent function to add block to /// @param blockName block name of new "finally" block. /// @param functionId output id used for printing /// @param terminatorBlock terminator "end" block /// @param unwindResumeBlock unwind resume block /// @param exceptionCaughtFlag reference exception caught/thrown status storage /// @param exceptionStorage reference to exception pointer storage /// @param caughtResultStorage reference to landingpad result storage /// @returns newly created block static llvm::BasicBlock *createFinallyBlock(llvm::LLVMContext &context, llvm::Module &module, llvm::IRBuilder<> &builder, llvm::Function &toAddTo, std::string &blockName, std::string &functionId, llvm::BasicBlock &terminatorBlock, llvm::BasicBlock &unwindResumeBlock, llvm::Value **exceptionCaughtFlag, llvm::Value **exceptionStorage, llvm::Value **caughtResultStorage) { assert(exceptionCaughtFlag && "ExceptionDemo::createFinallyBlock(...):exceptionCaughtFlag " "is NULL"); assert(exceptionStorage && "ExceptionDemo::createFinallyBlock(...):exceptionStorage " "is NULL"); assert(caughtResultStorage && "ExceptionDemo::createFinallyBlock(...):caughtResultStorage " "is NULL"); *exceptionCaughtFlag = createEntryBlockAlloca(toAddTo, "exceptionCaught", ourExceptionNotThrownState->getType(), ourExceptionNotThrownState); llvm::PointerType *exceptionStorageType = builder.getInt8PtrTy(); *exceptionStorage = createEntryBlockAlloca(toAddTo, "exceptionStorage", exceptionStorageType, llvm::ConstantPointerNull::get( exceptionStorageType)); *caughtResultStorage = createEntryBlockAlloca(toAddTo, "caughtResultStorage", ourCaughtResultType, llvm::ConstantAggregateZero::get( ourCaughtResultType)); llvm::BasicBlock *ret = llvm::BasicBlock::Create(context, blockName, &toAddTo); builder.SetInsertPoint(ret); std::ostringstream bufferToPrint; bufferToPrint << "Gen: Executing finally block " << blockName << " in " << functionId << "\n"; generateStringPrint(context, module, builder, bufferToPrint.str(), USE_GLOBAL_STR_CONSTS); llvm::SwitchInst *theSwitch = builder.CreateSwitch(builder.CreateLoad( *exceptionCaughtFlag), &terminatorBlock, 2); theSwitch->addCase(ourExceptionCaughtState, &terminatorBlock); theSwitch->addCase(ourExceptionThrownState, &unwindResumeBlock); return(ret); } /// Generates catch block semantics which print a string to indicate type of /// catch executed, sets an exception caught flag, and executes passed in /// end block (terminator block). /// @param context llvm context /// @param module code for module instance /// @param builder builder instance /// @param toAddTo parent function to add block to /// @param blockName block name of new "catch" block. /// @param functionId output id used for printing /// @param terminatorBlock terminator "end" block /// @param exceptionCaughtFlag exception caught/thrown status /// @returns newly created block static llvm::BasicBlock *createCatchBlock(llvm::LLVMContext &context, llvm::Module &module, llvm::IRBuilder<> &builder, llvm::Function &toAddTo, std::string &blockName, std::string &functionId, llvm::BasicBlock &terminatorBlock, llvm::Value &exceptionCaughtFlag) { llvm::BasicBlock *ret = llvm::BasicBlock::Create(context, blockName, &toAddTo); builder.SetInsertPoint(ret); std::ostringstream bufferToPrint; bufferToPrint << "Gen: Executing catch block " << blockName << " in " << functionId << std::endl; generateStringPrint(context, module, builder, bufferToPrint.str(), USE_GLOBAL_STR_CONSTS); builder.CreateStore(ourExceptionCaughtState, &exceptionCaughtFlag); builder.CreateBr(&terminatorBlock); return(ret); } /// Generates a function which invokes a function (toInvoke) and, whose /// unwind block will "catch" the type info types correspondingly held in the /// exceptionTypesToCatch argument. If the toInvoke function throws an /// exception which does not match any type info types contained in /// exceptionTypesToCatch, the generated code will call _Unwind_Resume /// with the raised exception. On the other hand the generated code will /// normally exit if the toInvoke function does not throw an exception. /// The generated "finally" block is always run regardless of the cause of /// the generated function exit. /// The generated function is returned after being verified. /// @param module code for module instance /// @param builder builder instance /// @param fpm a function pass manager holding optional IR to IR /// transformations /// @param toInvoke inner function to invoke /// @param ourId id used to printing purposes /// @param numExceptionsToCatch length of exceptionTypesToCatch array /// @param exceptionTypesToCatch array of type info types to "catch" /// @returns generated function static llvm::Function *createCatchWrappedInvokeFunction( llvm::Module &module, llvm::IRBuilder<> &builder, llvm::legacy::FunctionPassManager &fpm, llvm::Function &toInvoke, std::string ourId, unsigned numExceptionsToCatch, unsigned exceptionTypesToCatch[]) { llvm::LLVMContext &context = module.getContext(); llvm::Function *toPrint32Int = module.getFunction("print32Int"); ArgTypes argTypes; argTypes.push_back(builder.getInt32Ty()); ArgNames argNames; argNames.push_back("exceptTypeToThrow"); llvm::Function *ret = createFunction(module, builder.getVoidTy(), argTypes, argNames, ourId, llvm::Function::ExternalLinkage, false, false); // Block which calls invoke llvm::BasicBlock *entryBlock = llvm::BasicBlock::Create(context, "entry", ret); // Normal block for invoke llvm::BasicBlock *normalBlock = llvm::BasicBlock::Create(context, "normal", ret); // Unwind block for invoke llvm::BasicBlock *exceptionBlock = llvm::BasicBlock::Create(context, "exception", ret); // Block which routes exception to correct catch handler block llvm::BasicBlock *exceptionRouteBlock = llvm::BasicBlock::Create(context, "exceptionRoute", ret); // Foreign exception handler llvm::BasicBlock *externalExceptionBlock = llvm::BasicBlock::Create(context, "externalException", ret); // Block which calls _Unwind_Resume llvm::BasicBlock *unwindResumeBlock = llvm::BasicBlock::Create(context, "unwindResume", ret); // Clean up block which delete exception if needed llvm::BasicBlock *endBlock = llvm::BasicBlock::Create(context, "end", ret); std::string nextName; std::vector<llvm::BasicBlock*> catchBlocks(numExceptionsToCatch); llvm::Value *exceptionCaughtFlag = NULL; llvm::Value *exceptionStorage = NULL; llvm::Value *caughtResultStorage = NULL; // Finally block which will branch to unwindResumeBlock if // exception is not caught. Initializes/allocates stack locations. llvm::BasicBlock *finallyBlock = createFinallyBlock(context, module, builder, *ret, nextName = "finally", ourId, *endBlock, *unwindResumeBlock, &exceptionCaughtFlag, &exceptionStorage, &caughtResultStorage ); for (unsigned i = 0; i < numExceptionsToCatch; ++i) { nextName = ourTypeInfoNames[exceptionTypesToCatch[i]]; // One catch block per type info to be caught catchBlocks[i] = createCatchBlock(context, module, builder, *ret, nextName, ourId, *finallyBlock, *exceptionCaughtFlag); } // Entry Block builder.SetInsertPoint(entryBlock); std::vector<llvm::Value*> args; args.push_back(namedValues["exceptTypeToThrow"]); builder.CreateInvoke(&toInvoke, normalBlock, exceptionBlock, args); // End Block builder.SetInsertPoint(endBlock); generateStringPrint(context, module, builder, "Gen: In end block: exiting in " + ourId + ".\n", USE_GLOBAL_STR_CONSTS); llvm::Function *deleteOurException = module.getFunction("deleteOurException"); // Note: function handles NULL exceptions builder.CreateCall(deleteOurException, builder.CreateLoad(exceptionStorage)); builder.CreateRetVoid(); // Normal Block builder.SetInsertPoint(normalBlock); generateStringPrint(context, module, builder, "Gen: No exception in " + ourId + "!\n", USE_GLOBAL_STR_CONSTS); // Finally block is always called builder.CreateBr(finallyBlock); // Unwind Resume Block builder.SetInsertPoint(unwindResumeBlock); builder.CreateResume(builder.CreateLoad(caughtResultStorage)); // Exception Block builder.SetInsertPoint(exceptionBlock); llvm::Function *personality = module.getFunction("ourPersonality"); ret->setPersonalityFn(personality); llvm::LandingPadInst *caughtResult = builder.CreateLandingPad(ourCaughtResultType, numExceptionsToCatch, "landingPad"); caughtResult->setCleanup(true); for (unsigned i = 0; i < numExceptionsToCatch; ++i) { // Set up type infos to be caught caughtResult->addClause(module.getGlobalVariable( ourTypeInfoNames[exceptionTypesToCatch[i]])); } llvm::Value *unwindException = builder.CreateExtractValue(caughtResult, 0); llvm::Value *retTypeInfoIndex = builder.CreateExtractValue(caughtResult, 1); // FIXME: Redundant storage which, beyond utilizing value of // caughtResultStore for unwindException storage, may be alleviated // altogether with a block rearrangement builder.CreateStore(caughtResult, caughtResultStorage); builder.CreateStore(unwindException, exceptionStorage); builder.CreateStore(ourExceptionThrownState, exceptionCaughtFlag); // Retrieve exception_class member from thrown exception // (_Unwind_Exception instance). This member tells us whether or not // the exception is foreign. llvm::Value *unwindExceptionClass = builder.CreateLoad(builder.CreateStructGEP( ourUnwindExceptionType, builder.CreatePointerCast(unwindException, ourUnwindExceptionType->getPointerTo()), 0)); // Branch to the externalExceptionBlock if the exception is foreign or // to a catch router if not. Either way the finally block will be run. builder.CreateCondBr(builder.CreateICmpEQ(unwindExceptionClass, llvm::ConstantInt::get(builder.getInt64Ty(), ourBaseExceptionClass)), exceptionRouteBlock, externalExceptionBlock); // External Exception Block builder.SetInsertPoint(externalExceptionBlock); generateStringPrint(context, module, builder, "Gen: Foreign exception received.\n", USE_GLOBAL_STR_CONSTS); // Branch to the finally block builder.CreateBr(finallyBlock); // Exception Route Block builder.SetInsertPoint(exceptionRouteBlock); // Casts exception pointer (_Unwind_Exception instance) to parent // (OurException instance). // // Note: ourBaseFromUnwindOffset is usually negative llvm::Value *typeInfoThrown = builder.CreatePointerCast( builder.CreateConstGEP1_64(unwindException, ourBaseFromUnwindOffset), ourExceptionType->getPointerTo()); // Retrieve thrown exception type info type // // Note: Index is not relative to pointer but instead to structure // unlike a true getelementptr (GEP) instruction typeInfoThrown = builder.CreateStructGEP(ourExceptionType, typeInfoThrown, 0); llvm::Value *typeInfoThrownType = builder.CreateStructGEP(builder.getInt8PtrTy(), typeInfoThrown, 0); generateIntegerPrint(context, module, builder, *toPrint32Int, *(builder.CreateLoad(typeInfoThrownType)), "Gen: Exception type <%d> received (stack unwound) " " in " + ourId + ".\n", USE_GLOBAL_STR_CONSTS); // Route to matched type info catch block or run cleanup finally block llvm::SwitchInst *switchToCatchBlock = builder.CreateSwitch(retTypeInfoIndex, finallyBlock, numExceptionsToCatch); unsigned nextTypeToCatch; for (unsigned i = 1; i <= numExceptionsToCatch; ++i) { nextTypeToCatch = i - 1; switchToCatchBlock->addCase(llvm::ConstantInt::get( llvm::Type::getInt32Ty(context), i), catchBlocks[nextTypeToCatch]); } llvm::verifyFunction(*ret); fpm.run(*ret); return(ret); } /// Generates function which throws either an exception matched to a runtime /// determined type info type (argument to generated function), or if this /// runtime value matches nativeThrowType, throws a foreign exception by /// calling nativeThrowFunct. /// @param module code for module instance /// @param builder builder instance /// @param fpm a function pass manager holding optional IR to IR /// transformations /// @param ourId id used to printing purposes /// @param nativeThrowType a runtime argument of this value results in /// nativeThrowFunct being called to generate/throw exception. /// @param nativeThrowFunct function which will throw a foreign exception /// if the above nativeThrowType matches generated function's arg. /// @returns generated function static llvm::Function * createThrowExceptionFunction(llvm::Module &module, llvm::IRBuilder<> &builder, llvm::legacy::FunctionPassManager &fpm, std::string ourId, int32_t nativeThrowType, llvm::Function &nativeThrowFunct) { llvm::LLVMContext &context = module.getContext(); namedValues.clear(); ArgTypes unwindArgTypes; unwindArgTypes.push_back(builder.getInt32Ty()); ArgNames unwindArgNames; unwindArgNames.push_back("exceptTypeToThrow"); llvm::Function *ret = createFunction(module, builder.getVoidTy(), unwindArgTypes, unwindArgNames, ourId, llvm::Function::ExternalLinkage, false, false); // Throws either one of our exception or a native C++ exception depending // on a runtime argument value containing a type info type. llvm::BasicBlock *entryBlock = llvm::BasicBlock::Create(context, "entry", ret); // Throws a foreign exception llvm::BasicBlock *nativeThrowBlock = llvm::BasicBlock::Create(context, "nativeThrow", ret); // Throws one of our Exceptions llvm::BasicBlock *generatedThrowBlock = llvm::BasicBlock::Create(context, "generatedThrow", ret); // Retrieved runtime type info type to throw llvm::Value *exceptionType = namedValues["exceptTypeToThrow"]; // nativeThrowBlock block builder.SetInsertPoint(nativeThrowBlock); // Throws foreign exception builder.CreateCall(&nativeThrowFunct, exceptionType); builder.CreateUnreachable(); // entry block builder.SetInsertPoint(entryBlock); llvm::Function *toPrint32Int = module.getFunction("print32Int"); generateIntegerPrint(context, module, builder, *toPrint32Int, *exceptionType, "\nGen: About to throw exception type <%d> in " + ourId + ".\n", USE_GLOBAL_STR_CONSTS); // Switches on runtime type info type value to determine whether or not // a foreign exception is thrown. Defaults to throwing one of our // generated exceptions. llvm::SwitchInst *theSwitch = builder.CreateSwitch(exceptionType, generatedThrowBlock, 1); theSwitch->addCase(llvm::ConstantInt::get(llvm::Type::getInt32Ty(context), nativeThrowType), nativeThrowBlock); // generatedThrow block builder.SetInsertPoint(generatedThrowBlock); llvm::Function *createOurException = module.getFunction("createOurException"); llvm::Function *raiseOurException = module.getFunction( "_Unwind_RaiseException"); // Creates exception to throw with runtime type info type. llvm::Value *exception = builder.CreateCall(createOurException, namedValues["exceptTypeToThrow"]); // Throw generated Exception builder.CreateCall(raiseOurException, exception); builder.CreateUnreachable(); llvm::verifyFunction(*ret); fpm.run(*ret); return(ret); } static void createStandardUtilityFunctions(unsigned numTypeInfos, llvm::Module &module, llvm::IRBuilder<> &builder); /// Creates test code by generating and organizing these functions into the /// test case. The test case consists of an outer function setup to invoke /// an inner function within an environment having multiple catch and single /// finally blocks. This inner function is also setup to invoke a throw /// function within an evironment similar in nature to the outer function's /// catch and finally blocks. Each of these two functions catch mutually /// exclusive subsets (even or odd) of the type info types configured /// for this this. All generated functions have a runtime argument which /// holds a type info type to throw that each function takes and passes it /// to the inner one if such a inner function exists. This type info type is /// looked at by the generated throw function to see whether or not it should /// throw a generated exception with the same type info type, or instead call /// a supplied a function which in turn will throw a foreign exception. /// @param module code for module instance /// @param builder builder instance /// @param fpm a function pass manager holding optional IR to IR /// transformations /// @param nativeThrowFunctName name of external function which will throw /// a foreign exception /// @returns outermost generated test function. llvm::Function * createUnwindExceptionTest(llvm::Module &module, llvm::IRBuilder<> &builder, llvm::legacy::FunctionPassManager &fpm, std::string nativeThrowFunctName) { // Number of type infos to generate unsigned numTypeInfos = 6; // Initialze intrisics and external functions to use along with exception // and type info globals. createStandardUtilityFunctions(numTypeInfos, module, builder); llvm::Function *nativeThrowFunct = module.getFunction(nativeThrowFunctName); // Create exception throw function using the value ~0 to cause // foreign exceptions to be thrown. llvm::Function *throwFunct = createThrowExceptionFunction(module, builder, fpm, "throwFunct", ~0, *nativeThrowFunct); // Inner function will catch even type infos unsigned innerExceptionTypesToCatch[] = {6, 2, 4}; size_t numExceptionTypesToCatch = sizeof(innerExceptionTypesToCatch) / sizeof(unsigned); // Generate inner function. llvm::Function *innerCatchFunct = createCatchWrappedInvokeFunction(module, builder, fpm, *throwFunct, "innerCatchFunct", numExceptionTypesToCatch, innerExceptionTypesToCatch); // Outer function will catch odd type infos unsigned outerExceptionTypesToCatch[] = {3, 1, 5}; numExceptionTypesToCatch = sizeof(outerExceptionTypesToCatch) / sizeof(unsigned); // Generate outer function llvm::Function *outerCatchFunct = createCatchWrappedInvokeFunction(module, builder, fpm, *innerCatchFunct, "outerCatchFunct", numExceptionTypesToCatch, outerExceptionTypesToCatch); // Return outer function to run return(outerCatchFunct); } namespace { /// Represents our foreign exceptions class OurCppRunException : public std::runtime_error { public: OurCppRunException(const std::string reason) : std::runtime_error(reason) {} OurCppRunException (const OurCppRunException &toCopy) : std::runtime_error(toCopy) {} OurCppRunException &operator = (const OurCppRunException &toCopy) { return(reinterpret_cast<OurCppRunException&>( std::runtime_error::operator=(toCopy))); } ~OurCppRunException(void) throw() override {} }; } // end anonymous namespace /// Throws foreign C++ exception. /// @param ignoreIt unused parameter that allows function to match implied /// generated function contract. extern "C" void throwCppException (int32_t ignoreIt) { throw(OurCppRunException("thrown by throwCppException(...)")); } typedef void (*OurExceptionThrowFunctType) (int32_t typeToThrow); /// This is a test harness which runs test by executing generated /// function with a type info type to throw. Harness wraps the execution /// of generated function in a C++ try catch clause. /// @param engine execution engine to use for executing generated function. /// This demo program expects this to be a JIT instance for demo /// purposes. /// @param function generated test function to run /// @param typeToThrow type info type of generated exception to throw, or /// indicator to cause foreign exception to be thrown. static void runExceptionThrow(llvm::ExecutionEngine *engine, llvm::Function *function, int32_t typeToThrow) { // Find test's function pointer OurExceptionThrowFunctType functPtr = reinterpret_cast<OurExceptionThrowFunctType>( reinterpret_cast<intptr_t>(engine->getPointerToFunction(function))); try { // Run test (*functPtr)(typeToThrow); } catch (OurCppRunException exc) { // Catch foreign C++ exception fprintf(stderr, "\nrunExceptionThrow(...):In C++ catch OurCppRunException " "with reason: %s.\n", exc.what()); } catch (...) { // Catch all exceptions including our generated ones. This latter // functionality works according to the example in rules 1.6.4 of // http://mentorembedded.github.com/cxx-abi/abi-eh.html (v1.22), // given that these will be exceptions foreign to C++ // (the _Unwind_Exception::exception_class should be different from // the one used by C++). fprintf(stderr, "\nrunExceptionThrow(...):In C++ catch all.\n"); } } // // End test functions // typedef llvm::ArrayRef<llvm::Type*> TypeArray; /// This initialization routine creates type info globals and /// adds external function declarations to module. /// @param numTypeInfos number of linear type info associated type info types /// to create as GlobalVariable instances, starting with the value 1. /// @param module code for module instance /// @param builder builder instance static void createStandardUtilityFunctions(unsigned numTypeInfos, llvm::Module &module, llvm::IRBuilder<> &builder) { llvm::LLVMContext &context = module.getContext(); // Exception initializations // Setup exception catch state ourExceptionNotThrownState = llvm::ConstantInt::get(llvm::Type::getInt8Ty(context), 0), ourExceptionThrownState = llvm::ConstantInt::get(llvm::Type::getInt8Ty(context), 1), ourExceptionCaughtState = llvm::ConstantInt::get(llvm::Type::getInt8Ty(context), 2), // Create our type info type ourTypeInfoType = llvm::StructType::get(context, TypeArray(builder.getInt32Ty())); llvm::Type *caughtResultFieldTypes[] = { builder.getInt8PtrTy(), builder.getInt32Ty() }; // Create our landingpad result type ourCaughtResultType = llvm::StructType::get(context, TypeArray(caughtResultFieldTypes)); // Create OurException type ourExceptionType = llvm::StructType::get(context, TypeArray(ourTypeInfoType)); // Create portion of _Unwind_Exception type // // Note: Declaring only a portion of the _Unwind_Exception struct. // Does this cause problems? ourUnwindExceptionType = llvm::StructType::get(context, TypeArray(builder.getInt64Ty())); struct OurBaseException_t dummyException; // Calculate offset of OurException::unwindException member. ourBaseFromUnwindOffset = ((uintptr_t) &dummyException) - ((uintptr_t) &(dummyException.unwindException)); #ifdef DEBUG fprintf(stderr, "createStandardUtilityFunctions(...):ourBaseFromUnwindOffset " "= %" PRIi64 ", sizeof(struct OurBaseException_t) - " "sizeof(struct _Unwind_Exception) = %lu.\n", ourBaseFromUnwindOffset, sizeof(struct OurBaseException_t) - sizeof(struct _Unwind_Exception)); #endif size_t numChars = sizeof(ourBaseExcpClassChars) / sizeof(char); // Create our _Unwind_Exception::exception_class value ourBaseExceptionClass = genClass(ourBaseExcpClassChars, numChars); // Type infos std::string baseStr = "typeInfo", typeInfoName; std::ostringstream typeInfoNameBuilder; std::vector<llvm::Constant*> structVals; llvm::Constant *nextStruct; // Generate each type info // // Note: First type info is not used. for (unsigned i = 0; i <= numTypeInfos; ++i) { structVals.clear(); structVals.push_back(llvm::ConstantInt::get(builder.getInt32Ty(), i)); nextStruct = llvm::ConstantStruct::get(ourTypeInfoType, structVals); typeInfoNameBuilder.str(""); typeInfoNameBuilder << baseStr << i; typeInfoName = typeInfoNameBuilder.str(); // Note: Does not seem to work without allocation new llvm::GlobalVariable(module, ourTypeInfoType, true, llvm::GlobalValue::ExternalLinkage, nextStruct, typeInfoName); ourTypeInfoNames.push_back(typeInfoName); ourTypeInfoNamesIndex[i] = typeInfoName; } ArgNames argNames; ArgTypes argTypes; llvm::Function *funct = NULL; // print32Int llvm::Type *retType = builder.getVoidTy(); argTypes.clear(); argTypes.push_back(builder.getInt32Ty()); argTypes.push_back(builder.getInt8PtrTy()); argNames.clear(); createFunction(module, retType, argTypes, argNames, "print32Int", llvm::Function::ExternalLinkage, true, false); // print64Int retType = builder.getVoidTy(); argTypes.clear(); argTypes.push_back(builder.getInt64Ty()); argTypes.push_back(builder.getInt8PtrTy()); argNames.clear(); createFunction(module, retType, argTypes, argNames, "print64Int", llvm::Function::ExternalLinkage, true, false); // printStr retType = builder.getVoidTy(); argTypes.clear(); argTypes.push_back(builder.getInt8PtrTy()); argNames.clear(); createFunction(module, retType, argTypes, argNames, "printStr", llvm::Function::ExternalLinkage, true, false); // throwCppException retType = builder.getVoidTy(); argTypes.clear(); argTypes.push_back(builder.getInt32Ty()); argNames.clear(); createFunction(module, retType, argTypes, argNames, "throwCppException", llvm::Function::ExternalLinkage, true, false); // deleteOurException retType = builder.getVoidTy(); argTypes.clear(); argTypes.push_back(builder.getInt8PtrTy()); argNames.clear(); createFunction(module, retType, argTypes, argNames, "deleteOurException", llvm::Function::ExternalLinkage, true, false); // createOurException retType = builder.getInt8PtrTy(); argTypes.clear(); argTypes.push_back(builder.getInt32Ty()); argNames.clear(); createFunction(module, retType, argTypes, argNames, "createOurException", llvm::Function::ExternalLinkage, true, false); // _Unwind_RaiseException retType = builder.getInt32Ty(); argTypes.clear(); argTypes.push_back(builder.getInt8PtrTy()); argNames.clear(); funct = createFunction(module, retType, argTypes, argNames, "_Unwind_RaiseException", llvm::Function::ExternalLinkage, true, false); funct->setDoesNotReturn(); // _Unwind_Resume retType = builder.getInt32Ty(); argTypes.clear(); argTypes.push_back(builder.getInt8PtrTy()); argNames.clear(); funct = createFunction(module, retType, argTypes, argNames, "_Unwind_Resume", llvm::Function::ExternalLinkage, true, false); funct->setDoesNotReturn(); // ourPersonality retType = builder.getInt32Ty(); argTypes.clear(); argTypes.push_back(builder.getInt32Ty()); argTypes.push_back(builder.getInt32Ty()); argTypes.push_back(builder.getInt64Ty()); argTypes.push_back(builder.getInt8PtrTy()); argTypes.push_back(builder.getInt8PtrTy()); argNames.clear(); createFunction(module, retType, argTypes, argNames, "ourPersonality", llvm::Function::ExternalLinkage, true, false); // llvm.eh.typeid.for intrinsic getDeclaration(&module, llvm::Intrinsic::eh_typeid_for); } //===----------------------------------------------------------------------===// // Main test driver code. //===----------------------------------------------------------------------===// /// Demo main routine which takes the type info types to throw. A test will /// be run for each given type info type. While type info types with the value /// of -1 will trigger a foreign C++ exception to be thrown; type info types /// <= 6 and >= 1 will be caught by test functions; and type info types > 6 /// will result in exceptions which pass through to the test harness. All other /// type info types are not supported and could cause a crash. int main(int argc, char *argv[]) { if (argc == 1) { fprintf(stderr, "\nUsage: ExceptionDemo <exception type to throw> " "[<type 2>...<type n>].\n" " Each type must have the value of 1 - 6 for " "generated exceptions to be caught;\n" " the value -1 for foreign C++ exceptions to be " "generated and thrown;\n" " or the values > 6 for exceptions to be ignored.\n" "\nTry: ExceptionDemo 2 3 7 -1\n" " for a full test.\n\n"); return(0); } // If not set, exception handling will not be turned on llvm::TargetOptions Opts; llvm::InitializeNativeTarget(); llvm::InitializeNativeTargetAsmPrinter(); llvm::LLVMContext &context = llvm::getGlobalContext(); llvm::IRBuilder<> theBuilder(context); // Make the module, which holds all the code. std::unique_ptr<llvm::Module> Owner = llvm::make_unique<llvm::Module>("my cool jit", context); llvm::Module *module = Owner.get(); std::unique_ptr<llvm::RTDyldMemoryManager> MemMgr(new llvm::SectionMemoryManager()); // Build engine with JIT llvm::EngineBuilder factory(std::move(Owner)); factory.setEngineKind(llvm::EngineKind::JIT); factory.setTargetOptions(Opts); factory.setMCJITMemoryManager(std::move(MemMgr)); llvm::ExecutionEngine *executionEngine = factory.create(); { llvm::legacy::FunctionPassManager fpm(module); // Set up the optimizer pipeline. // Start with registering info about how the // target lays out data structures. module->setDataLayout(executionEngine->getDataLayout()); // Optimizations turned on #ifdef ADD_OPT_PASSES // Basic AliasAnslysis support for GVN. fpm.add(llvm::createBasicAliasAnalysisPass()); // Promote allocas to registers. fpm.add(llvm::createPromoteMemoryToRegisterPass()); // Do simple "peephole" optimizations and bit-twiddling optzns. fpm.add(llvm::createInstructionCombiningPass()); // Reassociate expressions. fpm.add(llvm::createReassociatePass()); // Eliminate Common SubExpressions. fpm.add(llvm::createGVNPass()); // Simplify the control flow graph (deleting unreachable // blocks, etc). fpm.add(llvm::createCFGSimplificationPass()); #endif // ADD_OPT_PASSES fpm.doInitialization(); // Generate test code using function throwCppException(...) as // the function which throws foreign exceptions. llvm::Function *toRun = createUnwindExceptionTest(*module, theBuilder, fpm, "throwCppException"); executionEngine->finalizeObject(); fprintf(stderr, "\nBegin module dump:\n\n"); module->dump(); fprintf(stderr, "\nEnd module dump:\n"); fprintf(stderr, "\n\nBegin Test:\n"); for (int i = 1; i < argc; ++i) { // Run test for each argument whose value is the exception // type to throw. runExceptionThrow(executionEngine, toRun, (unsigned) strtoul(argv[i], NULL, 10)); } fprintf(stderr, "\nEnd Test:\n\n"); } delete executionEngine; return 0; }