1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-24 19:52:54 +01:00
llvm-mirror/examples/ExceptionDemo/ExceptionDemo.cpp
Garrison Venn bb6a6a6a47 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).

llvm-svn: 95723
2010-02-09 23:22:43 +00:00

2029 lines
72 KiB
C++

//===-- 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;
}