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/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 this 68 // header file included. A user on ubuntu reported, undefined symbols 69 // for stderr, and fprintf, and the addition of this include fixed the 70 // issue for them. Given that LLVM's best practices include the goal 71 // of reducing the number of redundant header files included, the 72 // correct solution would be to find out why these symbols are not 73 // defined for the system in question, and fix the issue by finding out 74 // 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_CONSTS 85 #define USE_GLOBAL_STR_CONSTS true 86 #endif 87 88 llvm::ExitOnError ExitOnErr; 89 90 // 91 // Example types 92 // 93 94 /// This is our simplistic type info 95 struct OurExceptionType_t { 96 /// type info type 97 int type; 98 }; 99 100 101 /// This is our Exception class which relies on a negative offset to calculate 102 /// pointers to its instances from pointers to its unwindException member. 103 /// 104 /// Note: The above unwind.h defines struct _Unwind_Exception to be aligned 105 /// on a double word boundary. This is necessary to match the standard: 106 /// http://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html 107 struct OurBaseException_t { 108 struct OurExceptionType_t type; 109 110 // Note: This is properly aligned in unwind.h 111 struct _Unwind_Exception unwindException; 112 }; 113 114 115 // Note: Not needed since we are C++ 116 typedef struct OurBaseException_t OurException; 117 typedef struct _Unwind_Exception OurUnwindException; 118 119 // 120 // Various globals used to support typeinfo and generatted exceptions in 121 // general 122 // 123 124 static std::map<std::string, llvm::Value*> namedValues; 125 126 int64_t ourBaseFromUnwindOffset; 127 128 const unsigned char ourBaseExcpClassChars[] = 129 {'o', 'b', 'j', '\0', 'b', 'a', 's', '\0'}; 130 131 132 static uint64_t ourBaseExceptionClass = 0; 133 134 static std::vector<std::string> ourTypeInfoNames; 135 static std::map<int, std::string> ourTypeInfoNamesIndex; 136 137 static llvm::StructType *ourTypeInfoType; 138 static llvm::StructType *ourCaughtResultType; 139 static llvm::StructType *ourExceptionType; 140 static llvm::StructType *ourUnwindExceptionType; 141 142 static llvm::ConstantInt *ourExceptionNotThrownState; 143 static llvm::ConstantInt *ourExceptionThrownState; 144 static llvm::ConstantInt *ourExceptionCaughtState; 145 146 typedef std::vector<std::string> ArgNames; 147 typedef std::vector<llvm::Type*> ArgTypes; 148 typedef llvm::ArrayRef<llvm::Type *> TypeArray; 149 150 // 151 // Code Generation Utilities 152 // 153 154 /// Utility used to create a function, both declarations and definitions 155 /// @param module for module instance 156 /// @param retType function return type 157 /// @param theArgTypes function's ordered argument types 158 /// @param theArgNames function's ordered arguments needed if use of this 159 /// function corresponds to a function definition. Use empty 160 /// aggregate for function declarations. 161 /// @param functName function name 162 /// @param linkage function linkage 163 /// @param declarationOnly for function declarations 164 /// @param isVarArg function uses vararg arguments 165 /// @returns function instance 166 llvm::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 of 196 /// the parent function. This is used for mutable variables etc. 197 /// @param function parent instance 198 /// @param varName stack variable name 199 /// @param type stack variable type 200 /// @param initWith optional constant initialization value 201 /// @returns AllocaInst instance 202 static 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 End 219 // 220 221 // 222 // Runtime C Library functions 223 // 224 225 namespace { 226 template <typename Type_> 227 uintptr_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 used 236 extern "C" { 237 238 // Note: Better ways to decide on bit width 239 // 240 /// Prints a 32 bit number, according to the format, to stderr. 241 /// @param intToPrint integer to print 242 /// @param format printf like format to use when printing 243 void print32Int(int intToPrint, const char *format) { 244 if (format) { 245 // Note: No NULL check 246 fprintf(stderr, format, intToPrint); 247 } 248 else { 249 // Note: No NULL check 250 fprintf(stderr, "::print32Int(...):NULL arg.\n"); 251 } 252 } 253 254 255 // Note: Better ways to decide on bit width 256 // 257 /// Prints a 64 bit number, according to the format, to stderr. 258 /// @param intToPrint integer to print 259 /// @param format printf like format to use when printing 260 void print64Int(long int intToPrint, const char *format) { 261 if (format) { 262 // Note: No NULL check 263 fprintf(stderr, format, intToPrint); 264 } 265 else { 266 // Note: No NULL check 267 fprintf(stderr, "::print64Int(...):NULL arg.\n"); 268 } 269 } 270 271 272 /// Prints a C string to stderr 273 /// @param toPrint string to print 274 void 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 address 285 /// is calculated from the supplied OurBaseException_t::unwindException 286 /// member address. Handles (ignores), NULL pointers. 287 /// @param expToDelete exception to delete 288 void deleteOurException(OurUnwindException *expToDelete) { 289 #ifdef DEBUG 290 fprintf(stderr, 291 "deleteOurException(...).\n"); 292 #endif 293 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 function 303 /// used by foreign exception handlers when deleting our exception 304 /// (OurException), instances. 305 /// @param reason See @link http://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html 306 /// @unlink 307 /// @param expToDelete exception instance to delete 308 void deleteFromUnwindOurException(_Unwind_Reason_Code reason, 309 OurUnwindException *expToDelete) { 310 #ifdef DEBUG 311 fprintf(stderr, 312 "deleteFromUnwindOurException(...).\n"); 313 #endif 314 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 type 322 OurUnwindException *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 pointer 334 /// See Variable Length Data in: 335 /// @link http://dwarfstd.org/Dwarf3.pdf @unlink 336 /// @param data reference variable holding memory pointer to decode from 337 /// @returns decoded value 338 static 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 pointer 358 /// See Variable Length Data in: 359 /// @link http://dwarfstd.org/Dwarf3.pdf @unlink 360 /// @param data reference variable holding memory pointer to decode from 361 /// @returns decoded value 362 static 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 384 unsigned 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 supported 405 abort(); 406 } 407 } 408 409 /// Read a pointer encoded value and advance pointer 410 /// See Variable Length Data in: 411 /// @link http://dwarfstd.org/Dwarf3.pdf @unlink 412 /// @param data reference variable holding memory pointer to decode from 413 /// @param encoding dwarf encoding type 414 /// @returns decoded value 415 static 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 value 423 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 tested 431 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 supported 454 abort(); 455 break; 456 } 457 458 // then add relative offset 459 switch (encoding & 0x70) { 460 case llvm::dwarf::DW_EH_PE_absptr: 461 // do nothing 462 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 supported 472 abort(); 473 break; 474 } 475 476 // then apply indirection 477 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 infos 488 /// (OurExceptionType_t instances). Returns whether or not a dwarf emitted 489 /// action matches the supplied exception type. If such a match succeeds, 490 /// the resultAction argument will be set with > 0 index value. Only 491 /// corresponding llvm.eh.selector type info arguments, cleanup arguments 492 /// are supported. Filters are not supported. 493 /// See Variable Length Data in: 494 /// @link http://dwarfstd.org/Dwarf3.pdf @unlink 495 /// Also see @link http://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html @unlink 496 /// @param resultAction reference variable which will be set with result 497 /// @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 only 504 /// a cleanup was found 505 static 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 DEBUG 524 fprintf(stderr, 525 "handleActionValue(...): exceptionObject = <%p>, " 526 "excp = <%p>.\n", 527 (void*)exceptionObject, 528 (void*)excp); 529 #endif 530 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 of 538 // type info address offset, and action offset to the next 539 // emitted action. 540 typeOffset = readSLEB128(&actionPos); 541 tempActionPos = actionPos; 542 actionOffset = readSLEB128(&tempActionPos); 543 544 #ifdef DEBUG 545 fprintf(stderr, 546 "handleActionValue(...):typeOffset: <%" PRIi64 ">, " 547 "actionOffset: <%" PRIi64 ">.\n", 548 typeOffset, 549 actionOffset); 550 #endif 551 assert((typeOffset >= 0) && 552 "handleActionValue(...):filters are not supported."); 553 554 // Note: A typeOffset == 0 implies that a cleanup llvm.eh.selector 555 // argument has been matched. 556 if (typeOffset > 0) { 557 #ifdef DEBUG 558 fprintf(stderr, 559 "handleActionValue(...):actionValue <%d> found.\n", 560 i); 561 #endif 562 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 DEBUG 575 fprintf(stderr, 576 "handleActionValue(...):actionValue not found.\n"); 577 #endif 578 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 @unlink 590 /// @param version unsupported (ignored), unwind version 591 /// @param lsda language specific data area 592 /// @param _Unwind_Action actions minimally supported unwind stage 593 /// (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 context 598 /// @returns minimally supported unwinding control indicator 599 static _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 DEBUG 610 fprintf(stderr, 611 "handleLsda(...):lsda is non-zero.\n"); 612 #endif 613 614 // Get the current instruction pointer and offset it before next 615 // 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 the 619 // 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 corresponding 625 // dwarf emission 626 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 which 639 // were flagged by type info arguments to llvm.eh.selector 640 // intrinsic 641 classInfoOffset = readULEB128(&lsda); 642 ClassInfo = lsda + classInfoOffset; 643 } 644 645 // Walk call-site table looking for range that 646 // 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 value 665 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 pads 670 actionEntry = 0; 671 } 672 673 if (landingPad == 0) { 674 #ifdef DEBUG 675 fprintf(stderr, 676 "handleLsda(...): No landing pad found.\n"); 677 #endif 678 679 continue; // no landing pad for this entry 680 } 681 682 if (actionEntry) { 683 actionEntry += ((uintptr_t) actionTableStart) - 1; 684 } 685 else { 686 #ifdef DEBUG 687 fprintf(stderr, 688 "handleLsda(...):No action table found.\n"); 689 #endif 690 } 691 692 bool exceptionMatched = false; 693 694 if ((start <= pcOffset) && (pcOffset < (start + length))) { 695 #ifdef DEBUG 696 fprintf(stderr, 697 "handleLsda(...): Landing pad found.\n"); 698 #endif 699 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 DEBUG 712 fprintf(stderr, 713 "handleLsda(...): installed landing pad " 714 "context.\n"); 715 #endif 716 717 // Found landing pad for the PC. 718 // Set Instruction Pointer to so we re-enter function 719 // at landing pad. The landing pad is created by the 720 // 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 corresponds 726 // to the return of the llvm.eh.selector intrinsic 727 if (!actionEntry || !exceptionMatched) { 728 // We indicate cleanup only 729 _Unwind_SetGR(context, 730 __builtin_eh_return_data_regno(1), 731 0); 732 } 733 else { 734 // Matched type info index of llvm.eh.selector intrinsic 735 // passed here. 736 _Unwind_SetGR(context, 737 __builtin_eh_return_data_regno(1), 738 actionValue); 739 } 740 741 // To execute landing pad set here 742 _Unwind_SetIP(context, funcStart + landingPad); 743 ret = _URC_INSTALL_CONTEXT; 744 } 745 else if (exceptionMatched) { 746 #ifdef DEBUG 747 fprintf(stderr, 748 "handleLsda(...): setting handler found.\n"); 749 #endif 750 ret = _URC_HANDLER_FOUND; 751 } 752 else { 753 // Note: Only non-clean up handlers are marked as 754 // found. Otherwise the clean up handlers will be 755 // re-found and executed during the clean up 756 // phase. 757 #ifdef DEBUG 758 fprintf(stderr, 759 "handleLsda(...): cleanup handler found.\n"); 760 #endif 761 } 762 763 break; 764 } 765 } 766 767 return(ret); 768 } 769 770 771 /// This is the personality function which is embedded (dwarf emitted), in the 772 /// dwarf unwind info block. Again see: JITDwarfEmitter.cpp. 773 /// See @link http://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html @unlink 774 /// @param version unsupported (ignored), unwind version 775 /// @param _Unwind_Action actions minimally supported unwind stage 776 /// (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 context 781 /// @returns minimally supported unwinding control indicator 782 _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 DEBUG 787 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 #endif 798 799 const uint8_t *lsda = (const uint8_t *)_Unwind_GetLanguageSpecificData(context); 800 801 #ifdef DEBUG 802 fprintf(stderr, 803 "ourPersonality(...):lsda = <%p>.\n", 804 (void*)lsda); 805 #endif 806 807 // The real work of the personality function is captured here 808 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 handling 819 /// embedded \0s. 820 /// See @link http://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html @unlink 821 /// @param classChars char array to encode. NULL values not checkedf 822 /// @param classCharsSize number of chars in classChars. Value is not checked. 823 /// @returns class value 824 uint64_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 End 840 // 841 842 // 843 // Code generation functions 844 // 845 846 /// Generates code to print given constant string 847 /// @param context llvm context 848 /// @param module code for module instance 849 /// @param builder builder instance 850 /// @param toPrint string to print 851 /// @param useGlobal A value of true (default) indicates a GlobalValue is 852 /// generated, and is used to hold the constant string. A value of 853 /// false indicates that the constant string will be stored on the 854 /// stack. 855 void 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 allocation 868 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 constant 887 /// string format, and a given print function. 888 /// @param context llvm context 889 /// @param module code for module instance 890 /// @param builder builder instance 891 /// @param printFunct function used to "print" integer 892 /// @param toPrint string to print 893 /// @param format printf like formatting string for print 894 /// @param useGlobal A value of true (default) indicates a GlobalValue is 895 /// generated, and is used to hold the constant string. A value of 896 /// false indicates that the constant string will be stored on the 897 /// stack. 898 void 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 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.getPtrTy()); 923 builder.CreateCall(&printFunct, {toPrint, cast}); 924 } 925 926 /// Generates code to handle finally block type semantics: always runs 927 /// regardless of whether a thrown exception is passing through or the 928 /// parent function is simply exiting. In addition to printing some state 929 /// to stderr, this code will resume the exception handling--runs the 930 /// unwind resume block, if the exception has not been previously caught 931 /// by a catch clause, and will otherwise execute the end block (terminator 932 /// block). In addition this function creates the corresponding function's 933 /// stack storage for the exception pointer and catch flag status. 934 /// @param context llvm context 935 /// @param module code for module instance 936 /// @param builder builder instance 937 /// @param toAddTo parent function to add block to 938 /// @param blockName block name of new "finally" block. 939 /// @param functionId output id used for printing 940 /// @param terminatorBlock terminator "end" block 941 /// @param unwindResumeBlock unwind resume block 942 /// @param exceptionCaughtFlag reference exception caught/thrown status storage 943 /// @param exceptionStorage reference to exception pointer storage 944 /// @param caughtResultStorage reference to landingpad result storage 945 /// @returns newly created block 946 static 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 of 1011 /// catch executed, sets an exception caught flag, and executes passed in 1012 /// end block (terminator block). 1013 /// @param context llvm context 1014 /// @param module code for module instance 1015 /// @param builder builder instance 1016 /// @param toAddTo parent function to add block to 1017 /// @param blockName block name of new "catch" block. 1018 /// @param functionId output id used for printing 1019 /// @param terminatorBlock terminator "end" block 1020 /// @param exceptionCaughtFlag exception caught/thrown status 1021 /// @returns newly created block 1022 static 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 << blockName 1040 << " in " 1041 << functionId 1042 << 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, whose 1056 /// unwind block will "catch" the type info types correspondingly held in the 1057 /// exceptionTypesToCatch argument. If the toInvoke function throws an 1058 /// exception which does not match any type info types contained in 1059 /// exceptionTypesToCatch, the generated code will call _Unwind_Resume 1060 /// with the raised exception. On the other hand the generated code will 1061 /// normally exit if the toInvoke function does not throw an exception. 1062 /// The generated "finally" block is always run regardless of the cause of 1063 /// the generated function exit. 1064 /// The generated function is returned after being verified. 1065 /// @param module code for module instance 1066 /// @param builder builder instance 1067 /// @param fpm a function pass manager holding optional IR to IR 1068 /// transformations 1069 /// @param toInvoke inner function to invoke 1070 /// @param ourId id used to printing purposes 1071 /// @param numExceptionsToCatch length of exceptionTypesToCatch array 1072 /// @param exceptionTypesToCatch array of type info types to "catch" 1073 /// @returns generated function 1074 static 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 invoke 1099 llvm::BasicBlock *entryBlock = llvm::BasicBlock::Create(context, 1100 "entry", 1101 ret); 1102 // Normal block for invoke 1103 llvm::BasicBlock *normalBlock = llvm::BasicBlock::Create(context, 1104 "normal", 1105 ret); 1106 // Unwind block for invoke 1107 llvm::BasicBlock *exceptionBlock = llvm::BasicBlock::Create(context, 1108 "exception", 1109 ret); 1110 1111 // Block which routes exception to correct catch handler block 1112 llvm::BasicBlock *exceptionRouteBlock = llvm::BasicBlock::Create(context, 1113 "exceptionRoute", 1114 ret); 1115 1116 // Foreign exception handler 1117 llvm::BasicBlock *externalExceptionBlock = llvm::BasicBlock::Create(context, 1118 "externalException", 1119 ret); 1120 1121 // Block which calls _Unwind_Resume 1122 llvm::BasicBlock *unwindResumeBlock = llvm::BasicBlock::Create(context, 1123 "unwindResume", 1124 ret); 1125 1126 // Clean up block which delete exception if needed 1127 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 if 1136 // 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 &caughtResultStorage 1148 ); 1149 1150 for (unsigned i = 0; i < numExceptionsToCatch; ++i) { 1151 nextName = ourTypeInfoNames[exceptionTypesToCatch[i]]; 1152 1153 // One catch block per type info to be caught 1154 catchBlocks[i] = createCatchBlock(context, 1155 module, 1156 builder, 1157 *ret, 1158 nextName, 1159 ourId, 1160 *finallyBlock, 1161 *exceptionCaughtFlag); 1162 } 1163 1164 // Entry Block 1165 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 Block 1176 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 exceptions 1187 builder.CreateCall(deleteOurException, 1188 builder.CreateLoad(builder.getPtrTy(), exceptionStorage)); 1189 builder.CreateRetVoid(); 1190 1191 // Normal Block 1192 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 called 1202 builder.CreateBr(finallyBlock); 1203 1204 // Unwind Resume Block 1205 1206 builder.SetInsertPoint(unwindResumeBlock); 1207 1208 builder.CreateResume( 1209 builder.CreateLoad(ourCaughtResultType, 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 = builder.CreateLoad( 1245 builder.getInt64Ty(), 1246 builder.CreateStructGEP(ourUnwindExceptionType, unwindException, 0)); 1247 1248 // Branch to the externalExceptionBlock if the exception is foreign or 1249 // 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 Block 1257 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 block 1267 builder.CreateBr(finallyBlock); 1268 1269 // Exception Route Block 1270 1271 builder.SetInsertPoint(exceptionRouteBlock); 1272 1273 // Casts exception pointer (_Unwind_Exception instance) to parent 1274 // (OurException instance). 1275 // 1276 // Note: ourBaseFromUnwindOffset is usually negative 1277 llvm::Value *typeInfoThrown = builder.CreateConstGEP1_64( 1278 builder.getInt8Ty(), unwindException, ourBaseFromUnwindOffset); 1279 1280 // Retrieve thrown exception type info type 1281 // 1282 // Note: Index is not relative to pointer but instead to structure 1283 // unlike a true getelementptr (GEP) instruction 1284 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 block 1298 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 runtime 1318 /// determined type info type (argument to generated function), or if this 1319 /// runtime value matches nativeThrowType, throws a foreign exception by 1320 /// calling nativeThrowFunct. 1321 /// @param module code for module instance 1322 /// @param builder builder instance 1323 /// @param fpm a function pass manager holding optional IR to IR 1324 /// transformations 1325 /// @param ourId id used to printing purposes 1326 /// @param nativeThrowType a runtime argument of this value results in 1327 /// nativeThrowFunct being called to generate/throw exception. 1328 /// @param nativeThrowFunct function which will throw a foreign exception 1329 /// if the above nativeThrowType matches generated function's arg. 1330 /// @returns generated function 1331 static llvm::Function * 1332 createThrowExceptionFunction(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 depending 1353 // 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 exception 1358 llvm::BasicBlock *nativeThrowBlock = llvm::BasicBlock::Create(context, 1359 "nativeThrow", 1360 ret); 1361 // Throws one of our Exceptions 1362 llvm::BasicBlock *generatedThrowBlock = llvm::BasicBlock::Create(context, 1363 "generatedThrow", 1364 ret); 1365 // Retrieved runtime type info type to throw 1366 llvm::Value *exceptionType = namedValues["exceptTypeToThrow"]; 1367 1368 // nativeThrowBlock block 1369 1370 builder.SetInsertPoint(nativeThrowBlock); 1371 1372 // Throws foreign exception 1373 builder.CreateCall(&nativeThrowFunct, exceptionType); 1374 builder.CreateUnreachable(); 1375 1376 // entry block 1377 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 not 1388 // a foreign exception is thrown. Defaults to throwing one of our 1389 // 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 block 1399 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 Exception 1411 builder.CreateCall(raiseOurException, exception); 1412 builder.CreateUnreachable(); 1413 1414 llvm::verifyFunction(*ret); 1415 fpm.run(*ret); 1416 1417 return(ret); 1418 } 1419 1420 static void createStandardUtilityFunctions(unsigned numTypeInfos, 1421 llvm::Module &module, 1422 llvm::IRBuilder<> &builder); 1423 1424 /// Creates test code by generating and organizing these functions into the 1425 /// test case. The test case consists of an outer function setup to invoke 1426 /// an inner function within an environment having multiple catch and single 1427 /// finally blocks. This inner function is also setup to invoke a throw 1428 /// function within an evironment similar in nature to the outer function's 1429 /// catch and finally blocks. Each of these two functions catch mutually 1430 /// exclusive subsets (even or odd) of the type info types configured 1431 /// for this this. All generated functions have a runtime argument which 1432 /// holds a type info type to throw that each function takes and passes it 1433 /// to the inner one if such a inner function exists. This type info type is 1434 /// looked at by the generated throw function to see whether or not it should 1435 /// throw a generated exception with the same type info type, or instead call 1436 /// a supplied a function which in turn will throw a foreign exception. 1437 /// @param module code for module instance 1438 /// @param builder builder instance 1439 /// @param fpm a function pass manager holding optional IR to IR 1440 /// transformations 1441 /// @param nativeThrowFunctName name of external function which will throw 1442 /// a foreign exception 1443 /// @returns outermost generated test function. 1444 llvm::Function * 1445 createUnwindExceptionTest(llvm::Module &module, llvm::IRBuilder<> &builder, 1446 llvm::legacy::FunctionPassManager &fpm, 1447 std::string nativeThrowFunctName) { 1448 // Number of type infos to generate 1449 unsigned numTypeInfos = 6; 1450 1451 // Initialze intrisics and external functions to use along with exception 1452 // 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 cause 1459 // 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 infos 1467 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 infos 1481 unsigned outerExceptionTypesToCatch[] = {3, 1, 5}; 1482 numExceptionTypesToCatch = sizeof(outerExceptionTypesToCatch) / 1483 sizeof(unsigned); 1484 1485 // Generate outer function 1486 llvm::Function *outerCatchFunct = createCatchWrappedInvokeFunction(module, 1487 builder, 1488 fpm, 1489 *innerCatchFunct, 1490 "outerCatchFunct", 1491 numExceptionTypesToCatch, 1492 outerExceptionTypesToCatch); 1493 1494 // Return outer function to run 1495 return(outerCatchFunct); 1496 } 1497 1498 namespace { 1499 /// Represents our foreign exceptions 1500 class OurCppRunException : public std::runtime_error { 1501 public: 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 namespace 1516 1517 /// Throws foreign C++ exception. 1518 /// @param ignoreIt unused parameter that allows function to match implied 1519 /// generated function contract. 1520 extern "C" 1521 void throwCppException (int32_t ignoreIt) { 1522 throw(OurCppRunException("thrown by throwCppException(...)")); 1523 } 1524 1525 typedef void (*OurExceptionThrowFunctType) (int32_t typeToThrow); 1526 1527 /// This is a test harness which runs test by executing generated 1528 /// function with a type info type to throw. Harness wraps the execution 1529 /// 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 demo 1532 /// purposes. 1533 /// @param function generated test function to run 1534 /// @param typeToThrow type info type of generated exception to throw, or 1535 /// indicator to cause foreign exception to be thrown. 1536 static void runExceptionThrow(llvm::orc::LLJIT *JIT, std::string function, 1537 int32_t typeToThrow) { 1538 1539 // Find test's function pointer 1540 OurExceptionThrowFunctType functPtr = 1541 ExitOnErr(JIT->lookup(function)).toPtr<OurExceptionThrowFunctType>(); 1542 1543 try { 1544 // Run test 1545 (*functPtr)(typeToThrow); 1546 } 1547 catch (OurCppRunException exc) { 1548 // Catch foreign C++ exception 1549 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 latter 1556 // functionality works according to the example in rules 1.6.4 of 1557 // 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 from 1560 // the one used by C++). 1561 fprintf(stderr, 1562 "\nrunExceptionThrow(...):In C++ catch all.\n"); 1563 } 1564 } 1565 1566 // 1567 // End test functions 1568 // 1569 1570 /// This initialization routine creates type info globals and 1571 /// adds external function declarations to module. 1572 /// @param numTypeInfos number of linear type info associated type info types 1573 /// to create as GlobalVariable instances, starting with the value 1. 1574 /// @param module code for module instance 1575 /// @param builder builder instance 1576 static void createStandardUtilityFunctions(unsigned numTypeInfos, 1577 llvm::Module &module, 1578 llvm::IRBuilder<> &builder) { 1579 1580 llvm::LLVMContext &context = module.getContext(); 1581 1582 // Exception initializations 1583 1584 // Setup exception catch state 1585 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 type 1595 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 type 1604 ourCaughtResultType = llvm::StructType::get(context, 1605 TypeArray(caughtResultFieldTypes)); 1606 1607 // Create OurException type 1608 ourExceptionType = llvm::StructType::get(context, 1609 TypeArray(ourTypeInfoType)); 1610 1611 // Create portion of _Unwind_Exception type 1612 // 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 DEBUG 1626 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 #endif 1634 1635 size_t numChars = sizeof(ourBaseExcpClassChars) / sizeof(char); 1636 1637 // Create our _Unwind_Exception::exception_class value 1638 ourBaseExceptionClass = genClass(ourBaseExcpClassChars, numChars); 1639 1640 // Type infos 1641 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 info 1649 // 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 allocation 1661 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 // print32Int 1677 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 // print64Int 1696 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 // printStr 1715 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 // throwCppException 1733 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 // deleteOurException 1751 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 // createOurException 1769 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_RaiseException 1787 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_Resume 1807 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 // ourPersonality 1827 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 intrinsic 1849 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 will 1860 /// be run for each given type info type. While type info types with the value 1861 /// of -1 will trigger a foreign C++ exception to be thrown; type info types 1862 /// <= 6 and >= 1 will be caught by test functions; and type info types > 6 1863 /// will result in exceptions which pass through to the test harness. All other 1864 /// type info types are not supported and could cause a crash. 1865 int 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 LLJIT 1891 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 on 1898 #ifdef ADD_OPT_PASSES 1899 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 unreachable 1916 // blocks, etc). 1917 fpm.add(llvm::createCFGSimplificationPass()); 1918 #endif // ADD_OPT_PASSES 1919 1920 fpm.doInitialization(); 1921 1922 // Generate test code using function throwCppException(...) as 1923 // 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 NDEBUG 1930 fprintf(stderr, "\nBegin module dump:\n\n"); 1931 1932 module->print(llvm::errs(), nullptr); 1933 1934 fprintf(stderr, "\nEnd module dump:\n"); 1935 #endif 1936 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 exception 1942 // 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