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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // Demo program which implements an example LLVM exception implementation, and 10 // shows several test cases including the handling of foreign exceptions. 11 // It is run with type info types arguments to throw. A test will 12 // be run for each given type info type. While type info types with the value 13 // of -1 will trigger a foreign C++ exception to be thrown; type info types 14 // <= 6 and >= 1 will cause the associated generated exceptions to be thrown 15 // and caught by generated test functions; and type info types > 6 16 // will result in exceptions which pass through to the test harness. All other 17 // type info types are not supported and could cause a crash. In all cases, 18 // the "finally" blocks of every generated test functions will executed 19 // regardless of whether or not that test function ignores or catches the 20 // thrown exception. 21 // 22 // examples: 23 // 24 // ExceptionDemo 25 // 26 // causes a usage to be printed to stderr 27 // 28 // ExceptionDemo 2 3 7 -1 29 // 30 // results in the following cases: 31 // - Value 2 causes an exception with a type info type of 2 to be 32 // thrown and caught by an inner generated test function. 33 // - Value 3 causes an exception with a type info type of 3 to be 34 // thrown and caught by an outer generated test function. 35 // - Value 7 causes an exception with a type info type of 7 to be 36 // thrown and NOT be caught by any generated function. 37 // - Value -1 causes a foreign C++ exception to be thrown and not be 38 // caught by any generated function 39 // 40 // Cases -1 and 7 are caught by a C++ test harness where the validity of 41 // of a C++ catch(...) clause catching a generated exception with a 42 // type info type of 7 is explained by: example in rules 1.6.4 in 43 // 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 llvm 46 // Kaleidoscope project. 47 // 48 //===----------------------------------------------------------------------===// 49 50 #include "llvm/ADT/STLExtras.h" 51 #include "llvm/BinaryFormat/Dwarf.h" 52 #include "llvm/ExecutionEngine/MCJIT.h" 53 #include "llvm/ExecutionEngine/SectionMemoryManager.h" 54 #include "llvm/IR/DataLayout.h" 55 #include "llvm/IR/DerivedTypes.h" 56 #include "llvm/IR/IRBuilder.h" 57 #include "llvm/IR/Intrinsics.h" 58 #include "llvm/IR/LLVMContext.h" 59 #include "llvm/IR/LegacyPassManager.h" 60 #include "llvm/IR/Module.h" 61 #include "llvm/IR/Verifier.h" 62 #include "llvm/Support/TargetSelect.h" 63 #include "llvm/Target/TargetOptions.h" 64 #include "llvm/Transforms/Scalar.h" 65 66 // FIXME: Although all systems tested with (Linux, OS X), do not need this 67 // header file included. A user on ubuntu reported, undefined symbols 68 // for stderr, and fprintf, and the addition of this include fixed the 69 // issue for them. Given that LLVM's best practices include the goal 70 // of reducing the number of redundant header files included, the 71 // correct solution would be to find out why these symbols are not 72 // defined for the system in question, and fix the issue by finding out 73 // which LLVM header file, if any, would include these symbols. 74 #include <cstdio> 75 76 #include <sstream> 77 #include <stdexcept> 78 79 #include <inttypes.h> 80 81 #include <unwind.h> 82 83 #ifndef USE_GLOBAL_STR_CONSTS 84 #define USE_GLOBAL_STR_CONSTS true 85 #endif 86 87 // 88 // Example types 89 // 90 91 /// This is our simplistic type info 92 struct OurExceptionType_t { 93 /// type info type 94 int type; 95 }; 96 97 98 /// This is our Exception class which relies on a negative offset to calculate 99 /// pointers to its instances from pointers to its unwindException member. 100 /// 101 /// Note: The above unwind.h defines struct _Unwind_Exception to be aligned 102 /// on a double word boundary. This is necessary to match the standard: 103 /// http://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html 104 struct OurBaseException_t { 105 struct OurExceptionType_t type; 106 107 // Note: This is properly aligned in unwind.h 108 struct _Unwind_Exception unwindException; 109 }; 110 111 112 // Note: Not needed since we are C++ 113 typedef struct OurBaseException_t OurException; 114 typedef struct _Unwind_Exception OurUnwindException; 115 116 // 117 // Various globals used to support typeinfo and generatted exceptions in 118 // general 119 // 120 121 static std::map<std::string, llvm::Value*> namedValues; 122 123 int64_t ourBaseFromUnwindOffset; 124 125 const unsigned char ourBaseExcpClassChars[] = 126 {'o', 'b', 'j', '\0', 'b', 'a', 's', '\0'}; 127 128 129 static uint64_t ourBaseExceptionClass = 0; 130 131 static std::vector<std::string> ourTypeInfoNames; 132 static std::map<int, std::string> ourTypeInfoNamesIndex; 133 134 static llvm::StructType *ourTypeInfoType; 135 static llvm::StructType *ourCaughtResultType; 136 static llvm::StructType *ourExceptionType; 137 static llvm::StructType *ourUnwindExceptionType; 138 139 static llvm::ConstantInt *ourExceptionNotThrownState; 140 static llvm::ConstantInt *ourExceptionThrownState; 141 static llvm::ConstantInt *ourExceptionCaughtState; 142 143 typedef std::vector<std::string> ArgNames; 144 typedef std::vector<llvm::Type*> ArgTypes; 145 146 // 147 // Code Generation Utilities 148 // 149 150 /// Utility used to create a function, both declarations and definitions 151 /// @param module for module instance 152 /// @param retType function return type 153 /// @param theArgTypes function's ordered argument types 154 /// @param theArgNames function's ordered arguments needed if use of this 155 /// function corresponds to a function definition. Use empty 156 /// aggregate for function declarations. 157 /// @param functName function name 158 /// @param linkage function linkage 159 /// @param declarationOnly for function declarations 160 /// @param isVarArg function uses vararg arguments 161 /// @returns function instance 162 llvm::Function *createFunction(llvm::Module &module, 163 llvm::Type *retType, 164 const ArgTypes &theArgTypes, 165 const ArgNames &theArgNames, 166 const std::string &functName, 167 llvm::GlobalValue::LinkageTypes linkage, 168 bool declarationOnly, 169 bool isVarArg) { 170 llvm::FunctionType *functType = 171 llvm::FunctionType::get(retType, theArgTypes, isVarArg); 172 llvm::Function *ret = 173 llvm::Function::Create(functType, linkage, functName, &module); 174 if (!ret || declarationOnly) 175 return(ret); 176 177 namedValues.clear(); 178 unsigned i = 0; 179 for (llvm::Function::arg_iterator argIndex = ret->arg_begin(); 180 i != theArgNames.size(); 181 ++argIndex, ++i) { 182 183 argIndex->setName(theArgNames[i]); 184 namedValues[theArgNames[i]] = argIndex; 185 } 186 187 return(ret); 188 } 189 190 191 /// Create an alloca instruction in the entry block of 192 /// the parent function. This is used for mutable variables etc. 193 /// @param function parent instance 194 /// @param varName stack variable name 195 /// @param type stack variable type 196 /// @param initWith optional constant initialization value 197 /// @returns AllocaInst instance 198 static llvm::AllocaInst *createEntryBlockAlloca(llvm::Function &function, 199 const std::string &varName, 200 llvm::Type *type, 201 llvm::Constant *initWith = 0) { 202 llvm::BasicBlock &block = function.getEntryBlock(); 203 llvm::IRBuilder<> tmp(&block, block.begin()); 204 llvm::AllocaInst *ret = tmp.CreateAlloca(type, 0, varName); 205 206 if (initWith) 207 tmp.CreateStore(initWith, ret); 208 209 return(ret); 210 } 211 212 213 // 214 // Code Generation Utilities End 215 // 216 217 // 218 // Runtime C Library functions 219 // 220 221 namespace { 222 template <typename Type_> 223 uintptr_t ReadType(const uint8_t *&p) { 224 Type_ value; 225 memcpy(&value, p, sizeof(Type_)); 226 p += sizeof(Type_); 227 return static_cast<uintptr_t>(value); 228 } 229 } 230 231 // Note: using an extern "C" block so that static functions can be used 232 extern "C" { 233 234 // Note: Better ways to decide on bit width 235 // 236 /// Prints a 32 bit number, according to the format, to stderr. 237 /// @param intToPrint integer to print 238 /// @param format printf like format to use when printing 239 void print32Int(int intToPrint, const char *format) { 240 if (format) { 241 // Note: No NULL check 242 fprintf(stderr, format, intToPrint); 243 } 244 else { 245 // Note: No NULL check 246 fprintf(stderr, "::print32Int(...):NULL arg.\n"); 247 } 248 } 249 250 251 // Note: Better ways to decide on bit width 252 // 253 /// Prints a 64 bit number, according to the format, to stderr. 254 /// @param intToPrint integer to print 255 /// @param format printf like format to use when printing 256 void print64Int(long int intToPrint, const char *format) { 257 if (format) { 258 // Note: No NULL check 259 fprintf(stderr, format, intToPrint); 260 } 261 else { 262 // Note: No NULL check 263 fprintf(stderr, "::print64Int(...):NULL arg.\n"); 264 } 265 } 266 267 268 /// Prints a C string to stderr 269 /// @param toPrint string to print 270 void printStr(char *toPrint) { 271 if (toPrint) { 272 fprintf(stderr, "%s", toPrint); 273 } 274 else { 275 fprintf(stderr, "::printStr(...):NULL arg.\n"); 276 } 277 } 278 279 280 /// Deletes the true previously allocated exception whose address 281 /// is calculated from the supplied OurBaseException_t::unwindException 282 /// member address. Handles (ignores), NULL pointers. 283 /// @param expToDelete exception to delete 284 void deleteOurException(OurUnwindException *expToDelete) { 285 #ifdef DEBUG 286 fprintf(stderr, 287 "deleteOurException(...).\n"); 288 #endif 289 290 if (expToDelete && 291 (expToDelete->exception_class == ourBaseExceptionClass)) { 292 293 free(((char*) expToDelete) + ourBaseFromUnwindOffset); 294 } 295 } 296 297 298 /// This function is the struct _Unwind_Exception API mandated delete function 299 /// used by foreign exception handlers when deleting our exception 300 /// (OurException), instances. 301 /// @param reason See @link http://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html 302 /// @unlink 303 /// @param expToDelete exception instance to delete 304 void deleteFromUnwindOurException(_Unwind_Reason_Code reason, 305 OurUnwindException *expToDelete) { 306 #ifdef DEBUG 307 fprintf(stderr, 308 "deleteFromUnwindOurException(...).\n"); 309 #endif 310 311 deleteOurException(expToDelete); 312 } 313 314 315 /// Creates (allocates on the heap), an exception (OurException instance), 316 /// of the supplied type info type. 317 /// @param type type info type 318 OurUnwindException *createOurException(int type) { 319 size_t size = sizeof(OurException); 320 OurException *ret = (OurException*) memset(malloc(size), 0, size); 321 (ret->type).type = type; 322 (ret->unwindException).exception_class = ourBaseExceptionClass; 323 (ret->unwindException).exception_cleanup = deleteFromUnwindOurException; 324 325 return(&(ret->unwindException)); 326 } 327 328 329 /// Read a uleb128 encoded value and advance pointer 330 /// See Variable Length Data in: 331 /// @link http://dwarfstd.org/Dwarf3.pdf @unlink 332 /// @param data reference variable holding memory pointer to decode from 333 /// @returns decoded value 334 static uintptr_t readULEB128(const uint8_t **data) { 335 uintptr_t result = 0; 336 uintptr_t shift = 0; 337 unsigned char byte; 338 const uint8_t *p = *data; 339 340 do { 341 byte = *p++; 342 result |= (byte & 0x7f) << shift; 343 shift += 7; 344 } 345 while (byte & 0x80); 346 347 *data = p; 348 349 return result; 350 } 351 352 353 /// Read a sleb128 encoded value and advance pointer 354 /// See Variable Length Data in: 355 /// @link http://dwarfstd.org/Dwarf3.pdf @unlink 356 /// @param data reference variable holding memory pointer to decode from 357 /// @returns decoded value 358 static uintptr_t readSLEB128(const uint8_t **data) { 359 uintptr_t result = 0; 360 uintptr_t shift = 0; 361 unsigned char byte; 362 const uint8_t *p = *data; 363 364 do { 365 byte = *p++; 366 result |= (byte & 0x7f) << shift; 367 shift += 7; 368 } 369 while (byte & 0x80); 370 371 *data = p; 372 373 if ((byte & 0x40) && (shift < (sizeof(result) << 3))) { 374 result |= (~0 << shift); 375 } 376 377 return result; 378 } 379 380 unsigned getEncodingSize(uint8_t Encoding) { 381 if (Encoding == llvm::dwarf::DW_EH_PE_omit) 382 return 0; 383 384 switch (Encoding & 0x0F) { 385 case llvm::dwarf::DW_EH_PE_absptr: 386 return sizeof(uintptr_t); 387 case llvm::dwarf::DW_EH_PE_udata2: 388 return sizeof(uint16_t); 389 case llvm::dwarf::DW_EH_PE_udata4: 390 return sizeof(uint32_t); 391 case llvm::dwarf::DW_EH_PE_udata8: 392 return sizeof(uint64_t); 393 case llvm::dwarf::DW_EH_PE_sdata2: 394 return sizeof(int16_t); 395 case llvm::dwarf::DW_EH_PE_sdata4: 396 return sizeof(int32_t); 397 case llvm::dwarf::DW_EH_PE_sdata8: 398 return sizeof(int64_t); 399 default: 400 // not supported 401 abort(); 402 } 403 } 404 405 /// Read a pointer encoded value and advance pointer 406 /// See Variable Length Data in: 407 /// @link http://dwarfstd.org/Dwarf3.pdf @unlink 408 /// @param data reference variable holding memory pointer to decode from 409 /// @param encoding dwarf encoding type 410 /// @returns decoded value 411 static uintptr_t readEncodedPointer(const uint8_t **data, uint8_t encoding) { 412 uintptr_t result = 0; 413 const uint8_t *p = *data; 414 415 if (encoding == llvm::dwarf::DW_EH_PE_omit) 416 return(result); 417 418 // first get value 419 switch (encoding & 0x0F) { 420 case llvm::dwarf::DW_EH_PE_absptr: 421 result = ReadType<uintptr_t>(p); 422 break; 423 case llvm::dwarf::DW_EH_PE_uleb128: 424 result = readULEB128(&p); 425 break; 426 // Note: This case has not been tested 427 case llvm::dwarf::DW_EH_PE_sleb128: 428 result = readSLEB128(&p); 429 break; 430 case llvm::dwarf::DW_EH_PE_udata2: 431 result = ReadType<uint16_t>(p); 432 break; 433 case llvm::dwarf::DW_EH_PE_udata4: 434 result = ReadType<uint32_t>(p); 435 break; 436 case llvm::dwarf::DW_EH_PE_udata8: 437 result = ReadType<uint64_t>(p); 438 break; 439 case llvm::dwarf::DW_EH_PE_sdata2: 440 result = ReadType<int16_t>(p); 441 break; 442 case llvm::dwarf::DW_EH_PE_sdata4: 443 result = ReadType<int32_t>(p); 444 break; 445 case llvm::dwarf::DW_EH_PE_sdata8: 446 result = ReadType<int64_t>(p); 447 break; 448 default: 449 // not supported 450 abort(); 451 break; 452 } 453 454 // then add relative offset 455 switch (encoding & 0x70) { 456 case llvm::dwarf::DW_EH_PE_absptr: 457 // do nothing 458 break; 459 case llvm::dwarf::DW_EH_PE_pcrel: 460 result += (uintptr_t)(*data); 461 break; 462 case llvm::dwarf::DW_EH_PE_textrel: 463 case llvm::dwarf::DW_EH_PE_datarel: 464 case llvm::dwarf::DW_EH_PE_funcrel: 465 case llvm::dwarf::DW_EH_PE_aligned: 466 default: 467 // not supported 468 abort(); 469 break; 470 } 471 472 // then apply indirection 473 if (encoding & llvm::dwarf::DW_EH_PE_indirect) { 474 result = *((uintptr_t*)result); 475 } 476 477 *data = p; 478 479 return result; 480 } 481 482 483 /// Deals with Dwarf actions matching our type infos 484 /// (OurExceptionType_t instances). Returns whether or not a dwarf emitted 485 /// action matches the supplied exception type. If such a match succeeds, 486 /// the resultAction argument will be set with > 0 index value. Only 487 /// corresponding llvm.eh.selector type info arguments, cleanup arguments 488 /// are supported. Filters are not supported. 489 /// See Variable Length Data in: 490 /// @link http://dwarfstd.org/Dwarf3.pdf @unlink 491 /// Also see @link http://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html @unlink 492 /// @param resultAction reference variable which will be set with result 493 /// @param classInfo our array of type info pointers (to globals) 494 /// @param actionEntry index into above type info array or 0 (clean up). 495 /// We do not support filters. 496 /// @param exceptionClass exception class (_Unwind_Exception::exception_class) 497 /// of thrown exception. 498 /// @param exceptionObject thrown _Unwind_Exception instance. 499 /// @returns whether or not a type info was found. False is returned if only 500 /// a cleanup was found 501 static bool handleActionValue(int64_t *resultAction, 502 uint8_t TTypeEncoding, 503 const uint8_t *ClassInfo, 504 uintptr_t actionEntry, 505 uint64_t exceptionClass, 506 struct _Unwind_Exception *exceptionObject) { 507 bool ret = false; 508 509 if (!resultAction || 510 !exceptionObject || 511 (exceptionClass != ourBaseExceptionClass)) 512 return(ret); 513 514 struct OurBaseException_t *excp = (struct OurBaseException_t*) 515 (((char*) exceptionObject) + ourBaseFromUnwindOffset); 516 struct OurExceptionType_t *excpType = &(excp->type); 517 int type = excpType->type; 518 519 #ifdef DEBUG 520 fprintf(stderr, 521 "handleActionValue(...): exceptionObject = <%p>, " 522 "excp = <%p>.\n", 523 (void*)exceptionObject, 524 (void*)excp); 525 #endif 526 527 const uint8_t *actionPos = (uint8_t*) actionEntry, 528 *tempActionPos; 529 int64_t typeOffset = 0, 530 actionOffset; 531 532 for (int i = 0; true; ++i) { 533 // Each emitted dwarf action corresponds to a 2 tuple of 534 // type info address offset, and action offset to the next 535 // emitted action. 536 typeOffset = readSLEB128(&actionPos); 537 tempActionPos = actionPos; 538 actionOffset = readSLEB128(&tempActionPos); 539 540 #ifdef DEBUG 541 fprintf(stderr, 542 "handleActionValue(...):typeOffset: <%" PRIi64 ">, " 543 "actionOffset: <%" PRIi64 ">.\n", 544 typeOffset, 545 actionOffset); 546 #endif 547 assert((typeOffset >= 0) && 548 "handleActionValue(...):filters are not supported."); 549 550 // Note: A typeOffset == 0 implies that a cleanup llvm.eh.selector 551 // argument has been matched. 552 if (typeOffset > 0) { 553 #ifdef DEBUG 554 fprintf(stderr, 555 "handleActionValue(...):actionValue <%d> found.\n", 556 i); 557 #endif 558 unsigned EncSize = getEncodingSize(TTypeEncoding); 559 const uint8_t *EntryP = ClassInfo - typeOffset * EncSize; 560 uintptr_t P = readEncodedPointer(&EntryP, TTypeEncoding); 561 struct OurExceptionType_t *ThisClassInfo = 562 reinterpret_cast<struct OurExceptionType_t *>(P); 563 if (ThisClassInfo->type == type) { 564 *resultAction = i + 1; 565 ret = true; 566 break; 567 } 568 } 569 570 #ifdef DEBUG 571 fprintf(stderr, 572 "handleActionValue(...):actionValue not found.\n"); 573 #endif 574 if (!actionOffset) 575 break; 576 577 actionPos += actionOffset; 578 } 579 580 return(ret); 581 } 582 583 584 /// Deals with the Language specific data portion of the emitted dwarf code. 585 /// See @link http://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html @unlink 586 /// @param version unsupported (ignored), unwind version 587 /// @param lsda language specific data area 588 /// @param _Unwind_Action actions minimally supported unwind stage 589 /// (forced specifically not supported) 590 /// @param exceptionClass exception class (_Unwind_Exception::exception_class) 591 /// of thrown exception. 592 /// @param exceptionObject thrown _Unwind_Exception instance. 593 /// @param context unwind system context 594 /// @returns minimally supported unwinding control indicator 595 static _Unwind_Reason_Code handleLsda(int version, const uint8_t *lsda, 596 _Unwind_Action actions, 597 _Unwind_Exception_Class exceptionClass, 598 struct _Unwind_Exception *exceptionObject, 599 struct _Unwind_Context *context) { 600 _Unwind_Reason_Code ret = _URC_CONTINUE_UNWIND; 601 602 if (!lsda) 603 return(ret); 604 605 #ifdef DEBUG 606 fprintf(stderr, 607 "handleLsda(...):lsda is non-zero.\n"); 608 #endif 609 610 // Get the current instruction pointer and offset it before next 611 // instruction in the current frame which threw the exception. 612 uintptr_t pc = _Unwind_GetIP(context)-1; 613 614 // Get beginning current frame's code (as defined by the 615 // emitted dwarf code) 616 uintptr_t funcStart = _Unwind_GetRegionStart(context); 617 uintptr_t pcOffset = pc - funcStart; 618 const uint8_t *ClassInfo = NULL; 619 620 // Note: See JITDwarfEmitter::EmitExceptionTable(...) for corresponding 621 // dwarf emission 622 623 // Parse LSDA header. 624 uint8_t lpStartEncoding = *lsda++; 625 626 if (lpStartEncoding != llvm::dwarf::DW_EH_PE_omit) { 627 readEncodedPointer(&lsda, lpStartEncoding); 628 } 629 630 uint8_t ttypeEncoding = *lsda++; 631 uintptr_t classInfoOffset; 632 633 if (ttypeEncoding != llvm::dwarf::DW_EH_PE_omit) { 634 // Calculate type info locations in emitted dwarf code which 635 // were flagged by type info arguments to llvm.eh.selector 636 // intrinsic 637 classInfoOffset = readULEB128(&lsda); 638 ClassInfo = lsda + classInfoOffset; 639 } 640 641 // Walk call-site table looking for range that 642 // includes current PC. 643 644 uint8_t callSiteEncoding = *lsda++; 645 uint32_t callSiteTableLength = readULEB128(&lsda); 646 const uint8_t *callSiteTableStart = lsda; 647 const uint8_t *callSiteTableEnd = callSiteTableStart + 648 callSiteTableLength; 649 const uint8_t *actionTableStart = callSiteTableEnd; 650 const uint8_t *callSitePtr = callSiteTableStart; 651 652 while (callSitePtr < callSiteTableEnd) { 653 uintptr_t start = readEncodedPointer(&callSitePtr, 654 callSiteEncoding); 655 uintptr_t length = readEncodedPointer(&callSitePtr, 656 callSiteEncoding); 657 uintptr_t landingPad = readEncodedPointer(&callSitePtr, 658 callSiteEncoding); 659 660 // Note: Action value 661 uintptr_t actionEntry = readULEB128(&callSitePtr); 662 663 if (exceptionClass != ourBaseExceptionClass) { 664 // We have been notified of a foreign exception being thrown, 665 // and we therefore need to execute cleanup landing pads 666 actionEntry = 0; 667 } 668 669 if (landingPad == 0) { 670 #ifdef DEBUG 671 fprintf(stderr, 672 "handleLsda(...): No landing pad found.\n"); 673 #endif 674 675 continue; // no landing pad for this entry 676 } 677 678 if (actionEntry) { 679 actionEntry += ((uintptr_t) actionTableStart) - 1; 680 } 681 else { 682 #ifdef DEBUG 683 fprintf(stderr, 684 "handleLsda(...):No action table found.\n"); 685 #endif 686 } 687 688 bool exceptionMatched = false; 689 690 if ((start <= pcOffset) && (pcOffset < (start + length))) { 691 #ifdef DEBUG 692 fprintf(stderr, 693 "handleLsda(...): Landing pad found.\n"); 694 #endif 695 int64_t actionValue = 0; 696 697 if (actionEntry) { 698 exceptionMatched = handleActionValue(&actionValue, 699 ttypeEncoding, 700 ClassInfo, 701 actionEntry, 702 exceptionClass, 703 exceptionObject); 704 } 705 706 if (!(actions & _UA_SEARCH_PHASE)) { 707 #ifdef DEBUG 708 fprintf(stderr, 709 "handleLsda(...): installed landing pad " 710 "context.\n"); 711 #endif 712 713 // Found landing pad for the PC. 714 // Set Instruction Pointer to so we re-enter function 715 // at landing pad. The landing pad is created by the 716 // compiler to take two parameters in registers. 717 _Unwind_SetGR(context, 718 __builtin_eh_return_data_regno(0), 719 (uintptr_t)exceptionObject); 720 721 // Note: this virtual register directly corresponds 722 // to the return of the llvm.eh.selector intrinsic 723 if (!actionEntry || !exceptionMatched) { 724 // We indicate cleanup only 725 _Unwind_SetGR(context, 726 __builtin_eh_return_data_regno(1), 727 0); 728 } 729 else { 730 // Matched type info index of llvm.eh.selector intrinsic 731 // passed here. 732 _Unwind_SetGR(context, 733 __builtin_eh_return_data_regno(1), 734 actionValue); 735 } 736 737 // To execute landing pad set here 738 _Unwind_SetIP(context, funcStart + landingPad); 739 ret = _URC_INSTALL_CONTEXT; 740 } 741 else if (exceptionMatched) { 742 #ifdef DEBUG 743 fprintf(stderr, 744 "handleLsda(...): setting handler found.\n"); 745 #endif 746 ret = _URC_HANDLER_FOUND; 747 } 748 else { 749 // Note: Only non-clean up handlers are marked as 750 // found. Otherwise the clean up handlers will be 751 // re-found and executed during the clean up 752 // phase. 753 #ifdef DEBUG 754 fprintf(stderr, 755 "handleLsda(...): cleanup handler found.\n"); 756 #endif 757 } 758 759 break; 760 } 761 } 762 763 return(ret); 764 } 765 766 767 /// This is the personality function which is embedded (dwarf emitted), in the 768 /// dwarf unwind info block. Again see: JITDwarfEmitter.cpp. 769 /// See @link http://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html @unlink 770 /// @param version unsupported (ignored), unwind version 771 /// @param _Unwind_Action actions minimally supported unwind stage 772 /// (forced specifically not supported) 773 /// @param exceptionClass exception class (_Unwind_Exception::exception_class) 774 /// of thrown exception. 775 /// @param exceptionObject thrown _Unwind_Exception instance. 776 /// @param context unwind system context 777 /// @returns minimally supported unwinding control indicator 778 _Unwind_Reason_Code ourPersonality(int version, _Unwind_Action actions, 779 _Unwind_Exception_Class exceptionClass, 780 struct _Unwind_Exception *exceptionObject, 781 struct _Unwind_Context *context) { 782 #ifdef DEBUG 783 fprintf(stderr, 784 "We are in ourPersonality(...):actions is <%d>.\n", 785 actions); 786 787 if (actions & _UA_SEARCH_PHASE) { 788 fprintf(stderr, "ourPersonality(...):In search phase.\n"); 789 } 790 else { 791 fprintf(stderr, "ourPersonality(...):In non-search phase.\n"); 792 } 793 #endif 794 795 const uint8_t *lsda = (const uint8_t *)_Unwind_GetLanguageSpecificData(context); 796 797 #ifdef DEBUG 798 fprintf(stderr, 799 "ourPersonality(...):lsda = <%p>.\n", 800 (void*)lsda); 801 #endif 802 803 // The real work of the personality function is captured here 804 return(handleLsda(version, 805 lsda, 806 actions, 807 exceptionClass, 808 exceptionObject, 809 context)); 810 } 811 812 813 /// Generates our _Unwind_Exception class from a given character array. 814 /// thereby handling arbitrary lengths (not in standard), and handling 815 /// embedded \0s. 816 /// See @link http://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html @unlink 817 /// @param classChars char array to encode. NULL values not checkedf 818 /// @param classCharsSize number of chars in classChars. Value is not checked. 819 /// @returns class value 820 uint64_t genClass(const unsigned char classChars[], size_t classCharsSize) 821 { 822 uint64_t ret = classChars[0]; 823 824 for (unsigned i = 1; i < classCharsSize; ++i) { 825 ret <<= 8; 826 ret += classChars[i]; 827 } 828 829 return(ret); 830 } 831 832 } // extern "C" 833 834 // 835 // Runtime C Library functions End 836 // 837 838 // 839 // Code generation functions 840 // 841 842 /// Generates code to print given constant string 843 /// @param context llvm context 844 /// @param module code for module instance 845 /// @param builder builder instance 846 /// @param toPrint string to print 847 /// @param useGlobal A value of true (default) indicates a GlobalValue is 848 /// generated, and is used to hold the constant string. A value of 849 /// false indicates that the constant string will be stored on the 850 /// stack. 851 void generateStringPrint(llvm::LLVMContext &context, 852 llvm::Module &module, 853 llvm::IRBuilder<> &builder, 854 std::string toPrint, 855 bool useGlobal = true) { 856 llvm::Function *printFunct = module.getFunction("printStr"); 857 858 llvm::Value *stringVar; 859 llvm::Constant *stringConstant = 860 llvm::ConstantDataArray::getString(context, toPrint); 861 862 if (useGlobal) { 863 // Note: Does not work without allocation 864 stringVar = 865 new llvm::GlobalVariable(module, 866 stringConstant->getType(), 867 true, 868 llvm::GlobalValue::PrivateLinkage, 869 stringConstant, 870 ""); 871 } 872 else { 873 stringVar = builder.CreateAlloca(stringConstant->getType()); 874 builder.CreateStore(stringConstant, stringVar); 875 } 876 877 llvm::Value *cast = builder.CreatePointerCast(stringVar, 878 builder.getInt8PtrTy()); 879 builder.CreateCall(printFunct, cast); 880 } 881 882 883 /// Generates code to print given runtime integer according to constant 884 /// string format, and a given print function. 885 /// @param context llvm context 886 /// @param module code for module instance 887 /// @param builder builder instance 888 /// @param printFunct function used to "print" integer 889 /// @param toPrint string to print 890 /// @param format printf like formating string for print 891 /// @param useGlobal A value of true (default) indicates a GlobalValue is 892 /// generated, and is used to hold the constant string. A value of 893 /// false indicates that the constant string will be stored on the 894 /// stack. 895 void generateIntegerPrint(llvm::LLVMContext &context, 896 llvm::Module &module, 897 llvm::IRBuilder<> &builder, 898 llvm::Function &printFunct, 899 llvm::Value &toPrint, 900 std::string format, 901 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 allocation 908 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.getInt8PtrTy()); 923 builder.CreateCall(&printFunct, {&toPrint, cast}); 924 } 925 926 927 /// Generates code to handle finally block type semantics: always runs 928 /// regardless of whether a thrown exception is passing through or the 929 /// parent function is simply exiting. In addition to printing some state 930 /// to stderr, this code will resume the exception handling--runs the 931 /// unwind resume block, if the exception has not been previously caught 932 /// by a catch clause, and will otherwise execute the end block (terminator 933 /// block). In addition this function creates the corresponding function's 934 /// stack storage for the exception pointer and catch flag status. 935 /// @param context llvm context 936 /// @param module code for module instance 937 /// @param builder builder instance 938 /// @param toAddTo parent function to add block to 939 /// @param blockName block name of new "finally" block. 940 /// @param functionId output id used for printing 941 /// @param terminatorBlock terminator "end" block 942 /// @param unwindResumeBlock unwind resume block 943 /// @param exceptionCaughtFlag reference exception caught/thrown status storage 944 /// @param exceptionStorage reference to exception pointer storage 945 /// @param caughtResultStorage reference to landingpad result storage 946 /// @returns newly created block 947 static llvm::BasicBlock *createFinallyBlock(llvm::LLVMContext &context, 948 llvm::Module &module, 949 llvm::IRBuilder<> &builder, 950 llvm::Function &toAddTo, 951 std::string &blockName, 952 std::string &functionId, 953 llvm::BasicBlock &terminatorBlock, 954 llvm::BasicBlock &unwindResumeBlock, 955 llvm::Value **exceptionCaughtFlag, 956 llvm::Value **exceptionStorage, 957 llvm::Value **caughtResultStorage) { 958 assert(exceptionCaughtFlag && 959 "ExceptionDemo::createFinallyBlock(...):exceptionCaughtFlag " 960 "is NULL"); 961 assert(exceptionStorage && 962 "ExceptionDemo::createFinallyBlock(...):exceptionStorage " 963 "is NULL"); 964 assert(caughtResultStorage && 965 "ExceptionDemo::createFinallyBlock(...):caughtResultStorage " 966 "is NULL"); 967 968 *exceptionCaughtFlag = createEntryBlockAlloca(toAddTo, 969 "exceptionCaught", 970 ourExceptionNotThrownState->getType(), 971 ourExceptionNotThrownState); 972 973 llvm::PointerType *exceptionStorageType = builder.getInt8PtrTy(); 974 *exceptionStorage = createEntryBlockAlloca(toAddTo, 975 "exceptionStorage", 976 exceptionStorageType, 977 llvm::ConstantPointerNull::get( 978 exceptionStorageType)); 979 *caughtResultStorage = createEntryBlockAlloca(toAddTo, 980 "caughtResultStorage", 981 ourCaughtResultType, 982 llvm::ConstantAggregateZero::get( 983 ourCaughtResultType)); 984 985 llvm::BasicBlock *ret = llvm::BasicBlock::Create(context, 986 blockName, 987 &toAddTo); 988 989 builder.SetInsertPoint(ret); 990 991 std::ostringstream bufferToPrint; 992 bufferToPrint << "Gen: Executing finally block " 993 << blockName << " in " << functionId << "\n"; 994 generateStringPrint(context, 995 module, 996 builder, 997 bufferToPrint.str(), 998 USE_GLOBAL_STR_CONSTS); 999 1000 llvm::SwitchInst *theSwitch = builder.CreateSwitch(builder.CreateLoad( 1001 *exceptionCaughtFlag), 1002 &terminatorBlock, 1003 2); 1004 theSwitch->addCase(ourExceptionCaughtState, &terminatorBlock); 1005 theSwitch->addCase(ourExceptionThrownState, &unwindResumeBlock); 1006 1007 return(ret); 1008 } 1009 1010 1011 /// Generates catch block semantics which print a string to indicate type of 1012 /// catch executed, sets an exception caught flag, and executes passed in 1013 /// end block (terminator block). 1014 /// @param context llvm context 1015 /// @param module code for module instance 1016 /// @param builder builder instance 1017 /// @param toAddTo parent function to add block to 1018 /// @param blockName block name of new "catch" block. 1019 /// @param functionId output id used for printing 1020 /// @param terminatorBlock terminator "end" block 1021 /// @param exceptionCaughtFlag exception caught/thrown status 1022 /// @returns newly created block 1023 static llvm::BasicBlock *createCatchBlock(llvm::LLVMContext &context, 1024 llvm::Module &module, 1025 llvm::IRBuilder<> &builder, 1026 llvm::Function &toAddTo, 1027 std::string &blockName, 1028 std::string &functionId, 1029 llvm::BasicBlock &terminatorBlock, 1030 llvm::Value &exceptionCaughtFlag) { 1031 1032 llvm::BasicBlock *ret = llvm::BasicBlock::Create(context, 1033 blockName, 1034 &toAddTo); 1035 1036 builder.SetInsertPoint(ret); 1037 1038 std::ostringstream bufferToPrint; 1039 bufferToPrint << "Gen: Executing catch block " 1040 << blockName 1041 << " in " 1042 << functionId 1043 << std::endl; 1044 generateStringPrint(context, 1045 module, 1046 builder, 1047 bufferToPrint.str(), 1048 USE_GLOBAL_STR_CONSTS); 1049 builder.CreateStore(ourExceptionCaughtState, &exceptionCaughtFlag); 1050 builder.CreateBr(&terminatorBlock); 1051 1052 return(ret); 1053 } 1054 1055 1056 /// Generates a function which invokes a function (toInvoke) and, whose 1057 /// unwind block will "catch" the type info types correspondingly held in the 1058 /// exceptionTypesToCatch argument. If the toInvoke function throws an 1059 /// exception which does not match any type info types contained in 1060 /// exceptionTypesToCatch, the generated code will call _Unwind_Resume 1061 /// with the raised exception. On the other hand the generated code will 1062 /// normally exit if the toInvoke function does not throw an exception. 1063 /// The generated "finally" block is always run regardless of the cause of 1064 /// the generated function exit. 1065 /// The generated function is returned after being verified. 1066 /// @param module code for module instance 1067 /// @param builder builder instance 1068 /// @param fpm a function pass manager holding optional IR to IR 1069 /// transformations 1070 /// @param toInvoke inner function to invoke 1071 /// @param ourId id used to printing purposes 1072 /// @param numExceptionsToCatch length of exceptionTypesToCatch array 1073 /// @param exceptionTypesToCatch array of type info types to "catch" 1074 /// @returns generated function 1075 static llvm::Function *createCatchWrappedInvokeFunction( 1076 llvm::Module &module, llvm::IRBuilder<> &builder, 1077 llvm::legacy::FunctionPassManager &fpm, llvm::Function &toInvoke, 1078 std::string ourId, unsigned numExceptionsToCatch, 1079 unsigned exceptionTypesToCatch[]) { 1080 1081 llvm::LLVMContext &context = module.getContext(); 1082 llvm::Function *toPrint32Int = module.getFunction("print32Int"); 1083 1084 ArgTypes argTypes; 1085 argTypes.push_back(builder.getInt32Ty()); 1086 1087 ArgNames argNames; 1088 argNames.push_back("exceptTypeToThrow"); 1089 1090 llvm::Function *ret = createFunction(module, 1091 builder.getVoidTy(), 1092 argTypes, 1093 argNames, 1094 ourId, 1095 llvm::Function::ExternalLinkage, 1096 false, 1097 false); 1098 1099 // Block which calls invoke 1100 llvm::BasicBlock *entryBlock = llvm::BasicBlock::Create(context, 1101 "entry", 1102 ret); 1103 // Normal block for invoke 1104 llvm::BasicBlock *normalBlock = llvm::BasicBlock::Create(context, 1105 "normal", 1106 ret); 1107 // Unwind block for invoke 1108 llvm::BasicBlock *exceptionBlock = llvm::BasicBlock::Create(context, 1109 "exception", 1110 ret); 1111 1112 // Block which routes exception to correct catch handler block 1113 llvm::BasicBlock *exceptionRouteBlock = llvm::BasicBlock::Create(context, 1114 "exceptionRoute", 1115 ret); 1116 1117 // Foreign exception handler 1118 llvm::BasicBlock *externalExceptionBlock = llvm::BasicBlock::Create(context, 1119 "externalException", 1120 ret); 1121 1122 // Block which calls _Unwind_Resume 1123 llvm::BasicBlock *unwindResumeBlock = llvm::BasicBlock::Create(context, 1124 "unwindResume", 1125 ret); 1126 1127 // Clean up block which delete exception if needed 1128 llvm::BasicBlock *endBlock = llvm::BasicBlock::Create(context, "end", ret); 1129 1130 std::string nextName; 1131 std::vector<llvm::BasicBlock*> catchBlocks(numExceptionsToCatch); 1132 llvm::Value *exceptionCaughtFlag = NULL; 1133 llvm::Value *exceptionStorage = NULL; 1134 llvm::Value *caughtResultStorage = NULL; 1135 1136 // Finally block which will branch to unwindResumeBlock if 1137 // exception is not caught. Initializes/allocates stack locations. 1138 llvm::BasicBlock *finallyBlock = createFinallyBlock(context, 1139 module, 1140 builder, 1141 *ret, 1142 nextName = "finally", 1143 ourId, 1144 *endBlock, 1145 *unwindResumeBlock, 1146 &exceptionCaughtFlag, 1147 &exceptionStorage, 1148 &caughtResultStorage 1149 ); 1150 1151 for (unsigned i = 0; i < numExceptionsToCatch; ++i) { 1152 nextName = ourTypeInfoNames[exceptionTypesToCatch[i]]; 1153 1154 // One catch block per type info to be caught 1155 catchBlocks[i] = createCatchBlock(context, 1156 module, 1157 builder, 1158 *ret, 1159 nextName, 1160 ourId, 1161 *finallyBlock, 1162 *exceptionCaughtFlag); 1163 } 1164 1165 // Entry Block 1166 1167 builder.SetInsertPoint(entryBlock); 1168 1169 std::vector<llvm::Value*> args; 1170 args.push_back(namedValues["exceptTypeToThrow"]); 1171 builder.CreateInvoke(&toInvoke, 1172 normalBlock, 1173 exceptionBlock, 1174 args); 1175 1176 // End Block 1177 1178 builder.SetInsertPoint(endBlock); 1179 1180 generateStringPrint(context, 1181 module, 1182 builder, 1183 "Gen: In end block: exiting in " + ourId + ".\n", 1184 USE_GLOBAL_STR_CONSTS); 1185 llvm::Function *deleteOurException = module.getFunction("deleteOurException"); 1186 1187 // Note: function handles NULL exceptions 1188 builder.CreateCall(deleteOurException, 1189 builder.CreateLoad(exceptionStorage)); 1190 builder.CreateRetVoid(); 1191 1192 // Normal Block 1193 1194 builder.SetInsertPoint(normalBlock); 1195 1196 generateStringPrint(context, 1197 module, 1198 builder, 1199 "Gen: No exception in " + ourId + "!\n", 1200 USE_GLOBAL_STR_CONSTS); 1201 1202 // Finally block is always called 1203 builder.CreateBr(finallyBlock); 1204 1205 // Unwind Resume Block 1206 1207 builder.SetInsertPoint(unwindResumeBlock); 1208 1209 builder.CreateResume(builder.CreateLoad(caughtResultStorage)); 1210 1211 // Exception Block 1212 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 caught 1227 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 of 1235 // caughtResultStore for unwindException storage, may be alleviated 1236 // altogether with a block rearrangement 1237 builder.CreateStore(caughtResult, caughtResultStorage); 1238 builder.CreateStore(unwindException, exceptionStorage); 1239 builder.CreateStore(ourExceptionThrownState, exceptionCaughtFlag); 1240 1241 // Retrieve exception_class member from thrown exception 1242 // (_Unwind_Exception instance). This member tells us whether or not 1243 // the exception is foreign. 1244 llvm::Value *unwindExceptionClass = 1245 builder.CreateLoad(builder.CreateStructGEP( 1246 ourUnwindExceptionType, 1247 builder.CreatePointerCast(unwindException, 1248 ourUnwindExceptionType->getPointerTo()), 1249 0)); 1250 1251 // Branch to the externalExceptionBlock if the exception is foreign or 1252 // to a catch router if not. Either way the finally block will be run. 1253 builder.CreateCondBr(builder.CreateICmpEQ(unwindExceptionClass, 1254 llvm::ConstantInt::get(builder.getInt64Ty(), 1255 ourBaseExceptionClass)), 1256 exceptionRouteBlock, 1257 externalExceptionBlock); 1258 1259 // External Exception Block 1260 1261 builder.SetInsertPoint(externalExceptionBlock); 1262 1263 generateStringPrint(context, 1264 module, 1265 builder, 1266 "Gen: Foreign exception received.\n", 1267 USE_GLOBAL_STR_CONSTS); 1268 1269 // Branch to the finally block 1270 builder.CreateBr(finallyBlock); 1271 1272 // Exception Route Block 1273 1274 builder.SetInsertPoint(exceptionRouteBlock); 1275 1276 // Casts exception pointer (_Unwind_Exception instance) to parent 1277 // (OurException instance). 1278 // 1279 // Note: ourBaseFromUnwindOffset is usually negative 1280 llvm::Value *typeInfoThrown = builder.CreatePointerCast( 1281 builder.CreateConstGEP1_64(unwindException, 1282 ourBaseFromUnwindOffset), 1283 ourExceptionType->getPointerTo()); 1284 1285 // Retrieve thrown exception type info type 1286 // 1287 // Note: Index is not relative to pointer but instead to structure 1288 // unlike a true getelementptr (GEP) instruction 1289 typeInfoThrown = builder.CreateStructGEP(ourExceptionType, typeInfoThrown, 0); 1290 1291 llvm::Value *typeInfoThrownType = 1292 builder.CreateStructGEP(builder.getInt8PtrTy(), typeInfoThrown, 0); 1293 1294 generateIntegerPrint(context, 1295 module, 1296 builder, 1297 *toPrint32Int, 1298 *(builder.CreateLoad(typeInfoThrownType)), 1299 "Gen: Exception type <%d> received (stack unwound) " 1300 " in " + 1301 ourId + 1302 ".\n", 1303 USE_GLOBAL_STR_CONSTS); 1304 1305 // Route to matched type info catch block or run cleanup finally block 1306 llvm::SwitchInst *switchToCatchBlock = builder.CreateSwitch(retTypeInfoIndex, 1307 finallyBlock, 1308 numExceptionsToCatch); 1309 1310 unsigned nextTypeToCatch; 1311 1312 for (unsigned i = 1; i <= numExceptionsToCatch; ++i) { 1313 nextTypeToCatch = i - 1; 1314 switchToCatchBlock->addCase(llvm::ConstantInt::get( 1315 llvm::Type::getInt32Ty(context), i), 1316 catchBlocks[nextTypeToCatch]); 1317 } 1318 1319 llvm::verifyFunction(*ret); 1320 fpm.run(*ret); 1321 1322 return(ret); 1323 } 1324 1325 1326 /// Generates function which throws either an exception matched to a runtime 1327 /// determined type info type (argument to generated function), or if this 1328 /// runtime value matches nativeThrowType, throws a foreign exception by 1329 /// calling nativeThrowFunct. 1330 /// @param module code for module instance 1331 /// @param builder builder instance 1332 /// @param fpm a function pass manager holding optional IR to IR 1333 /// transformations 1334 /// @param ourId id used to printing purposes 1335 /// @param nativeThrowType a runtime argument of this value results in 1336 /// nativeThrowFunct being called to generate/throw exception. 1337 /// @param nativeThrowFunct function which will throw a foreign exception 1338 /// if the above nativeThrowType matches generated function's arg. 1339 /// @returns generated function 1340 static llvm::Function * 1341 createThrowExceptionFunction(llvm::Module &module, llvm::IRBuilder<> &builder, 1342 llvm::legacy::FunctionPassManager &fpm, 1343 std::string ourId, int32_t nativeThrowType, 1344 llvm::Function &nativeThrowFunct) { 1345 llvm::LLVMContext &context = module.getContext(); 1346 namedValues.clear(); 1347 ArgTypes unwindArgTypes; 1348 unwindArgTypes.push_back(builder.getInt32Ty()); 1349 ArgNames unwindArgNames; 1350 unwindArgNames.push_back("exceptTypeToThrow"); 1351 1352 llvm::Function *ret = createFunction(module, 1353 builder.getVoidTy(), 1354 unwindArgTypes, 1355 unwindArgNames, 1356 ourId, 1357 llvm::Function::ExternalLinkage, 1358 false, 1359 false); 1360 1361 // Throws either one of our exception or a native C++ exception depending 1362 // on a runtime argument value containing a type info type. 1363 llvm::BasicBlock *entryBlock = llvm::BasicBlock::Create(context, 1364 "entry", 1365 ret); 1366 // Throws a foreign exception 1367 llvm::BasicBlock *nativeThrowBlock = llvm::BasicBlock::Create(context, 1368 "nativeThrow", 1369 ret); 1370 // Throws one of our Exceptions 1371 llvm::BasicBlock *generatedThrowBlock = llvm::BasicBlock::Create(context, 1372 "generatedThrow", 1373 ret); 1374 // Retrieved runtime type info type to throw 1375 llvm::Value *exceptionType = namedValues["exceptTypeToThrow"]; 1376 1377 // nativeThrowBlock block 1378 1379 builder.SetInsertPoint(nativeThrowBlock); 1380 1381 // Throws foreign exception 1382 builder.CreateCall(&nativeThrowFunct, exceptionType); 1383 builder.CreateUnreachable(); 1384 1385 // entry block 1386 1387 builder.SetInsertPoint(entryBlock); 1388 1389 llvm::Function *toPrint32Int = module.getFunction("print32Int"); 1390 generateIntegerPrint(context, 1391 module, 1392 builder, 1393 *toPrint32Int, 1394 *exceptionType, 1395 "\nGen: About to throw exception type <%d> in " + 1396 ourId + 1397 ".\n", 1398 USE_GLOBAL_STR_CONSTS); 1399 1400 // Switches on runtime type info type value to determine whether or not 1401 // a foreign exception is thrown. Defaults to throwing one of our 1402 // generated exceptions. 1403 llvm::SwitchInst *theSwitch = builder.CreateSwitch(exceptionType, 1404 generatedThrowBlock, 1405 1); 1406 1407 theSwitch->addCase(llvm::ConstantInt::get(llvm::Type::getInt32Ty(context), 1408 nativeThrowType), 1409 nativeThrowBlock); 1410 1411 // generatedThrow block 1412 1413 builder.SetInsertPoint(generatedThrowBlock); 1414 1415 llvm::Function *createOurException = module.getFunction("createOurException"); 1416 llvm::Function *raiseOurException = module.getFunction( 1417 "_Unwind_RaiseException"); 1418 1419 // Creates exception to throw with runtime type info type. 1420 llvm::Value *exception = builder.CreateCall(createOurException, 1421 namedValues["exceptTypeToThrow"]); 1422 1423 // Throw generated Exception 1424 builder.CreateCall(raiseOurException, exception); 1425 builder.CreateUnreachable(); 1426 1427 llvm::verifyFunction(*ret); 1428 fpm.run(*ret); 1429 1430 return(ret); 1431 } 1432 1433 static void createStandardUtilityFunctions(unsigned numTypeInfos, 1434 llvm::Module &module, 1435 llvm::IRBuilder<> &builder); 1436 1437 /// Creates test code by generating and organizing these functions into the 1438 /// test case. The test case consists of an outer function setup to invoke 1439 /// an inner function within an environment having multiple catch and single 1440 /// finally blocks. This inner function is also setup to invoke a throw 1441 /// function within an evironment similar in nature to the outer function's 1442 /// catch and finally blocks. Each of these two functions catch mutually 1443 /// exclusive subsets (even or odd) of the type info types configured 1444 /// for this this. All generated functions have a runtime argument which 1445 /// holds a type info type to throw that each function takes and passes it 1446 /// to the inner one if such a inner function exists. This type info type is 1447 /// looked at by the generated throw function to see whether or not it should 1448 /// throw a generated exception with the same type info type, or instead call 1449 /// a supplied a function which in turn will throw a foreign exception. 1450 /// @param module code for module instance 1451 /// @param builder builder instance 1452 /// @param fpm a function pass manager holding optional IR to IR 1453 /// transformations 1454 /// @param nativeThrowFunctName name of external function which will throw 1455 /// a foreign exception 1456 /// @returns outermost generated test function. 1457 llvm::Function * 1458 createUnwindExceptionTest(llvm::Module &module, llvm::IRBuilder<> &builder, 1459 llvm::legacy::FunctionPassManager &fpm, 1460 std::string nativeThrowFunctName) { 1461 // Number of type infos to generate 1462 unsigned numTypeInfos = 6; 1463 1464 // Initialze intrisics and external functions to use along with exception 1465 // and type info globals. 1466 createStandardUtilityFunctions(numTypeInfos, 1467 module, 1468 builder); 1469 llvm::Function *nativeThrowFunct = module.getFunction(nativeThrowFunctName); 1470 1471 // Create exception throw function using the value ~0 to cause 1472 // foreign exceptions to be thrown. 1473 llvm::Function *throwFunct = createThrowExceptionFunction(module, 1474 builder, 1475 fpm, 1476 "throwFunct", 1477 ~0, 1478 *nativeThrowFunct); 1479 // Inner function will catch even type infos 1480 unsigned innerExceptionTypesToCatch[] = {6, 2, 4}; 1481 size_t numExceptionTypesToCatch = sizeof(innerExceptionTypesToCatch) / 1482 sizeof(unsigned); 1483 1484 // Generate inner function. 1485 llvm::Function *innerCatchFunct = createCatchWrappedInvokeFunction(module, 1486 builder, 1487 fpm, 1488 *throwFunct, 1489 "innerCatchFunct", 1490 numExceptionTypesToCatch, 1491 innerExceptionTypesToCatch); 1492 1493 // Outer function will catch odd type infos 1494 unsigned outerExceptionTypesToCatch[] = {3, 1, 5}; 1495 numExceptionTypesToCatch = sizeof(outerExceptionTypesToCatch) / 1496 sizeof(unsigned); 1497 1498 // Generate outer function 1499 llvm::Function *outerCatchFunct = createCatchWrappedInvokeFunction(module, 1500 builder, 1501 fpm, 1502 *innerCatchFunct, 1503 "outerCatchFunct", 1504 numExceptionTypesToCatch, 1505 outerExceptionTypesToCatch); 1506 1507 // Return outer function to run 1508 return(outerCatchFunct); 1509 } 1510 1511 namespace { 1512 /// Represents our foreign exceptions 1513 class OurCppRunException : public std::runtime_error { 1514 public: 1515 OurCppRunException(const std::string reason) : 1516 std::runtime_error(reason) {} 1517 1518 OurCppRunException (const OurCppRunException &toCopy) : 1519 std::runtime_error(toCopy) {} 1520 1521 OurCppRunException &operator = (const OurCppRunException &toCopy) { 1522 return(reinterpret_cast<OurCppRunException&>( 1523 std::runtime_error::operator=(toCopy))); 1524 } 1525 1526 ~OurCppRunException(void) throw() override {} 1527 }; 1528 } // end anonymous namespace 1529 1530 /// Throws foreign C++ exception. 1531 /// @param ignoreIt unused parameter that allows function to match implied 1532 /// generated function contract. 1533 extern "C" 1534 void throwCppException (int32_t ignoreIt) { 1535 throw(OurCppRunException("thrown by throwCppException(...)")); 1536 } 1537 1538 typedef void (*OurExceptionThrowFunctType) (int32_t typeToThrow); 1539 1540 /// This is a test harness which runs test by executing generated 1541 /// function with a type info type to throw. Harness wraps the execution 1542 /// of generated function in a C++ try catch clause. 1543 /// @param engine execution engine to use for executing generated function. 1544 /// This demo program expects this to be a JIT instance for demo 1545 /// purposes. 1546 /// @param function generated test function to run 1547 /// @param typeToThrow type info type of generated exception to throw, or 1548 /// indicator to cause foreign exception to be thrown. 1549 static 1550 void runExceptionThrow(llvm::ExecutionEngine *engine, 1551 llvm::Function *function, 1552 int32_t typeToThrow) { 1553 1554 // Find test's function pointer 1555 OurExceptionThrowFunctType functPtr = 1556 reinterpret_cast<OurExceptionThrowFunctType>( 1557 reinterpret_cast<intptr_t>(engine->getPointerToFunction(function))); 1558 1559 try { 1560 // Run test 1561 (*functPtr)(typeToThrow); 1562 } 1563 catch (OurCppRunException exc) { 1564 // Catch foreign C++ exception 1565 fprintf(stderr, 1566 "\nrunExceptionThrow(...):In C++ catch OurCppRunException " 1567 "with reason: %s.\n", 1568 exc.what()); 1569 } 1570 catch (...) { 1571 // Catch all exceptions including our generated ones. This latter 1572 // functionality works according to the example in rules 1.6.4 of 1573 // http://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html (v1.22), 1574 // given that these will be exceptions foreign to C++ 1575 // (the _Unwind_Exception::exception_class should be different from 1576 // the one used by C++). 1577 fprintf(stderr, 1578 "\nrunExceptionThrow(...):In C++ catch all.\n"); 1579 } 1580 } 1581 1582 // 1583 // End test functions 1584 // 1585 1586 typedef llvm::ArrayRef<llvm::Type*> TypeArray; 1587 1588 /// This initialization routine creates type info globals and 1589 /// adds external function declarations to module. 1590 /// @param numTypeInfos number of linear type info associated type info types 1591 /// to create as GlobalVariable instances, starting with the value 1. 1592 /// @param module code for module instance 1593 /// @param builder builder instance 1594 static void createStandardUtilityFunctions(unsigned numTypeInfos, 1595 llvm::Module &module, 1596 llvm::IRBuilder<> &builder) { 1597 1598 llvm::LLVMContext &context = module.getContext(); 1599 1600 // Exception initializations 1601 1602 // Setup exception catch state 1603 ourExceptionNotThrownState = 1604 llvm::ConstantInt::get(llvm::Type::getInt8Ty(context), 0), 1605 ourExceptionThrownState = 1606 llvm::ConstantInt::get(llvm::Type::getInt8Ty(context), 1), 1607 ourExceptionCaughtState = 1608 llvm::ConstantInt::get(llvm::Type::getInt8Ty(context), 2), 1609 1610 1611 1612 // Create our type info type 1613 ourTypeInfoType = llvm::StructType::get(context, 1614 TypeArray(builder.getInt32Ty())); 1615 1616 llvm::Type *caughtResultFieldTypes[] = { 1617 builder.getInt8PtrTy(), 1618 builder.getInt32Ty() 1619 }; 1620 1621 // Create our landingpad result type 1622 ourCaughtResultType = llvm::StructType::get(context, 1623 TypeArray(caughtResultFieldTypes)); 1624 1625 // Create OurException type 1626 ourExceptionType = llvm::StructType::get(context, 1627 TypeArray(ourTypeInfoType)); 1628 1629 // Create portion of _Unwind_Exception type 1630 // 1631 // Note: Declaring only a portion of the _Unwind_Exception struct. 1632 // Does this cause problems? 1633 ourUnwindExceptionType = 1634 llvm::StructType::get(context, 1635 TypeArray(builder.getInt64Ty())); 1636 1637 struct OurBaseException_t dummyException; 1638 1639 // Calculate offset of OurException::unwindException member. 1640 ourBaseFromUnwindOffset = ((uintptr_t) &dummyException) - 1641 ((uintptr_t) &(dummyException.unwindException)); 1642 1643 #ifdef DEBUG 1644 fprintf(stderr, 1645 "createStandardUtilityFunctions(...):ourBaseFromUnwindOffset " 1646 "= %" PRIi64 ", sizeof(struct OurBaseException_t) - " 1647 "sizeof(struct _Unwind_Exception) = %lu.\n", 1648 ourBaseFromUnwindOffset, 1649 sizeof(struct OurBaseException_t) - 1650 sizeof(struct _Unwind_Exception)); 1651 #endif 1652 1653 size_t numChars = sizeof(ourBaseExcpClassChars) / sizeof(char); 1654 1655 // Create our _Unwind_Exception::exception_class value 1656 ourBaseExceptionClass = genClass(ourBaseExcpClassChars, numChars); 1657 1658 // Type infos 1659 1660 std::string baseStr = "typeInfo", typeInfoName; 1661 std::ostringstream typeInfoNameBuilder; 1662 std::vector<llvm::Constant*> structVals; 1663 1664 llvm::Constant *nextStruct; 1665 1666 // Generate each type info 1667 // 1668 // Note: First type info is not used. 1669 for (unsigned i = 0; i <= numTypeInfos; ++i) { 1670 structVals.clear(); 1671 structVals.push_back(llvm::ConstantInt::get(builder.getInt32Ty(), i)); 1672 nextStruct = llvm::ConstantStruct::get(ourTypeInfoType, structVals); 1673 1674 typeInfoNameBuilder.str(""); 1675 typeInfoNameBuilder << baseStr << i; 1676 typeInfoName = typeInfoNameBuilder.str(); 1677 1678 // Note: Does not seem to work without allocation 1679 new llvm::GlobalVariable(module, 1680 ourTypeInfoType, 1681 true, 1682 llvm::GlobalValue::ExternalLinkage, 1683 nextStruct, 1684 typeInfoName); 1685 1686 ourTypeInfoNames.push_back(typeInfoName); 1687 ourTypeInfoNamesIndex[i] = typeInfoName; 1688 } 1689 1690 ArgNames argNames; 1691 ArgTypes argTypes; 1692 llvm::Function *funct = NULL; 1693 1694 // print32Int 1695 1696 llvm::Type *retType = builder.getVoidTy(); 1697 1698 argTypes.clear(); 1699 argTypes.push_back(builder.getInt32Ty()); 1700 argTypes.push_back(builder.getInt8PtrTy()); 1701 1702 argNames.clear(); 1703 1704 createFunction(module, 1705 retType, 1706 argTypes, 1707 argNames, 1708 "print32Int", 1709 llvm::Function::ExternalLinkage, 1710 true, 1711 false); 1712 1713 // print64Int 1714 1715 retType = builder.getVoidTy(); 1716 1717 argTypes.clear(); 1718 argTypes.push_back(builder.getInt64Ty()); 1719 argTypes.push_back(builder.getInt8PtrTy()); 1720 1721 argNames.clear(); 1722 1723 createFunction(module, 1724 retType, 1725 argTypes, 1726 argNames, 1727 "print64Int", 1728 llvm::Function::ExternalLinkage, 1729 true, 1730 false); 1731 1732 // printStr 1733 1734 retType = builder.getVoidTy(); 1735 1736 argTypes.clear(); 1737 argTypes.push_back(builder.getInt8PtrTy()); 1738 1739 argNames.clear(); 1740 1741 createFunction(module, 1742 retType, 1743 argTypes, 1744 argNames, 1745 "printStr", 1746 llvm::Function::ExternalLinkage, 1747 true, 1748 false); 1749 1750 // throwCppException 1751 1752 retType = builder.getVoidTy(); 1753 1754 argTypes.clear(); 1755 argTypes.push_back(builder.getInt32Ty()); 1756 1757 argNames.clear(); 1758 1759 createFunction(module, 1760 retType, 1761 argTypes, 1762 argNames, 1763 "throwCppException", 1764 llvm::Function::ExternalLinkage, 1765 true, 1766 false); 1767 1768 // deleteOurException 1769 1770 retType = builder.getVoidTy(); 1771 1772 argTypes.clear(); 1773 argTypes.push_back(builder.getInt8PtrTy()); 1774 1775 argNames.clear(); 1776 1777 createFunction(module, 1778 retType, 1779 argTypes, 1780 argNames, 1781 "deleteOurException", 1782 llvm::Function::ExternalLinkage, 1783 true, 1784 false); 1785 1786 // createOurException 1787 1788 retType = builder.getInt8PtrTy(); 1789 1790 argTypes.clear(); 1791 argTypes.push_back(builder.getInt32Ty()); 1792 1793 argNames.clear(); 1794 1795 createFunction(module, 1796 retType, 1797 argTypes, 1798 argNames, 1799 "createOurException", 1800 llvm::Function::ExternalLinkage, 1801 true, 1802 false); 1803 1804 // _Unwind_RaiseException 1805 1806 retType = builder.getInt32Ty(); 1807 1808 argTypes.clear(); 1809 argTypes.push_back(builder.getInt8PtrTy()); 1810 1811 argNames.clear(); 1812 1813 funct = createFunction(module, 1814 retType, 1815 argTypes, 1816 argNames, 1817 "_Unwind_RaiseException", 1818 llvm::Function::ExternalLinkage, 1819 true, 1820 false); 1821 1822 funct->setDoesNotReturn(); 1823 1824 // _Unwind_Resume 1825 1826 retType = builder.getInt32Ty(); 1827 1828 argTypes.clear(); 1829 argTypes.push_back(builder.getInt8PtrTy()); 1830 1831 argNames.clear(); 1832 1833 funct = createFunction(module, 1834 retType, 1835 argTypes, 1836 argNames, 1837 "_Unwind_Resume", 1838 llvm::Function::ExternalLinkage, 1839 true, 1840 false); 1841 1842 funct->setDoesNotReturn(); 1843 1844 // ourPersonality 1845 1846 retType = builder.getInt32Ty(); 1847 1848 argTypes.clear(); 1849 argTypes.push_back(builder.getInt32Ty()); 1850 argTypes.push_back(builder.getInt32Ty()); 1851 argTypes.push_back(builder.getInt64Ty()); 1852 argTypes.push_back(builder.getInt8PtrTy()); 1853 argTypes.push_back(builder.getInt8PtrTy()); 1854 1855 argNames.clear(); 1856 1857 createFunction(module, 1858 retType, 1859 argTypes, 1860 argNames, 1861 "ourPersonality", 1862 llvm::Function::ExternalLinkage, 1863 true, 1864 false); 1865 1866 // llvm.eh.typeid.for intrinsic 1867 1868 getDeclaration(&module, llvm::Intrinsic::eh_typeid_for); 1869 } 1870 1871 1872 //===----------------------------------------------------------------------===// 1873 // Main test driver code. 1874 //===----------------------------------------------------------------------===// 1875 1876 /// Demo main routine which takes the type info types to throw. A test will 1877 /// be run for each given type info type. While type info types with the value 1878 /// of -1 will trigger a foreign C++ exception to be thrown; type info types 1879 /// <= 6 and >= 1 will be caught by test functions; and type info types > 6 1880 /// will result in exceptions which pass through to the test harness. All other 1881 /// type info types are not supported and could cause a crash. 1882 int main(int argc, char *argv[]) { 1883 if (argc == 1) { 1884 fprintf(stderr, 1885 "\nUsage: ExceptionDemo <exception type to throw> " 1886 "[<type 2>...<type n>].\n" 1887 " Each type must have the value of 1 - 6 for " 1888 "generated exceptions to be caught;\n" 1889 " the value -1 for foreign C++ exceptions to be " 1890 "generated and thrown;\n" 1891 " or the values > 6 for exceptions to be ignored.\n" 1892 "\nTry: ExceptionDemo 2 3 7 -1\n" 1893 " for a full test.\n\n"); 1894 return(0); 1895 } 1896 1897 // If not set, exception handling will not be turned on 1898 llvm::TargetOptions Opts; 1899 1900 llvm::InitializeNativeTarget(); 1901 llvm::InitializeNativeTargetAsmPrinter(); 1902 llvm::LLVMContext Context; 1903 llvm::IRBuilder<> theBuilder(Context); 1904 1905 // Make the module, which holds all the code. 1906 std::unique_ptr<llvm::Module> Owner = 1907 std::make_unique<llvm::Module>("my cool jit", Context); 1908 llvm::Module *module = Owner.get(); 1909 1910 std::unique_ptr<llvm::RTDyldMemoryManager> MemMgr(new llvm::SectionMemoryManager()); 1911 1912 // Build engine with JIT 1913 llvm::EngineBuilder factory(std::move(Owner)); 1914 factory.setEngineKind(llvm::EngineKind::JIT); 1915 factory.setTargetOptions(Opts); 1916 factory.setMCJITMemoryManager(std::move(MemMgr)); 1917 llvm::ExecutionEngine *executionEngine = factory.create(); 1918 1919 { 1920 llvm::legacy::FunctionPassManager fpm(module); 1921 1922 // Set up the optimizer pipeline. 1923 // Start with registering info about how the 1924 // target lays out data structures. 1925 module->setDataLayout(executionEngine->getDataLayout()); 1926 1927 // Optimizations turned on 1928 #ifdef ADD_OPT_PASSES 1929 1930 // Basic AliasAnslysis support for GVN. 1931 fpm.add(llvm::createBasicAliasAnalysisPass()); 1932 1933 // Promote allocas to registers. 1934 fpm.add(llvm::createPromoteMemoryToRegisterPass()); 1935 1936 // Do simple "peephole" optimizations and bit-twiddling optzns. 1937 fpm.add(llvm::createInstructionCombiningPass()); 1938 1939 // Reassociate expressions. 1940 fpm.add(llvm::createReassociatePass()); 1941 1942 // Eliminate Common SubExpressions. 1943 fpm.add(llvm::createGVNPass()); 1944 1945 // Simplify the control flow graph (deleting unreachable 1946 // blocks, etc). 1947 fpm.add(llvm::createCFGSimplificationPass()); 1948 #endif // ADD_OPT_PASSES 1949 1950 fpm.doInitialization(); 1951 1952 // Generate test code using function throwCppException(...) as 1953 // the function which throws foreign exceptions. 1954 llvm::Function *toRun = 1955 createUnwindExceptionTest(*module, 1956 theBuilder, 1957 fpm, 1958 "throwCppException"); 1959 1960 executionEngine->finalizeObject(); 1961 1962 #ifndef NDEBUG 1963 fprintf(stderr, "\nBegin module dump:\n\n"); 1964 1965 module->dump(); 1966 1967 fprintf(stderr, "\nEnd module dump:\n"); 1968 #endif 1969 1970 fprintf(stderr, "\n\nBegin Test:\n"); 1971 1972 for (int i = 1; i < argc; ++i) { 1973 // Run test for each argument whose value is the exception 1974 // type to throw. 1975 runExceptionThrow(executionEngine, 1976 toRun, 1977 (unsigned) strtoul(argv[i], NULL, 10)); 1978 } 1979 1980 fprintf(stderr, "\nEnd Test:\n\n"); 1981 } 1982 1983 delete executionEngine; 1984 1985 return 0; 1986 } 1987