brintos

brintos / llvm-project-archived public Read only

0
0
Text · 67.6 KiB · ae33441 Raw
1950 lines · cpp
1//===-- ExceptionDemo.cpp - An example using llvm Exceptions --------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// Demo program which implements an example LLVM exception implementation, and10// shows several test cases including the handling of foreign exceptions.11// It is run with type info types arguments to throw. A test will12// be run for each given type info type. While type info types with the value13// of -1 will trigger a foreign C++ exception to be thrown; type info types14// <= 6 and >= 1 will cause the associated generated exceptions to be thrown15// and caught by generated test functions; and type info types > 616// will result in exceptions which pass through to the test harness. All other17// type info types are not supported and could cause a crash. In all cases,18// the "finally" blocks of every generated test functions will executed19// regardless of whether or not that test function ignores or catches the20// thrown exception.21//22// examples:23//24// ExceptionDemo25//26//     causes a usage to be printed to stderr27//28// ExceptionDemo 2 3 7 -129//30//     results in the following cases:31//         - Value 2 causes an exception with a type info type of 2 to be32//           thrown and caught by an inner generated test function.33//         - Value 3 causes an exception with a type info type of 3 to be34//           thrown and caught by an outer generated test function.35//         - Value 7 causes an exception with a type info type of 7 to be36//           thrown and NOT be caught by any generated function.37//         - Value -1 causes a foreign C++ exception to be thrown and not be38//           caught by any generated function39//40//     Cases -1 and 7 are caught by a C++ test harness where the validity of41//         of a C++ catch(...) clause catching a generated exception with a42//         type info type of 7 is explained by: example in rules 1.6.4 in43//         http://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html (v1.22)44//45// This code uses code from the llvm compiler-rt project and the llvm46// Kaleidoscope project.47//48//===----------------------------------------------------------------------===//49 50#include "llvm/ADT/STLExtras.h"51#include "llvm/BinaryFormat/Dwarf.h"52#include "llvm/ExecutionEngine/Orc/Core.h"53#include "llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h"54#include "llvm/ExecutionEngine/Orc/LLJIT.h"55#include "llvm/IR/DataLayout.h"56#include "llvm/IR/DerivedTypes.h"57#include "llvm/IR/IRBuilder.h"58#include "llvm/IR/Intrinsics.h"59#include "llvm/IR/LLVMContext.h"60#include "llvm/IR/LegacyPassManager.h"61#include "llvm/IR/Module.h"62#include "llvm/IR/Verifier.h"63#include "llvm/Support/TargetSelect.h"64#include "llvm/Target/TargetOptions.h"65#include "llvm/Transforms/Scalar.h"66 67// FIXME: Although all systems tested with (Linux, OS X), do not need this68//        header file included. A user on ubuntu reported, undefined symbols69//        for stderr, and fprintf, and the addition of this include fixed the70//        issue for them. Given that LLVM's best practices include the goal71//        of reducing the number of redundant header files included, the72//        correct solution would be to find out why these symbols are not73//        defined for the system in question, and fix the issue by finding out74//        which LLVM header file, if any, would include these symbols.75#include <cstdio>76 77#include <sstream>78#include <stdexcept>79 80#include <inttypes.h>81 82#include <unwind.h>83 84#ifndef USE_GLOBAL_STR_CONSTS85#define USE_GLOBAL_STR_CONSTS true86#endif87 88llvm::ExitOnError ExitOnErr;89 90//91// Example types92//93 94/// This is our simplistic type info95struct OurExceptionType_t {96  /// type info type97  int type;98};99 100 101/// This is our Exception class which relies on a negative offset to calculate102/// pointers to its instances from pointers to its unwindException member.103///104/// Note: The above unwind.h defines struct _Unwind_Exception to be aligned105///       on a double word boundary. This is necessary to match the standard:106///       http://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html107struct OurBaseException_t {108  struct OurExceptionType_t type;109 110  // Note: This is properly aligned in unwind.h111  struct _Unwind_Exception unwindException;112};113 114 115// Note: Not needed since we are C++116typedef struct OurBaseException_t OurException;117typedef struct _Unwind_Exception OurUnwindException;118 119//120// Various globals used to support typeinfo and generatted exceptions in121// general122//123 124static std::map<std::string, llvm::Value*> namedValues;125 126int64_t ourBaseFromUnwindOffset;127 128const unsigned char ourBaseExcpClassChars[] =129{'o', 'b', 'j', '\0', 'b', 'a', 's', '\0'};130 131 132static uint64_t ourBaseExceptionClass = 0;133 134static std::vector<std::string> ourTypeInfoNames;135static std::map<int, std::string> ourTypeInfoNamesIndex;136 137static llvm::StructType *ourTypeInfoType;138static llvm::StructType *ourCaughtResultType;139static llvm::StructType *ourExceptionType;140static llvm::StructType *ourUnwindExceptionType;141 142static llvm::ConstantInt *ourExceptionNotThrownState;143static llvm::ConstantInt *ourExceptionThrownState;144static llvm::ConstantInt *ourExceptionCaughtState;145 146typedef std::vector<std::string> ArgNames;147typedef std::vector<llvm::Type*> ArgTypes;148typedef llvm::ArrayRef<llvm::Type *> TypeArray;149 150//151// Code Generation Utilities152//153 154/// Utility used to create a function, both declarations and definitions155/// @param module for module instance156/// @param retType function return type157/// @param theArgTypes function's ordered argument types158/// @param theArgNames function's ordered arguments needed if use of this159///        function corresponds to a function definition. Use empty160///        aggregate for function declarations.161/// @param functName function name162/// @param linkage function linkage163/// @param declarationOnly for function declarations164/// @param isVarArg function uses vararg arguments165/// @returns function instance166llvm::Function *createFunction(llvm::Module &module,167                               llvm::Type *retType,168                               const ArgTypes &theArgTypes,169                               const ArgNames &theArgNames,170                               const std::string &functName,171                               llvm::GlobalValue::LinkageTypes linkage,172                               bool declarationOnly,173                               bool isVarArg) {174  llvm::FunctionType *functType =175    llvm::FunctionType::get(retType, theArgTypes, isVarArg);176  llvm::Function *ret =177    llvm::Function::Create(functType, linkage, functName, &module);178  if (!ret || declarationOnly)179    return(ret);180 181  namedValues.clear();182  unsigned i = 0;183  for (llvm::Function::arg_iterator argIndex = ret->arg_begin();184       i != theArgNames.size();185       ++argIndex, ++i) {186 187    argIndex->setName(theArgNames[i]);188    namedValues[theArgNames[i]] = argIndex;189  }190 191  return(ret);192}193 194 195/// Create an alloca instruction in the entry block of196/// the parent function.  This is used for mutable variables etc.197/// @param function parent instance198/// @param varName stack variable name199/// @param type stack variable type200/// @param initWith optional constant initialization value201/// @returns AllocaInst instance202static llvm::AllocaInst *createEntryBlockAlloca(llvm::Function &function,203                                                const std::string &varName,204                                                llvm::Type *type,205                                                llvm::Constant *initWith = 0) {206  llvm::BasicBlock &block = function.getEntryBlock();207  llvm::IRBuilder<> tmp(&block, block.begin());208  llvm::AllocaInst *ret = tmp.CreateAlloca(type, 0, varName);209 210  if (initWith)211    tmp.CreateStore(initWith, ret);212 213  return(ret);214}215 216 217//218// Code Generation Utilities End219//220 221//222// Runtime C Library functions223//224 225namespace {226template <typename Type_>227uintptr_t ReadType(const uint8_t *&p) {228  Type_ value;229  memcpy(&value, p, sizeof(Type_));230  p += sizeof(Type_);231  return static_cast<uintptr_t>(value);232}233}234 235// Note: using an extern "C" block so that static functions can be used236extern "C" {237 238// Note: Better ways to decide on bit width239//240/// Prints a 32 bit number, according to the format, to stderr.241/// @param intToPrint integer to print242/// @param format printf like format to use when printing243void print32Int(int intToPrint, const char *format) {244  if (format) {245    // Note: No NULL check246    fprintf(stderr, format, intToPrint);247  }248  else {249    // Note: No NULL check250    fprintf(stderr, "::print32Int(...):NULL arg.\n");251  }252}253 254 255// Note: Better ways to decide on bit width256//257/// Prints a 64 bit number, according to the format, to stderr.258/// @param intToPrint integer to print259/// @param format printf like format to use when printing260void print64Int(long int intToPrint, const char *format) {261  if (format) {262    // Note: No NULL check263    fprintf(stderr, format, intToPrint);264  }265  else {266    // Note: No NULL check267    fprintf(stderr, "::print64Int(...):NULL arg.\n");268  }269}270 271 272/// Prints a C string to stderr273/// @param toPrint string to print274void printStr(char *toPrint) {275  if (toPrint) {276    fprintf(stderr, "%s", toPrint);277  }278  else {279    fprintf(stderr, "::printStr(...):NULL arg.\n");280  }281}282 283 284/// Deletes the true previously allocated exception whose address285/// is calculated from the supplied OurBaseException_t::unwindException286/// member address. Handles (ignores), NULL pointers.287/// @param expToDelete exception to delete288void deleteOurException(OurUnwindException *expToDelete) {289#ifdef DEBUG290  fprintf(stderr,291          "deleteOurException(...).\n");292#endif293 294  if (expToDelete &&295      (expToDelete->exception_class == ourBaseExceptionClass)) {296 297    free(((char*) expToDelete) + ourBaseFromUnwindOffset);298  }299}300 301 302/// This function is the struct _Unwind_Exception API mandated delete function303/// used by foreign exception handlers when deleting our exception304/// (OurException), instances.305/// @param reason See @link http://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html306/// @unlink307/// @param expToDelete exception instance to delete308void deleteFromUnwindOurException(_Unwind_Reason_Code reason,309                                  OurUnwindException *expToDelete) {310#ifdef DEBUG311  fprintf(stderr,312          "deleteFromUnwindOurException(...).\n");313#endif314 315  deleteOurException(expToDelete);316}317 318 319/// Creates (allocates on the heap), an exception (OurException instance),320/// of the supplied type info type.321/// @param type type info type322OurUnwindException *createOurException(int type) {323  size_t size = sizeof(OurException);324  OurException *ret = (OurException*) memset(malloc(size), 0, size);325  (ret->type).type = type;326  (ret->unwindException).exception_class = ourBaseExceptionClass;327  (ret->unwindException).exception_cleanup = deleteFromUnwindOurException;328 329  return(&(ret->unwindException));330}331 332 333/// Read a uleb128 encoded value and advance pointer334/// See Variable Length Data in:335/// @link http://dwarfstd.org/Dwarf3.pdf @unlink336/// @param data reference variable holding memory pointer to decode from337/// @returns decoded value338static uintptr_t readULEB128(const uint8_t **data) {339  uintptr_t result = 0;340  uintptr_t shift = 0;341  unsigned char byte;342  const uint8_t *p = *data;343 344  do {345    byte = *p++;346    result |= (byte & 0x7f) << shift;347    shift += 7;348  }349  while (byte & 0x80);350 351  *data = p;352 353  return result;354}355 356 357/// Read a sleb128 encoded value and advance pointer358/// See Variable Length Data in:359/// @link http://dwarfstd.org/Dwarf3.pdf @unlink360/// @param data reference variable holding memory pointer to decode from361/// @returns decoded value362static uintptr_t readSLEB128(const uint8_t **data) {363  uintptr_t result = 0;364  uintptr_t shift = 0;365  unsigned char byte;366  const uint8_t *p = *data;367 368  do {369    byte = *p++;370    result |= (byte & 0x7f) << shift;371    shift += 7;372  }373  while (byte & 0x80);374 375  *data = p;376 377  if ((byte & 0x40) && (shift < (sizeof(result) << 3))) {378    result |= (~0 << shift);379  }380 381  return result;382}383 384unsigned getEncodingSize(uint8_t Encoding) {385  if (Encoding == llvm::dwarf::DW_EH_PE_omit)386    return 0;387 388  switch (Encoding & 0x0F) {389  case llvm::dwarf::DW_EH_PE_absptr:390    return sizeof(uintptr_t);391  case llvm::dwarf::DW_EH_PE_udata2:392    return sizeof(uint16_t);393  case llvm::dwarf::DW_EH_PE_udata4:394    return sizeof(uint32_t);395  case llvm::dwarf::DW_EH_PE_udata8:396    return sizeof(uint64_t);397  case llvm::dwarf::DW_EH_PE_sdata2:398    return sizeof(int16_t);399  case llvm::dwarf::DW_EH_PE_sdata4:400    return sizeof(int32_t);401  case llvm::dwarf::DW_EH_PE_sdata8:402    return sizeof(int64_t);403  default:404    // not supported405    abort();406  }407}408 409/// Read a pointer encoded value and advance pointer410/// See Variable Length Data in:411/// @link http://dwarfstd.org/Dwarf3.pdf @unlink412/// @param data reference variable holding memory pointer to decode from413/// @param encoding dwarf encoding type414/// @returns decoded value415static uintptr_t readEncodedPointer(const uint8_t **data, uint8_t encoding) {416  uintptr_t result = 0;417  const uint8_t *p = *data;418 419  if (encoding == llvm::dwarf::DW_EH_PE_omit)420    return(result);421 422  // first get value423  switch (encoding & 0x0F) {424    case llvm::dwarf::DW_EH_PE_absptr:425      result = ReadType<uintptr_t>(p);426      break;427    case llvm::dwarf::DW_EH_PE_uleb128:428      result = readULEB128(&p);429      break;430      // Note: This case has not been tested431    case llvm::dwarf::DW_EH_PE_sleb128:432      result = readSLEB128(&p);433      break;434    case llvm::dwarf::DW_EH_PE_udata2:435      result = ReadType<uint16_t>(p);436      break;437    case llvm::dwarf::DW_EH_PE_udata4:438      result = ReadType<uint32_t>(p);439      break;440    case llvm::dwarf::DW_EH_PE_udata8:441      result = ReadType<uint64_t>(p);442      break;443    case llvm::dwarf::DW_EH_PE_sdata2:444      result = ReadType<int16_t>(p);445      break;446    case llvm::dwarf::DW_EH_PE_sdata4:447      result = ReadType<int32_t>(p);448      break;449    case llvm::dwarf::DW_EH_PE_sdata8:450      result = ReadType<int64_t>(p);451      break;452    default:453      // not supported454      abort();455      break;456  }457 458  // then add relative offset459  switch (encoding & 0x70) {460    case llvm::dwarf::DW_EH_PE_absptr:461      // do nothing462      break;463    case llvm::dwarf::DW_EH_PE_pcrel:464      result += (uintptr_t)(*data);465      break;466    case llvm::dwarf::DW_EH_PE_textrel:467    case llvm::dwarf::DW_EH_PE_datarel:468    case llvm::dwarf::DW_EH_PE_funcrel:469    case llvm::dwarf::DW_EH_PE_aligned:470    default:471      // not supported472      abort();473      break;474  }475 476  // then apply indirection477  if (encoding & llvm::dwarf::DW_EH_PE_indirect) {478    result = *((uintptr_t*)result);479  }480 481  *data = p;482 483  return result;484}485 486 487/// Deals with Dwarf actions matching our type infos488/// (OurExceptionType_t instances). Returns whether or not a dwarf emitted489/// action matches the supplied exception type. If such a match succeeds,490/// the resultAction argument will be set with > 0 index value. Only491/// corresponding llvm.eh.selector type info arguments, cleanup arguments492/// are supported. Filters are not supported.493/// See Variable Length Data in:494/// @link http://dwarfstd.org/Dwarf3.pdf @unlink495/// Also see @link http://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html @unlink496/// @param resultAction reference variable which will be set with result497/// @param classInfo our array of type info pointers (to globals)498/// @param actionEntry index into above type info array or 0 (clean up).499///        We do not support filters.500/// @param exceptionClass exception class (_Unwind_Exception::exception_class)501///        of thrown exception.502/// @param exceptionObject thrown _Unwind_Exception instance.503/// @returns whether or not a type info was found. False is returned if only504///          a cleanup was found505static bool handleActionValue(int64_t *resultAction,506                              uint8_t TTypeEncoding,507                              const uint8_t *ClassInfo,508                              uintptr_t actionEntry,509                              uint64_t exceptionClass,510                              struct _Unwind_Exception *exceptionObject) {511  bool ret = false;512 513  if (!resultAction ||514      !exceptionObject ||515      (exceptionClass != ourBaseExceptionClass))516    return(ret);517 518  struct OurBaseException_t *excp = (struct OurBaseException_t*)519  (((char*) exceptionObject) + ourBaseFromUnwindOffset);520  struct OurExceptionType_t *excpType = &(excp->type);521  int type = excpType->type;522 523#ifdef DEBUG524  fprintf(stderr,525          "handleActionValue(...): exceptionObject = <%p>, "526          "excp = <%p>.\n",527          (void*)exceptionObject,528          (void*)excp);529#endif530 531  const uint8_t *actionPos = (uint8_t*) actionEntry,532  *tempActionPos;533  int64_t typeOffset = 0,534  actionOffset;535 536  for (int i = 0; true; ++i) {537    // Each emitted dwarf action corresponds to a 2 tuple of538    // type info address offset, and action offset to the next539    // emitted action.540    typeOffset = readSLEB128(&actionPos);541    tempActionPos = actionPos;542    actionOffset = readSLEB128(&tempActionPos);543 544#ifdef DEBUG545    fprintf(stderr,546            "handleActionValue(...):typeOffset: <%" PRIi64 ">, "547            "actionOffset: <%" PRIi64 ">.\n",548            typeOffset,549            actionOffset);550#endif551    assert((typeOffset >= 0) &&552           "handleActionValue(...):filters are not supported.");553 554    // Note: A typeOffset == 0 implies that a cleanup llvm.eh.selector555    //       argument has been matched.556    if (typeOffset > 0) {557#ifdef DEBUG558      fprintf(stderr,559              "handleActionValue(...):actionValue <%d> found.\n",560              i);561#endif562      unsigned EncSize = getEncodingSize(TTypeEncoding);563      const uint8_t *EntryP = ClassInfo - typeOffset * EncSize;564      uintptr_t P = readEncodedPointer(&EntryP, TTypeEncoding);565      struct OurExceptionType_t *ThisClassInfo =566        reinterpret_cast<struct OurExceptionType_t *>(P);567      if (ThisClassInfo->type == type) {568        *resultAction = i + 1;569        ret = true;570        break;571      }572    }573 574#ifdef DEBUG575    fprintf(stderr,576            "handleActionValue(...):actionValue not found.\n");577#endif578    if (!actionOffset)579      break;580 581    actionPos += actionOffset;582  }583 584  return(ret);585}586 587 588/// Deals with the Language specific data portion of the emitted dwarf code.589/// See @link http://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html @unlink590/// @param version unsupported (ignored), unwind version591/// @param lsda language specific data area592/// @param _Unwind_Action actions minimally supported unwind stage593///        (forced specifically not supported)594/// @param exceptionClass exception class (_Unwind_Exception::exception_class)595///        of thrown exception.596/// @param exceptionObject thrown _Unwind_Exception instance.597/// @param context unwind system context598/// @returns minimally supported unwinding control indicator599static _Unwind_Reason_Code handleLsda(int version, const uint8_t *lsda,600                                      _Unwind_Action actions,601                                      _Unwind_Exception_Class exceptionClass,602                                      struct _Unwind_Exception *exceptionObject,603                                      struct _Unwind_Context *context) {604  _Unwind_Reason_Code ret = _URC_CONTINUE_UNWIND;605 606  if (!lsda)607    return(ret);608 609#ifdef DEBUG610  fprintf(stderr,611          "handleLsda(...):lsda is non-zero.\n");612#endif613 614  // Get the current instruction pointer and offset it before next615  // instruction in the current frame which threw the exception.616  uintptr_t pc = _Unwind_GetIP(context)-1;617 618  // Get beginning current frame's code (as defined by the619  // emitted dwarf code)620  uintptr_t funcStart = _Unwind_GetRegionStart(context);621  uintptr_t pcOffset = pc - funcStart;622  const uint8_t *ClassInfo = NULL;623 624  // Note: See JITDwarfEmitter::EmitExceptionTable(...) for corresponding625  //       dwarf emission626 627  // Parse LSDA header.628  uint8_t lpStartEncoding = *lsda++;629 630  if (lpStartEncoding != llvm::dwarf::DW_EH_PE_omit) {631    readEncodedPointer(&lsda, lpStartEncoding);632  }633 634  uint8_t ttypeEncoding = *lsda++;635  uintptr_t classInfoOffset;636 637  if (ttypeEncoding != llvm::dwarf::DW_EH_PE_omit) {638    // Calculate type info locations in emitted dwarf code which639    // were flagged by type info arguments to llvm.eh.selector640    // intrinsic641    classInfoOffset = readULEB128(&lsda);642    ClassInfo = lsda + classInfoOffset;643  }644 645  // Walk call-site table looking for range that646  // includes current PC.647 648  uint8_t         callSiteEncoding = *lsda++;649  uint32_t        callSiteTableLength = readULEB128(&lsda);650  const uint8_t   *callSiteTableStart = lsda;651  const uint8_t   *callSiteTableEnd = callSiteTableStart +652  callSiteTableLength;653  const uint8_t   *actionTableStart = callSiteTableEnd;654  const uint8_t   *callSitePtr = callSiteTableStart;655 656  while (callSitePtr < callSiteTableEnd) {657    uintptr_t start = readEncodedPointer(&callSitePtr,658                                         callSiteEncoding);659    uintptr_t length = readEncodedPointer(&callSitePtr,660                                          callSiteEncoding);661    uintptr_t landingPad = readEncodedPointer(&callSitePtr,662                                              callSiteEncoding);663 664    // Note: Action value665    uintptr_t actionEntry = readULEB128(&callSitePtr);666 667    if (exceptionClass != ourBaseExceptionClass) {668      // We have been notified of a foreign exception being thrown,669      // and we therefore need to execute cleanup landing pads670      actionEntry = 0;671    }672 673    if (landingPad == 0) {674#ifdef DEBUG675      fprintf(stderr,676              "handleLsda(...): No landing pad found.\n");677#endif678 679      continue; // no landing pad for this entry680    }681 682    if (actionEntry) {683      actionEntry += ((uintptr_t) actionTableStart) - 1;684    }685    else {686#ifdef DEBUG687      fprintf(stderr,688              "handleLsda(...):No action table found.\n");689#endif690    }691 692    bool exceptionMatched = false;693 694    if ((start <= pcOffset) && (pcOffset < (start + length))) {695#ifdef DEBUG696      fprintf(stderr,697              "handleLsda(...): Landing pad found.\n");698#endif699      int64_t actionValue = 0;700 701      if (actionEntry) {702        exceptionMatched = handleActionValue(&actionValue,703                                             ttypeEncoding,704                                             ClassInfo,705                                             actionEntry,706                                             exceptionClass,707                                             exceptionObject);708      }709 710      if (!(actions & _UA_SEARCH_PHASE)) {711#ifdef DEBUG712        fprintf(stderr,713                "handleLsda(...): installed landing pad "714                "context.\n");715#endif716 717        // Found landing pad for the PC.718        // Set Instruction Pointer to so we re-enter function719        // at landing pad. The landing pad is created by the720        // compiler to take two parameters in registers.721        _Unwind_SetGR(context,722                      __builtin_eh_return_data_regno(0),723                      (uintptr_t)exceptionObject);724 725        // Note: this virtual register directly corresponds726        //       to the return of the llvm.eh.selector intrinsic727        if (!actionEntry || !exceptionMatched) {728          // We indicate cleanup only729          _Unwind_SetGR(context,730                        __builtin_eh_return_data_regno(1),731                        0);732        }733        else {734          // Matched type info index of llvm.eh.selector intrinsic735          // passed here.736          _Unwind_SetGR(context,737                        __builtin_eh_return_data_regno(1),738                        actionValue);739        }740 741        // To execute landing pad set here742        _Unwind_SetIP(context, funcStart + landingPad);743        ret = _URC_INSTALL_CONTEXT;744      }745      else if (exceptionMatched) {746#ifdef DEBUG747        fprintf(stderr,748                "handleLsda(...): setting handler found.\n");749#endif750        ret = _URC_HANDLER_FOUND;751      }752      else {753        // Note: Only non-clean up handlers are marked as754        //       found. Otherwise the clean up handlers will be755        //       re-found and executed during the clean up756        //       phase.757#ifdef DEBUG758        fprintf(stderr,759                "handleLsda(...): cleanup handler found.\n");760#endif761      }762 763      break;764    }765  }766 767  return(ret);768}769 770 771/// This is the personality function which is embedded (dwarf emitted), in the772/// dwarf unwind info block. Again see: JITDwarfEmitter.cpp.773/// See @link http://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html @unlink774/// @param version unsupported (ignored), unwind version775/// @param _Unwind_Action actions minimally supported unwind stage776///        (forced specifically not supported)777/// @param exceptionClass exception class (_Unwind_Exception::exception_class)778///        of thrown exception.779/// @param exceptionObject thrown _Unwind_Exception instance.780/// @param context unwind system context781/// @returns minimally supported unwinding control indicator782_Unwind_Reason_Code ourPersonality(int version, _Unwind_Action actions,783                                   _Unwind_Exception_Class exceptionClass,784                                   struct _Unwind_Exception *exceptionObject,785                                   struct _Unwind_Context *context) {786#ifdef DEBUG787  fprintf(stderr,788          "We are in ourPersonality(...):actions is <%d>.\n",789          actions);790 791  if (actions & _UA_SEARCH_PHASE) {792    fprintf(stderr, "ourPersonality(...):In search phase.\n");793  }794  else {795    fprintf(stderr, "ourPersonality(...):In non-search phase.\n");796  }797#endif798 799  const uint8_t *lsda = (const uint8_t *)_Unwind_GetLanguageSpecificData(context);800 801#ifdef DEBUG802  fprintf(stderr,803          "ourPersonality(...):lsda = <%p>.\n",804          (void*)lsda);805#endif806 807  // The real work of the personality function is captured here808  return(handleLsda(version,809                    lsda,810                    actions,811                    exceptionClass,812                    exceptionObject,813                    context));814}815 816 817/// Generates our _Unwind_Exception class from a given character array.818/// thereby handling arbitrary lengths (not in standard), and handling819/// embedded \0s.820/// See @link http://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html @unlink821/// @param classChars char array to encode. NULL values not checkedf822/// @param classCharsSize number of chars in classChars. Value is not checked.823/// @returns class value824uint64_t genClass(const unsigned char classChars[], size_t classCharsSize)825{826  uint64_t ret = classChars[0];827 828  for (unsigned i = 1; i < classCharsSize; ++i) {829    ret <<= 8;830    ret += classChars[i];831  }832 833  return(ret);834}835 836} // extern "C"837 838//839// Runtime C Library functions End840//841 842//843// Code generation functions844//845 846/// Generates code to print given constant string847/// @param context llvm context848/// @param module code for module instance849/// @param builder builder instance850/// @param toPrint string to print851/// @param useGlobal A value of true (default) indicates a GlobalValue is852///        generated, and is used to hold the constant string. A value of853///        false indicates that the constant string will be stored on the854///        stack.855void generateStringPrint(llvm::LLVMContext &context,856                         llvm::Module &module,857                         llvm::IRBuilder<> &builder,858                         std::string toPrint,859                         bool useGlobal = true) {860  llvm::Function *printFunct = module.getFunction("printStr");861 862  llvm::Value *stringVar;863  llvm::Constant *stringConstant =864  llvm::ConstantDataArray::getString(context, toPrint);865 866  if (useGlobal) {867    // Note: Does not work without allocation868    stringVar =869    new llvm::GlobalVariable(module,870                             stringConstant->getType(),871                             true,872                             llvm::GlobalValue::PrivateLinkage,873                             stringConstant,874                             "");875  }876  else {877    stringVar = builder.CreateAlloca(stringConstant->getType());878    builder.CreateStore(stringConstant, stringVar);879  }880 881  llvm::Value *cast = builder.CreatePointerCast(stringVar,882                                                builder.getPtrTy());883  builder.CreateCall(printFunct, cast);884}885 886/// Generates code to print given runtime integer according to constant887/// string format, and a given print function.888/// @param context llvm context889/// @param module code for module instance890/// @param builder builder instance891/// @param printFunct function used to "print" integer892/// @param toPrint string to print893/// @param format printf like formatting string for print894/// @param useGlobal A value of true (default) indicates a GlobalValue is895///        generated, and is used to hold the constant string. A value of896///        false indicates that the constant string will be stored on the897///        stack.898void generateIntegerPrint(llvm::LLVMContext &context, llvm::Module &module,899                          llvm::IRBuilder<> &builder,900                          llvm::Function &printFunct, llvm::Value *toPrint,901                          std::string format, bool useGlobal = true) {902  llvm::Constant *stringConstant =903    llvm::ConstantDataArray::getString(context, format);904  llvm::Value *stringVar;905 906  if (useGlobal) {907    // Note: Does not seem to work without allocation908    stringVar =909    new llvm::GlobalVariable(module,910                             stringConstant->getType(),911                             true,912                             llvm::GlobalValue::PrivateLinkage,913                             stringConstant,914                             "");915  }916  else {917    stringVar = builder.CreateAlloca(stringConstant->getType());918    builder.CreateStore(stringConstant, stringVar);919  }920 921  llvm::Value *cast = builder.CreateBitCast(stringVar,922                                            builder.getPtrTy());923  builder.CreateCall(&printFunct, {toPrint, cast});924}925 926/// Generates code to handle finally block type semantics: always runs927/// regardless of whether a thrown exception is passing through or the928/// parent function is simply exiting. In addition to printing some state929/// to stderr, this code will resume the exception handling--runs the930/// unwind resume block, if the exception has not been previously caught931/// by a catch clause, and will otherwise execute the end block (terminator932/// block). In addition this function creates the corresponding function's933/// stack storage for the exception pointer and catch flag status.934/// @param context llvm context935/// @param module code for module instance936/// @param builder builder instance937/// @param toAddTo parent function to add block to938/// @param blockName block name of new "finally" block.939/// @param functionId output id used for printing940/// @param terminatorBlock terminator "end" block941/// @param unwindResumeBlock unwind resume block942/// @param exceptionCaughtFlag reference exception caught/thrown status storage943/// @param exceptionStorage reference to exception pointer storage944/// @param caughtResultStorage reference to landingpad result storage945/// @returns newly created block946static llvm::BasicBlock *createFinallyBlock(llvm::LLVMContext &context,947                                            llvm::Module &module,948                                            llvm::IRBuilder<> &builder,949                                            llvm::Function &toAddTo,950                                            std::string &blockName,951                                            std::string &functionId,952                                            llvm::BasicBlock &terminatorBlock,953                                            llvm::BasicBlock &unwindResumeBlock,954                                            llvm::Value **exceptionCaughtFlag,955                                            llvm::Value **exceptionStorage,956                                            llvm::Value **caughtResultStorage) {957  assert(exceptionCaughtFlag &&958         "ExceptionDemo::createFinallyBlock(...):exceptionCaughtFlag "959         "is NULL");960  assert(exceptionStorage &&961         "ExceptionDemo::createFinallyBlock(...):exceptionStorage "962         "is NULL");963  assert(caughtResultStorage &&964         "ExceptionDemo::createFinallyBlock(...):caughtResultStorage "965         "is NULL");966 967  *exceptionCaughtFlag = createEntryBlockAlloca(toAddTo,968                                         "exceptionCaught",969                                         ourExceptionNotThrownState->getType(),970                                         ourExceptionNotThrownState);971 972  llvm::PointerType *exceptionStorageType = builder.getPtrTy();973  *exceptionStorage = createEntryBlockAlloca(toAddTo,974                                             "exceptionStorage",975                                             exceptionStorageType,976                                             llvm::ConstantPointerNull::get(977                                               exceptionStorageType));978  *caughtResultStorage = createEntryBlockAlloca(toAddTo,979                                              "caughtResultStorage",980                                              ourCaughtResultType,981                                              llvm::ConstantAggregateZero::get(982                                                ourCaughtResultType));983 984  llvm::BasicBlock *ret = llvm::BasicBlock::Create(context,985                                                   blockName,986                                                   &toAddTo);987 988  builder.SetInsertPoint(ret);989 990  std::ostringstream bufferToPrint;991  bufferToPrint << "Gen: Executing finally block "992    << blockName << " in " << functionId << "\n";993  generateStringPrint(context,994                      module,995                      builder,996                      bufferToPrint.str(),997                      USE_GLOBAL_STR_CONSTS);998 999  llvm::SwitchInst *theSwitch = builder.CreateSwitch(1000      builder.CreateLoad(ourExceptionNotThrownState->getType(),1001                         *exceptionCaughtFlag),1002      &terminatorBlock, 2);1003  theSwitch->addCase(ourExceptionCaughtState, &terminatorBlock);1004  theSwitch->addCase(ourExceptionThrownState, &unwindResumeBlock);1005 1006  return(ret);1007}1008 1009 1010/// Generates catch block semantics which print a string to indicate type of1011/// catch executed, sets an exception caught flag, and executes passed in1012/// end block (terminator block).1013/// @param context llvm context1014/// @param module code for module instance1015/// @param builder builder instance1016/// @param toAddTo parent function to add block to1017/// @param blockName block name of new "catch" block.1018/// @param functionId output id used for printing1019/// @param terminatorBlock terminator "end" block1020/// @param exceptionCaughtFlag exception caught/thrown status1021/// @returns newly created block1022static llvm::BasicBlock *createCatchBlock(llvm::LLVMContext &context,1023                                          llvm::Module &module,1024                                          llvm::IRBuilder<> &builder,1025                                          llvm::Function &toAddTo,1026                                          std::string &blockName,1027                                          std::string &functionId,1028                                          llvm::BasicBlock &terminatorBlock,1029                                          llvm::Value &exceptionCaughtFlag) {1030 1031  llvm::BasicBlock *ret = llvm::BasicBlock::Create(context,1032                                                   blockName,1033                                                   &toAddTo);1034 1035  builder.SetInsertPoint(ret);1036 1037  std::ostringstream bufferToPrint;1038  bufferToPrint << "Gen: Executing catch block "1039  << blockName1040  << " in "1041  << functionId1042  << std::endl;1043  generateStringPrint(context,1044                      module,1045                      builder,1046                      bufferToPrint.str(),1047                      USE_GLOBAL_STR_CONSTS);1048  builder.CreateStore(ourExceptionCaughtState, &exceptionCaughtFlag);1049  builder.CreateBr(&terminatorBlock);1050 1051  return(ret);1052}1053 1054 1055/// Generates a function which invokes a function (toInvoke) and, whose1056/// unwind block will "catch" the type info types correspondingly held in the1057/// exceptionTypesToCatch argument. If the toInvoke function throws an1058/// exception which does not match any type info types contained in1059/// exceptionTypesToCatch, the generated code will call _Unwind_Resume1060/// with the raised exception. On the other hand the generated code will1061/// normally exit if the toInvoke function does not throw an exception.1062/// The generated "finally" block is always run regardless of the cause of1063/// the generated function exit.1064/// The generated function is returned after being verified.1065/// @param module code for module instance1066/// @param builder builder instance1067/// @param fpm a function pass manager holding optional IR to IR1068///        transformations1069/// @param toInvoke inner function to invoke1070/// @param ourId id used to printing purposes1071/// @param numExceptionsToCatch length of exceptionTypesToCatch array1072/// @param exceptionTypesToCatch array of type info types to "catch"1073/// @returns generated function1074static llvm::Function *createCatchWrappedInvokeFunction(1075    llvm::Module &module, llvm::IRBuilder<> &builder,1076    llvm::legacy::FunctionPassManager &fpm, llvm::Function &toInvoke,1077    std::string ourId, unsigned numExceptionsToCatch,1078    unsigned exceptionTypesToCatch[]) {1079 1080  llvm::LLVMContext &context = module.getContext();1081  llvm::Function *toPrint32Int = module.getFunction("print32Int");1082 1083  ArgTypes argTypes;1084  argTypes.push_back(builder.getInt32Ty());1085 1086  ArgNames argNames;1087  argNames.push_back("exceptTypeToThrow");1088 1089  llvm::Function *ret = createFunction(module,1090                                       builder.getVoidTy(),1091                                       argTypes,1092                                       argNames,1093                                       ourId,1094                                       llvm::Function::ExternalLinkage,1095                                       false,1096                                       false);1097 1098  // Block which calls invoke1099  llvm::BasicBlock *entryBlock = llvm::BasicBlock::Create(context,1100                                                          "entry",1101                                                          ret);1102  // Normal block for invoke1103  llvm::BasicBlock *normalBlock = llvm::BasicBlock::Create(context,1104                                                           "normal",1105                                                           ret);1106  // Unwind block for invoke1107  llvm::BasicBlock *exceptionBlock = llvm::BasicBlock::Create(context,1108                                                              "exception",1109                                                              ret);1110 1111  // Block which routes exception to correct catch handler block1112  llvm::BasicBlock *exceptionRouteBlock = llvm::BasicBlock::Create(context,1113                                                             "exceptionRoute",1114                                                             ret);1115 1116  // Foreign exception handler1117  llvm::BasicBlock *externalExceptionBlock = llvm::BasicBlock::Create(context,1118                                                          "externalException",1119                                                          ret);1120 1121  // Block which calls _Unwind_Resume1122  llvm::BasicBlock *unwindResumeBlock = llvm::BasicBlock::Create(context,1123                                                               "unwindResume",1124                                                               ret);1125 1126  // Clean up block which delete exception if needed1127  llvm::BasicBlock *endBlock = llvm::BasicBlock::Create(context, "end", ret);1128 1129  std::string nextName;1130  std::vector<llvm::BasicBlock*> catchBlocks(numExceptionsToCatch);1131  llvm::Value *exceptionCaughtFlag = NULL;1132  llvm::Value *exceptionStorage = NULL;1133  llvm::Value *caughtResultStorage = NULL;1134 1135  // Finally block which will branch to unwindResumeBlock if1136  // exception is not caught. Initializes/allocates stack locations.1137  llvm::BasicBlock *finallyBlock = createFinallyBlock(context,1138                                                      module,1139                                                      builder,1140                                                      *ret,1141                                                      nextName = "finally",1142                                                      ourId,1143                                                      *endBlock,1144                                                      *unwindResumeBlock,1145                                                      &exceptionCaughtFlag,1146                                                      &exceptionStorage,1147                                                      &caughtResultStorage1148                                                      );1149 1150  for (unsigned i = 0; i < numExceptionsToCatch; ++i) {1151    nextName = ourTypeInfoNames[exceptionTypesToCatch[i]];1152 1153    // One catch block per type info to be caught1154    catchBlocks[i] = createCatchBlock(context,1155                                      module,1156                                      builder,1157                                      *ret,1158                                      nextName,1159                                      ourId,1160                                      *finallyBlock,1161                                      *exceptionCaughtFlag);1162  }1163 1164  // Entry Block1165 1166  builder.SetInsertPoint(entryBlock);1167 1168  std::vector<llvm::Value*> args;1169  args.push_back(namedValues["exceptTypeToThrow"]);1170  builder.CreateInvoke(&toInvoke,1171                       normalBlock,1172                       exceptionBlock,1173                       args);1174 1175  // End Block1176 1177  builder.SetInsertPoint(endBlock);1178 1179  generateStringPrint(context,1180                      module,1181                      builder,1182                      "Gen: In end block: exiting in " + ourId + ".\n",1183                      USE_GLOBAL_STR_CONSTS);1184  llvm::Function *deleteOurException = module.getFunction("deleteOurException");1185 1186  // Note: function handles NULL exceptions1187  builder.CreateCall(deleteOurException,1188                     builder.CreateLoad(builder.getPtrTy(), exceptionStorage));1189  builder.CreateRetVoid();1190 1191  // Normal Block1192 1193  builder.SetInsertPoint(normalBlock);1194 1195  generateStringPrint(context,1196                      module,1197                      builder,1198                      "Gen: No exception in " + ourId + "!\n",1199                      USE_GLOBAL_STR_CONSTS);1200 1201  // Finally block is always called1202  builder.CreateBr(finallyBlock);1203 1204  // Unwind Resume Block1205 1206  builder.SetInsertPoint(unwindResumeBlock);1207 1208  builder.CreateResume(1209      builder.CreateLoad(ourCaughtResultType, caughtResultStorage));1210 1211  // Exception Block1212 1213  builder.SetInsertPoint(exceptionBlock);1214 1215  llvm::Function *personality = module.getFunction("ourPersonality");1216  ret->setPersonalityFn(personality);1217 1218  llvm::LandingPadInst *caughtResult =1219    builder.CreateLandingPad(ourCaughtResultType,1220                             numExceptionsToCatch,1221                             "landingPad");1222 1223  caughtResult->setCleanup(true);1224 1225  for (unsigned i = 0; i < numExceptionsToCatch; ++i) {1226    // Set up type infos to be caught1227    caughtResult->addClause(module.getGlobalVariable(1228                             ourTypeInfoNames[exceptionTypesToCatch[i]]));1229  }1230 1231  llvm::Value *unwindException = builder.CreateExtractValue(caughtResult, 0);1232  llvm::Value *retTypeInfoIndex = builder.CreateExtractValue(caughtResult, 1);1233 1234  // FIXME: Redundant storage which, beyond utilizing value of1235  //        caughtResultStore for unwindException storage, may be alleviated1236  //        altogether with a block rearrangement1237  builder.CreateStore(caughtResult, caughtResultStorage);1238  builder.CreateStore(unwindException, exceptionStorage);1239  builder.CreateStore(ourExceptionThrownState, exceptionCaughtFlag);1240 1241  // Retrieve exception_class member from thrown exception1242  // (_Unwind_Exception instance). This member tells us whether or not1243  // the exception is foreign.1244  llvm::Value *unwindExceptionClass = builder.CreateLoad(1245      builder.getInt64Ty(),1246      builder.CreateStructGEP(ourUnwindExceptionType, unwindException, 0));1247 1248  // Branch to the externalExceptionBlock if the exception is foreign or1249  // to a catch router if not. Either way the finally block will be run.1250  builder.CreateCondBr(builder.CreateICmpEQ(unwindExceptionClass,1251                            llvm::ConstantInt::get(builder.getInt64Ty(),1252                                                   ourBaseExceptionClass)),1253                       exceptionRouteBlock,1254                       externalExceptionBlock);1255 1256  // External Exception Block1257 1258  builder.SetInsertPoint(externalExceptionBlock);1259 1260  generateStringPrint(context,1261                      module,1262                      builder,1263                      "Gen: Foreign exception received.\n",1264                      USE_GLOBAL_STR_CONSTS);1265 1266  // Branch to the finally block1267  builder.CreateBr(finallyBlock);1268 1269  // Exception Route Block1270 1271  builder.SetInsertPoint(exceptionRouteBlock);1272 1273  // Casts exception pointer (_Unwind_Exception instance) to parent1274  // (OurException instance).1275  //1276  // Note: ourBaseFromUnwindOffset is usually negative1277  llvm::Value *typeInfoThrown = builder.CreateConstGEP1_64(1278      builder.getInt8Ty(), unwindException, ourBaseFromUnwindOffset);1279 1280  // Retrieve thrown exception type info type1281  //1282  // Note: Index is not relative to pointer but instead to structure1283  //       unlike a true getelementptr (GEP) instruction1284  typeInfoThrown = builder.CreateStructGEP(ourExceptionType, typeInfoThrown, 0);1285 1286  llvm::Value *typeInfoThrownType =1287      builder.CreateStructGEP(ourTypeInfoType, typeInfoThrown, 0);1288 1289  llvm::Value *ti32 =1290      builder.CreateLoad(builder.getInt32Ty(), typeInfoThrownType);1291  generateIntegerPrint(context, module, builder, *toPrint32Int, ti32,1292                       "Gen: Exception type <%d> received (stack unwound) "1293                       " in " +1294                           ourId + ".\n",1295                       USE_GLOBAL_STR_CONSTS);1296 1297  // Route to matched type info catch block or run cleanup finally block1298  llvm::SwitchInst *switchToCatchBlock = builder.CreateSwitch(retTypeInfoIndex,1299                                                          finallyBlock,1300                                                          numExceptionsToCatch);1301 1302  unsigned nextTypeToCatch;1303 1304  for (unsigned i = 1; i <= numExceptionsToCatch; ++i) {1305    nextTypeToCatch = i - 1;1306    switchToCatchBlock->addCase(llvm::ConstantInt::get(builder.getInt32Ty(), i),1307                                catchBlocks[nextTypeToCatch]);1308  }1309 1310  llvm::verifyFunction(*ret);1311  fpm.run(*ret);1312 1313  return(ret);1314}1315 1316 1317/// Generates function which throws either an exception matched to a runtime1318/// determined type info type (argument to generated function), or if this1319/// runtime value matches nativeThrowType, throws a foreign exception by1320/// calling nativeThrowFunct.1321/// @param module code for module instance1322/// @param builder builder instance1323/// @param fpm a function pass manager holding optional IR to IR1324///        transformations1325/// @param ourId id used to printing purposes1326/// @param nativeThrowType a runtime argument of this value results in1327///        nativeThrowFunct being called to generate/throw exception.1328/// @param nativeThrowFunct function which will throw a foreign exception1329///        if the above nativeThrowType matches generated function's arg.1330/// @returns generated function1331static llvm::Function *1332createThrowExceptionFunction(llvm::Module &module, llvm::IRBuilder<> &builder,1333                             llvm::legacy::FunctionPassManager &fpm,1334                             std::string ourId, int32_t nativeThrowType,1335                             llvm::Function &nativeThrowFunct) {1336  llvm::LLVMContext &context = module.getContext();1337  namedValues.clear();1338  ArgTypes unwindArgTypes;1339  unwindArgTypes.push_back(builder.getInt32Ty());1340  ArgNames unwindArgNames;1341  unwindArgNames.push_back("exceptTypeToThrow");1342 1343  llvm::Function *ret = createFunction(module,1344                                       builder.getVoidTy(),1345                                       unwindArgTypes,1346                                       unwindArgNames,1347                                       ourId,1348                                       llvm::Function::ExternalLinkage,1349                                       false,1350                                       false);1351 1352  // Throws either one of our exception or a native C++ exception depending1353  // on a runtime argument value containing a type info type.1354  llvm::BasicBlock *entryBlock = llvm::BasicBlock::Create(context,1355                                                          "entry",1356                                                          ret);1357  // Throws a foreign exception1358  llvm::BasicBlock *nativeThrowBlock = llvm::BasicBlock::Create(context,1359                                                                "nativeThrow",1360                                                                ret);1361  // Throws one of our Exceptions1362  llvm::BasicBlock *generatedThrowBlock = llvm::BasicBlock::Create(context,1363                                                             "generatedThrow",1364                                                             ret);1365  // Retrieved runtime type info type to throw1366  llvm::Value *exceptionType = namedValues["exceptTypeToThrow"];1367 1368  // nativeThrowBlock block1369 1370  builder.SetInsertPoint(nativeThrowBlock);1371 1372  // Throws foreign exception1373  builder.CreateCall(&nativeThrowFunct, exceptionType);1374  builder.CreateUnreachable();1375 1376  // entry block1377 1378  builder.SetInsertPoint(entryBlock);1379 1380  llvm::Function *toPrint32Int = module.getFunction("print32Int");1381  generateIntegerPrint(context, module, builder, *toPrint32Int,1382                       builder.CreateZExt(exceptionType, builder.getInt32Ty()),1383                       "\nGen: About to throw exception type <%d> in " + ourId +1384                           ".\n",1385                       USE_GLOBAL_STR_CONSTS);1386 1387  // Switches on runtime type info type value to determine whether or not1388  // a foreign exception is thrown. Defaults to throwing one of our1389  // generated exceptions.1390  llvm::SwitchInst *theSwitch = builder.CreateSwitch(exceptionType,1391                                                     generatedThrowBlock,1392                                                     1);1393 1394  theSwitch->addCase(llvm::ConstantInt::get(llvm::Type::getInt32Ty(context),1395                                            nativeThrowType),1396                     nativeThrowBlock);1397 1398  // generatedThrow block1399 1400  builder.SetInsertPoint(generatedThrowBlock);1401 1402  llvm::Function *createOurException = module.getFunction("createOurException");1403  llvm::Function *raiseOurException = module.getFunction(1404                                        "_Unwind_RaiseException");1405 1406  // Creates exception to throw with runtime type info type.1407  llvm::Value *exception = builder.CreateCall(createOurException,1408                                              namedValues["exceptTypeToThrow"]);1409 1410  // Throw generated Exception1411  builder.CreateCall(raiseOurException, exception);1412  builder.CreateUnreachable();1413 1414  llvm::verifyFunction(*ret);1415  fpm.run(*ret);1416 1417  return(ret);1418}1419 1420static void createStandardUtilityFunctions(unsigned numTypeInfos,1421                                           llvm::Module &module,1422                                           llvm::IRBuilder<> &builder);1423 1424/// Creates test code by generating and organizing these functions into the1425/// test case. The test case consists of an outer function setup to invoke1426/// an inner function within an environment having multiple catch and single1427/// finally blocks. This inner function is also setup to invoke a throw1428/// function within an evironment similar in nature to the outer function's1429/// catch and finally blocks. Each of these two functions catch mutually1430/// exclusive subsets (even or odd) of the type info types configured1431/// for this this. All generated functions have a runtime argument which1432/// holds a type info type to throw that each function takes and passes it1433/// to the inner one if such a inner function exists. This type info type is1434/// looked at by the generated throw function to see whether or not it should1435/// throw a generated exception with the same type info type, or instead call1436/// a supplied a function which in turn will throw a foreign exception.1437/// @param module code for module instance1438/// @param builder builder instance1439/// @param fpm a function pass manager holding optional IR to IR1440///        transformations1441/// @param nativeThrowFunctName name of external function which will throw1442///        a foreign exception1443/// @returns outermost generated test function.1444llvm::Function *1445createUnwindExceptionTest(llvm::Module &module, llvm::IRBuilder<> &builder,1446                          llvm::legacy::FunctionPassManager &fpm,1447                          std::string nativeThrowFunctName) {1448  // Number of type infos to generate1449  unsigned numTypeInfos = 6;1450 1451  // Initialze intrisics and external functions to use along with exception1452  // and type info globals.1453  createStandardUtilityFunctions(numTypeInfos,1454                                 module,1455                                 builder);1456  llvm::Function *nativeThrowFunct = module.getFunction(nativeThrowFunctName);1457 1458  // Create exception throw function using the value ~0 to cause1459  // foreign exceptions to be thrown.1460  llvm::Function *throwFunct = createThrowExceptionFunction(module,1461                                                            builder,1462                                                            fpm,1463                                                            "throwFunct",1464                                                            ~0,1465                                                            *nativeThrowFunct);1466  // Inner function will catch even type infos1467  unsigned innerExceptionTypesToCatch[] = {6, 2, 4};1468  size_t numExceptionTypesToCatch = sizeof(innerExceptionTypesToCatch) /1469                                    sizeof(unsigned);1470 1471  // Generate inner function.1472  llvm::Function *innerCatchFunct = createCatchWrappedInvokeFunction(module,1473                                                    builder,1474                                                    fpm,1475                                                    *throwFunct,1476                                                    "innerCatchFunct",1477                                                    numExceptionTypesToCatch,1478                                                    innerExceptionTypesToCatch);1479 1480  // Outer function will catch odd type infos1481  unsigned outerExceptionTypesToCatch[] = {3, 1, 5};1482  numExceptionTypesToCatch = sizeof(outerExceptionTypesToCatch) /1483  sizeof(unsigned);1484 1485  // Generate outer function1486  llvm::Function *outerCatchFunct = createCatchWrappedInvokeFunction(module,1487                                                    builder,1488                                                    fpm,1489                                                    *innerCatchFunct,1490                                                    "outerCatchFunct",1491                                                    numExceptionTypesToCatch,1492                                                    outerExceptionTypesToCatch);1493 1494  // Return outer function to run1495  return(outerCatchFunct);1496}1497 1498namespace {1499/// Represents our foreign exceptions1500class OurCppRunException : public std::runtime_error {1501public:1502  OurCppRunException(const std::string reason) :1503  std::runtime_error(reason) {}1504 1505  OurCppRunException (const OurCppRunException &toCopy) :1506  std::runtime_error(toCopy) {}1507 1508  OurCppRunException &operator = (const OurCppRunException &toCopy) {1509    return(reinterpret_cast<OurCppRunException&>(1510                                 std::runtime_error::operator=(toCopy)));1511  }1512 1513  ~OurCppRunException(void) throw() override {}1514};1515} // end anonymous namespace1516 1517/// Throws foreign C++ exception.1518/// @param ignoreIt unused parameter that allows function to match implied1519///        generated function contract.1520extern "C"1521void throwCppException (int32_t ignoreIt) {1522  throw(OurCppRunException("thrown by throwCppException(...)"));1523}1524 1525typedef void (*OurExceptionThrowFunctType) (int32_t typeToThrow);1526 1527/// This is a test harness which runs test by executing generated1528/// function with a type info type to throw. Harness wraps the execution1529/// of generated function in a C++ try catch clause.1530/// @param engine execution engine to use for executing generated function.1531///        This demo program expects this to be a JIT instance for demo1532///        purposes.1533/// @param function generated test function to run1534/// @param typeToThrow type info type of generated exception to throw, or1535///        indicator to cause foreign exception to be thrown.1536static void runExceptionThrow(llvm::orc::LLJIT *JIT, std::string function,1537                              int32_t typeToThrow) {1538 1539  // Find test's function pointer1540  OurExceptionThrowFunctType functPtr =1541      ExitOnErr(JIT->lookup(function)).toPtr<OurExceptionThrowFunctType>();1542 1543  try {1544    // Run test1545    (*functPtr)(typeToThrow);1546  }1547  catch (OurCppRunException exc) {1548    // Catch foreign C++ exception1549    fprintf(stderr,1550            "\nrunExceptionThrow(...):In C++ catch OurCppRunException "1551            "with reason: %s.\n",1552            exc.what());1553  }1554  catch (...) {1555    // Catch all exceptions including our generated ones. This latter1556    // functionality works according to the example in rules 1.6.4 of1557    // http://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html (v1.22),1558    // given that these will be exceptions foreign to C++1559    // (the _Unwind_Exception::exception_class should be different from1560    // the one used by C++).1561    fprintf(stderr,1562            "\nrunExceptionThrow(...):In C++ catch all.\n");1563  }1564}1565 1566//1567// End test functions1568//1569 1570/// This initialization routine creates type info globals and1571/// adds external function declarations to module.1572/// @param numTypeInfos number of linear type info associated type info types1573///        to create as GlobalVariable instances, starting with the value 1.1574/// @param module code for module instance1575/// @param builder builder instance1576static void createStandardUtilityFunctions(unsigned numTypeInfos,1577                                           llvm::Module &module,1578                                           llvm::IRBuilder<> &builder) {1579 1580  llvm::LLVMContext &context = module.getContext();1581 1582  // Exception initializations1583 1584  // Setup exception catch state1585  ourExceptionNotThrownState =1586    llvm::ConstantInt::get(llvm::Type::getInt8Ty(context), 0),1587  ourExceptionThrownState =1588    llvm::ConstantInt::get(llvm::Type::getInt8Ty(context), 1),1589  ourExceptionCaughtState =1590    llvm::ConstantInt::get(llvm::Type::getInt8Ty(context), 2),1591 1592 1593 1594  // Create our type info type1595  ourTypeInfoType = llvm::StructType::get(context,1596                                          TypeArray(builder.getInt32Ty()));1597 1598  llvm::Type *caughtResultFieldTypes[] = {1599    builder.getPtrTy(),1600    builder.getInt32Ty()1601  };1602 1603  // Create our landingpad result type1604  ourCaughtResultType = llvm::StructType::get(context,1605                                            TypeArray(caughtResultFieldTypes));1606 1607  // Create OurException type1608  ourExceptionType = llvm::StructType::get(context,1609                                           TypeArray(ourTypeInfoType));1610 1611  // Create portion of _Unwind_Exception type1612  //1613  // Note: Declaring only a portion of the _Unwind_Exception struct.1614  //       Does this cause problems?1615  ourUnwindExceptionType =1616    llvm::StructType::get(context,1617                    TypeArray(builder.getInt64Ty()));1618 1619  struct OurBaseException_t dummyException;1620 1621  // Calculate offset of OurException::unwindException member.1622  ourBaseFromUnwindOffset = ((uintptr_t) &dummyException) -1623                            ((uintptr_t) &(dummyException.unwindException));1624 1625#ifdef DEBUG1626  fprintf(stderr,1627          "createStandardUtilityFunctions(...):ourBaseFromUnwindOffset "1628          "= %" PRIi64 ", sizeof(struct OurBaseException_t) - "1629          "sizeof(struct _Unwind_Exception) = %lu.\n",1630          ourBaseFromUnwindOffset,1631          sizeof(struct OurBaseException_t) -1632          sizeof(struct _Unwind_Exception));1633#endif1634 1635  size_t numChars = sizeof(ourBaseExcpClassChars) / sizeof(char);1636 1637  // Create our _Unwind_Exception::exception_class value1638  ourBaseExceptionClass = genClass(ourBaseExcpClassChars, numChars);1639 1640  // Type infos1641 1642  std::string baseStr = "typeInfo", typeInfoName;1643  std::ostringstream typeInfoNameBuilder;1644  std::vector<llvm::Constant*> structVals;1645 1646  llvm::Constant *nextStruct;1647 1648  // Generate each type info1649  //1650  // Note: First type info is not used.1651  for (unsigned i = 0; i <= numTypeInfos; ++i) {1652    structVals.clear();1653    structVals.push_back(llvm::ConstantInt::get(builder.getInt32Ty(), i));1654    nextStruct = llvm::ConstantStruct::get(ourTypeInfoType, structVals);1655 1656    typeInfoNameBuilder.str("");1657    typeInfoNameBuilder << baseStr << i;1658    typeInfoName = typeInfoNameBuilder.str();1659 1660    // Note: Does not seem to work without allocation1661    new llvm::GlobalVariable(module,1662                             ourTypeInfoType,1663                             true,1664                             llvm::GlobalValue::ExternalLinkage,1665                             nextStruct,1666                             typeInfoName);1667 1668    ourTypeInfoNames.push_back(typeInfoName);1669    ourTypeInfoNamesIndex[i] = typeInfoName;1670  }1671 1672  ArgNames argNames;1673  ArgTypes argTypes;1674  llvm::Function *funct = NULL;1675 1676  // print32Int1677 1678  llvm::Type *retType = builder.getVoidTy();1679 1680  argTypes.clear();1681  argTypes.push_back(builder.getInt32Ty());1682  argTypes.push_back(builder.getPtrTy());1683 1684  argNames.clear();1685 1686  createFunction(module,1687                 retType,1688                 argTypes,1689                 argNames,1690                 "print32Int",1691                 llvm::Function::ExternalLinkage,1692                 true,1693                 false);1694 1695  // print64Int1696 1697  retType = builder.getVoidTy();1698 1699  argTypes.clear();1700  argTypes.push_back(builder.getInt64Ty());1701  argTypes.push_back(builder.getPtrTy());1702 1703  argNames.clear();1704 1705  createFunction(module,1706                 retType,1707                 argTypes,1708                 argNames,1709                 "print64Int",1710                 llvm::Function::ExternalLinkage,1711                 true,1712                 false);1713 1714  // printStr1715 1716  retType = builder.getVoidTy();1717 1718  argTypes.clear();1719  argTypes.push_back(builder.getPtrTy());1720 1721  argNames.clear();1722 1723  createFunction(module,1724                 retType,1725                 argTypes,1726                 argNames,1727                 "printStr",1728                 llvm::Function::ExternalLinkage,1729                 true,1730                 false);1731 1732  // throwCppException1733 1734  retType = builder.getVoidTy();1735 1736  argTypes.clear();1737  argTypes.push_back(builder.getInt32Ty());1738 1739  argNames.clear();1740 1741  createFunction(module,1742                 retType,1743                 argTypes,1744                 argNames,1745                 "throwCppException",1746                 llvm::Function::ExternalLinkage,1747                 true,1748                 false);1749 1750  // deleteOurException1751 1752  retType = builder.getVoidTy();1753 1754  argTypes.clear();1755  argTypes.push_back(builder.getPtrTy());1756 1757  argNames.clear();1758 1759  createFunction(module,1760                 retType,1761                 argTypes,1762                 argNames,1763                 "deleteOurException",1764                 llvm::Function::ExternalLinkage,1765                 true,1766                 false);1767 1768  // createOurException1769 1770  retType = builder.getPtrTy();1771 1772  argTypes.clear();1773  argTypes.push_back(builder.getInt32Ty());1774 1775  argNames.clear();1776 1777  createFunction(module,1778                 retType,1779                 argTypes,1780                 argNames,1781                 "createOurException",1782                 llvm::Function::ExternalLinkage,1783                 true,1784                 false);1785 1786  // _Unwind_RaiseException1787 1788  retType = builder.getInt32Ty();1789 1790  argTypes.clear();1791  argTypes.push_back(builder.getPtrTy());1792 1793  argNames.clear();1794 1795  funct = createFunction(module,1796                         retType,1797                         argTypes,1798                         argNames,1799                         "_Unwind_RaiseException",1800                         llvm::Function::ExternalLinkage,1801                         true,1802                         false);1803 1804  funct->setDoesNotReturn();1805 1806  // _Unwind_Resume1807 1808  retType = builder.getInt32Ty();1809 1810  argTypes.clear();1811  argTypes.push_back(builder.getPtrTy());1812 1813  argNames.clear();1814 1815  funct = createFunction(module,1816                         retType,1817                         argTypes,1818                         argNames,1819                         "_Unwind_Resume",1820                         llvm::Function::ExternalLinkage,1821                         true,1822                         false);1823 1824  funct->setDoesNotReturn();1825 1826  // ourPersonality1827 1828  retType = builder.getInt32Ty();1829 1830  argTypes.clear();1831  argTypes.push_back(builder.getInt32Ty());1832  argTypes.push_back(builder.getInt32Ty());1833  argTypes.push_back(builder.getInt64Ty());1834  argTypes.push_back(builder.getPtrTy());1835  argTypes.push_back(builder.getPtrTy());1836 1837  argNames.clear();1838 1839  createFunction(module,1840                 retType,1841                 argTypes,1842                 argNames,1843                 "ourPersonality",1844                 llvm::Function::ExternalLinkage,1845                 true,1846                 false);1847 1848  // llvm.eh.typeid.for intrinsic1849 1850  getOrInsertDeclaration(&module, llvm::Intrinsic::eh_typeid_for,1851                         builder.getPtrTy());1852}1853 1854 1855//===----------------------------------------------------------------------===//1856// Main test driver code.1857//===----------------------------------------------------------------------===//1858 1859/// Demo main routine which takes the type info types to throw. A test will1860/// be run for each given type info type. While type info types with the value1861/// of -1 will trigger a foreign C++ exception to be thrown; type info types1862/// <= 6 and >= 1 will be caught by test functions; and type info types > 61863/// will result in exceptions which pass through to the test harness. All other1864/// type info types are not supported and could cause a crash.1865int main(int argc, char *argv[]) {1866  if (argc == 1) {1867    fprintf(stderr,1868            "\nUsage: ExceptionDemo <exception type to throw> "1869            "[<type 2>...<type n>].\n"1870            "   Each type must have the value of 1 - 6 for "1871            "generated exceptions to be caught;\n"1872            "   the value -1 for foreign C++ exceptions to be "1873            "generated and thrown;\n"1874            "   or the values > 6 for exceptions to be ignored.\n"1875            "\nTry: ExceptionDemo 2 3 7 -1\n"1876            "   for a full test.\n\n");1877    return(0);1878  }1879 1880  llvm::InitializeNativeTarget();1881  llvm::InitializeNativeTargetAsmPrinter();1882  auto Context = std::make_unique<llvm::LLVMContext>();1883  llvm::IRBuilder<> theBuilder(*Context);1884 1885  // Make the module, which holds all the code.1886  std::unique_ptr<llvm::Module> Owner =1887      std::make_unique<llvm::Module>("my cool jit", *Context);1888  llvm::Module *module = Owner.get();1889 1890  // Build LLJIT1891  std::unique_ptr<llvm::orc::LLJIT> JIT =1892      ExitOnErr(llvm::orc::LLJITBuilder().create());1893 1894  // Set up the optimizer pipeline.1895  llvm::legacy::FunctionPassManager fpm(module);1896 1897  // Optimizations turned on1898#ifdef ADD_OPT_PASSES1899 1900  // Basic AliasAnslysis support for GVN.1901  fpm.add(llvm::createBasicAliasAnalysisPass());1902 1903  // Promote allocas to registers.1904  fpm.add(llvm::createPromoteMemoryToRegisterPass());1905 1906  // Do simple "peephole" optimizations and bit-twiddling optzns.1907  fpm.add(llvm::createInstructionCombiningPass());1908 1909  // Reassociate expressions.1910  fpm.add(llvm::createReassociatePass());1911 1912  // Eliminate Common SubExpressions.1913  fpm.add(llvm::createGVNPass());1914 1915  // Simplify the control flow graph (deleting unreachable1916  // blocks, etc).1917  fpm.add(llvm::createCFGSimplificationPass());1918#endif  // ADD_OPT_PASSES1919 1920  fpm.doInitialization();1921 1922  // Generate test code using function throwCppException(...) as1923  // the function which throws foreign exceptions.1924  createUnwindExceptionTest(*module, theBuilder, fpm, "throwCppException");1925 1926  ExitOnErr(JIT->addIRModule(1927      llvm::orc::ThreadSafeModule(std::move(Owner), std::move(Context))));1928 1929#ifndef NDEBUG1930  fprintf(stderr, "\nBegin module dump:\n\n");1931 1932  module->print(llvm::errs(), nullptr);1933 1934  fprintf(stderr, "\nEnd module dump:\n");1935#endif1936 1937  fprintf(stderr, "\n\nBegin Test:\n");1938  std::string toRun = "outerCatchFunct";1939 1940  for (int i = 1; i < argc; ++i) {1941    // Run test for each argument whose value is the exception1942    // type to throw.1943    runExceptionThrow(JIT.get(), toRun, (unsigned)strtoul(argv[i], NULL, 10));1944  }1945 1946  fprintf(stderr, "\nEnd Test:\n\n");1947 1948  return 0;1949}1950