diff options
author | Garrison Venn <gvenn.cfe.dev@gmail.com> | 2010-02-09 23:22:43 +0000 |
---|---|---|
committer | Garrison Venn <gvenn.cfe.dev@gmail.com> | 2010-02-09 23:22:43 +0000 |
commit | a2c2f1ae849c8091ca8eabfb21eea7947c180c18 (patch) | |
tree | f02436e064c4c3e10b254f382e66cb21b256f868 /examples | |
parent | d0bc7f060ece77c670794ef60f7052e2ff1847c9 (diff) |
Adds a JIT based exception handling example to the examples directory.
Both zero cost example domain specific, and C++ foreign exception handling are
shown. The example's documentation fully explains how to run the example.
Notes:
1) The code uses an extremely simple type info model.
2) Only a single landing pad is used per unwind edge
(one call to llvm.eh.selector)
3) llvm.eh.selector support for filter arguments is not given.
4) llvm.eh.typeid.for is not used.
5) Forced unwind behavior is not supported.
6) Very little if any error handling is given.
7) __attribute__((__aligned__)) is used.
8) The code uses parts from the llvm compiler-rt project and
the llvm Kaleidoscope example.
9) The code has not been ported or tested on WINDOWS.
10) The code was not tested with a cmake build.
11) The code was tested for a debug build on 32bit X86 CentOS LINUX,
and both a debug and release build on OS X 10.6.2 (64bit).
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@95723 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'examples')
-rw-r--r-- | examples/CMakeLists.txt | 1 | ||||
-rw-r--r-- | examples/ExceptionDemo/CMakeLists.txt | 5 | ||||
-rw-r--r-- | examples/ExceptionDemo/ExceptionDemo.cpp | 2028 | ||||
-rw-r--r-- | examples/ExceptionDemo/Makefile | 17 | ||||
-rw-r--r-- | examples/Makefile | 3 |
5 files changed, 2053 insertions, 1 deletions
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index c5b80793e0..b54adeb9be 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -3,6 +3,7 @@ add_subdirectory(Fibonacci) add_subdirectory(HowToUseJIT) add_subdirectory(Kaleidoscope) add_subdirectory(ModuleMaker) +add_subdirectory(ExceptionDemo) include(CheckIncludeFile) check_include_file(pthread.h HAVE_PTHREAD_H) diff --git a/examples/ExceptionDemo/CMakeLists.txt b/examples/ExceptionDemo/CMakeLists.txt new file mode 100644 index 0000000000..d661915568 --- /dev/null +++ b/examples/ExceptionDemo/CMakeLists.txt @@ -0,0 +1,5 @@ +set(LLVM_LINK_COMPONENTS jit nativecodegen) + +add_llvm_example(ExceptionDemo + ExceptionDemo.cpp + ) diff --git a/examples/ExceptionDemo/ExceptionDemo.cpp b/examples/ExceptionDemo/ExceptionDemo.cpp new file mode 100644 index 0000000000..46dc481021 --- /dev/null +++ b/examples/ExceptionDemo/ExceptionDemo.cpp @@ -0,0 +1,2028 @@ +//===-- examples/ExceptionDemo/ExceptionDemo.cpp - +// An example use of the llvm Exception mechanism --===// +// +// 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 questionable. +// +// This code uses code from the llvm compiler-rt project and the llvm +// Kaleidoscope project. +// +//===--------------------------------------------------------------------===// + + +#include "llvm/LLVMContext.h" +#include "llvm/DerivedTypes.h" +#include "llvm/ExecutionEngine/ExecutionEngine.h" +#include "llvm/ExecutionEngine/JIT.h" +#include "llvm/Module.h" +#include "llvm/PassManager.h" +#include "llvm/Intrinsics.h" +#include "llvm/Analysis/Verifier.h" +#include "llvm/Target/TargetData.h" +#include "llvm/Target/TargetSelect.h" +#include "llvm/Target/TargetOptions.h" +#include "llvm/Transforms/Scalar.h" +#include "llvm/Support/IRBuilder.h" +#include "llvm/Support/Dwarf.h" + +#include <cstdio> +#include <string> +#include <sstream> +#include <map> +#include <vector> +#include <stdexcept> + + +#ifndef USE_GLOBAL_STR_CONSTS +#define USE_GLOBAL_STR_CONSTS true +#endif + +// System C++ ABI unwind types from: +// http://refspecs.freestandards.org/abi-eh-1.21.html + +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://refspecs.freestandards.org/abi-eh-1.21.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* 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<const 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, + const 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, + const llvm::Type* type, + llvm::Constant* initWith = NULL) { + 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 previosly 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 @link http://refspecs.freestandards.org/abi-eh-1.21.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; +} + + +/// 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://refspecs.freestandards.org/abi-eh-1.21.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, + struct OurExceptionType_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", + exceptionObject, + 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: <%lld>, " + "actionOffset: <%lld>.\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) && + (type == (classInfo[-typeOffset])->type)) { +#ifdef DEBUG + fprintf(stderr, + "handleActionValue(...):actionValue <%d> found.\n", + i); +#endif + *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://refspecs.freestandards.org/abi-eh-1.21.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; + struct OurExceptionType_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 = (struct OurExceptionType_t**) (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; + + bool foreignException = false; + + 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; + foreignException = true; + } + + 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, + 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://refspecs.freestandards.org/abi-eh-1.21.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 = (uint8_t*) + _Unwind_GetLanguageSpecificData(context); + +#ifdef DEBUG + fprintf(stderr, + "ourPersonality(...):lsda = <%p>.\n", + 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://refspecs.freestandards.org/abi-eh-1.21.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::ConstantArray::get(context, toPrint); + + if (useGlobal) { + // Note: Does not work without allocation + stringVar = + new llvm::GlobalVariable(module, + stringConstant->getType(), + true, + llvm::GlobalValue::LinkerPrivateLinkage, + stringConstant, + ""); + } + else { + stringVar = builder.CreateAlloca(stringConstant->getType()); + builder.CreateStore(stringConstant, stringVar); + } + + llvm::Value* cast = + builder.CreatePointerCast(stringVar, + builder.getInt8Ty()->getPointerTo()); + 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::ConstantArray::get(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::LinkerPrivateLinkage, + stringConstant, + ""); + } + else { + stringVar = builder.CreateAlloca(stringConstant->getType()); + builder.CreateStore(stringConstant, stringVar); + } + + llvm::Value* cast = + builder.CreateBitCast(stringVar, + builder.getInt8Ty()->getPointerTo()); + builder.CreateCall2(&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 +/// @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) { + assert(exceptionCaughtFlag && + "ExceptionDemo::createFinallyBlock(...):exceptionCaughtFlag " + "is NULL"); + assert(exceptionStorage && + "ExceptionDemo::createFinallyBlock(...):exceptionStorage " + "is NULL"); + + *exceptionCaughtFlag = + createEntryBlockAlloca(toAddTo, + "exceptionCaught", + ourExceptionNotThrownState->getType(), + ourExceptionNotThrownState); + + const llvm::PointerType* exceptionStorageType = + builder.getInt8Ty()->getPointerTo(); + *exceptionStorage = + createEntryBlockAlloca(toAddTo, + "exceptionStorage", + exceptionStorageType, + llvm::ConstantPointerNull::get( + exceptionStorageType)); + + llvm::BasicBlock *ret = llvm::BasicBlock::Create(context, + blockName, + &toAddTo); + + builder.SetInsertPoint(ret); + + std::ostringstream bufferToPrint; + bufferToPrint << "Gen: Executing finally block " + << blockName + << " in " + << functionId + << std::endl; + 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::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; + + // 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); + + 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.begin(), + args.end()); + + // 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); + + llvm::Function *resumeOurException = + module.getFunction("_Unwind_Resume"); + builder.CreateCall(resumeOurException, + builder.CreateLoad(exceptionStorage)); + builder.CreateUnreachable(); + + // Exception Block + + builder.SetInsertPoint(exceptionBlock); + + llvm::Function *ehException = module.getFunction("llvm.eh.exception"); + + // Retrieve thrown exception + llvm::Value* unwindException = builder.CreateCall(ehException); + + // Store exception and flag + builder.CreateStore(unwindException, exceptionStorage); + builder.CreateStore(ourExceptionThrownState, exceptionCaughtFlag); + llvm::Function *personality = module.getFunction("ourPersonality"); + llvm::Value* functPtr = + builder.CreatePointerCast(personality, + builder.getInt8Ty()->getPointerTo()); + + args.clear(); + args.push_back(unwindException); + args.push_back(functPtr); + + // Note: Skipping index 0 + for (unsigned i = 0; i < numExceptionsToCatch; ++i) { + // Set up type infos to be caught + args.push_back( + module.getGlobalVariable( + ourTypeInfoNames[exceptionTypesToCatch[i]])); + } + + args.push_back(llvm::ConstantInt::get(builder.getInt32Ty(), 0)); + + llvm::Function *ehSelector = module.getFunction("llvm.eh.selector"); + + // Set up this exeption block as the landing pad which will handle + // given type infos. See case Intrinsic::eh_selector in + // SelectionDAGBuilder::visitIntrinsicCall(...) and AddCatchInfo(...) + // implemented in FunctionLoweringInfo.cpp to see how the implementation + // handles this call. This landing pad (this exception block), will be + // called either because it nees to cleanup (call finally) or a type + // info was found which matched the thrown exception. + llvm::Value* retTypeInfoIndex = builder.CreateCall(ehSelector, + args.begin(), + args.end()); + + // 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( + 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(typeInfoThrown, 0); + + llvm::Value* typeInfoThrownType = + builder.CreateStructGEP(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::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::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); +} + + +/// 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 () {}; +}; + + +/// 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 excecution +/// 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. I'm not sure + // why this latter functionality should work, as it seems that + // our exceptions should be foreign to C++ (the _Unwind_Exception:: + // exception_class should be different from the one used by C++), and + // therefore C++ should ignore the generated exceptions. + + fprintf(stderr, + "\nrunExceptionThrow(...):In C++ catch all.\n"); + } +} + +// +// End test functions +// + +/// 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, + builder.getInt32Ty(), + NULL); + + // Create OurException type + ourExceptionType = llvm::StructType::get(context, + ourTypeInfoType, + NULL); + + // 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, + builder.getInt64Ty(), + NULL); + struct OurBaseException_t dummyException; + + // Calculate offset of OurException::unwindException member. + ourBaseFromUnwindOffset = ((uintptr_t) &dummyException) - + ((uintptr_t) &(dummyException.unwindException)); + +#ifdef DEBUG + fprintf(stderr, + "createStandardUtilityFunctions(...):ourBaseFromUnwindOffset " + "= %lld, 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; + llvm::GlobalVariable* nextGlobal = NULL; + + // 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 + nextGlobal = + 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 + + const llvm::Type* retType = builder.getVoidTy(); + + argTypes.clear(); + argTypes.push_back(builder.getInt32Ty()); + argTypes.push_back(builder.getInt8Ty()->getPointerTo()); + + 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.getInt8Ty()->getPointerTo()); + + argNames.clear(); + + createFunction(module, + retType, + argTypes, + argNames, + "print64Int", + llvm::Function::ExternalLinkage, + true, + false); + + // printStr + + retType = builder.getVoidTy(); + + argTypes.clear(); + argTypes.push_back(builder.getInt8Ty()->getPointerTo()); + + 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.getInt8Ty()->getPointerTo()); + + argNames.clear(); + + createFunction(module, + retType, + argTypes, + argNames, + "deleteOurException", + llvm::Function::ExternalLinkage, + true, + false); + + // createOurException + + retType = builder.getInt8Ty()->getPointerTo(); + + 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.getInt8Ty()->getPointerTo()); + + argNames.clear(); + + funct = createFunction(module, + retType, + argTypes, + argNames, + "_Unwind_RaiseException", + llvm::Function::ExternalLinkage, + true, + false); + + funct->addFnAttr(llvm::Attribute::NoReturn); + + // _Unwind_Resume + + retType = builder.getInt32Ty(); + + argTypes.clear(); + argTypes.push_back(builder.getInt8Ty()->getPointerTo()); + + argNames.clear(); + + funct = createFunction(module, + retType, + argTypes, + argNames, + "_Unwind_Resume", + llvm::Function::ExternalLinkage, + true, + false); + + funct->addFnAttr(llvm::Attribute::NoReturn); + + // 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.getInt8Ty()->getPointerTo()); + argTypes.push_back(builder.getInt8Ty()->getPointerTo()); + + argNames.clear(); + + createFunction(module, + retType, + argTypes, + argNames, + "ourPersonality", + llvm::Function::ExternalLinkage, + true, + false); + + // llvm.eh.selector intrinsic + + getDeclaration(&module, llvm::Intrinsic::eh_selector); + + // llvm.eh.exception intrinsic + + getDeclaration(&module, llvm::Intrinsic::eh_exception); + + // 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::DwarfExceptionHandling = true; + + llvm::InitializeNativeTarget(); + llvm::LLVMContext& context = llvm::getGlobalContext(); + llvm::IRBuilder<> theBuilder(context); + + // Make the module, which holds all the code. + llvm::Module* module = new llvm::Module("my cool jit", context); + + // Build engine with JIT + llvm::EngineBuilder factory(module); + factory.setEngineKind(llvm::EngineKind::JIT); + factory.setAllocateGVsWithCode(false); + llvm::ExecutionEngine* executionEngine = factory.create(); + + { + llvm::FunctionPassManager fpm(module); + + // Set up the optimizer pipeline. + // Start with registering info about how the + // target lays out data structures. + fpm.add(new llvm::TargetData(*executionEngine->getTargetData())); + + // Optimizations turned on +#ifdef ADD_OPT_PASSES + + // 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"); + + 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; +} + diff --git a/examples/ExceptionDemo/Makefile b/examples/ExceptionDemo/Makefile new file mode 100644 index 0000000000..06bba66e8b --- /dev/null +++ b/examples/ExceptionDemo/Makefile @@ -0,0 +1,17 @@ +##===- examples/ExceptionDemo/Makefile --------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===---------------------------------------------------------------------===## +LEVEL = ../.. +TOOLNAME = ExceptionDemo +EXAMPLE_TOOL = 1 + +LINK_COMPONENTS := jit interpreter nativecodegen + +include $(LEVEL)/Makefile.common + +CXXFLAGS += -fexceptions diff --git a/examples/Makefile b/examples/Makefile index eb5dabdcb1..488d5895a8 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -10,7 +10,8 @@ LEVEL=.. include $(LEVEL)/Makefile.config -PARALLEL_DIRS:= BrainF Fibonacci HowToUseJIT Kaleidoscope ModuleMaker +PARALLEL_DIRS:= BrainF Fibonacci HowToUseJIT Kaleidoscope ModuleMaker \ + ExceptionDemo ifeq ($(HAVE_PTHREAD),1) PARALLEL_DIRS += ParallelJIT |