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