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 887 /// Generates code to print given runtime integer according to constant 888 /// string format, and a given print function. 889 /// @param context llvm context 890 /// @param module code for module instance 891 /// @param builder builder instance 892 /// @param printFunct function used to "print" integer 893 /// @param toPrint string to print 894 /// @param format printf like formating string for print 895 /// @param useGlobal A value of true (default) indicates a GlobalValue is 896 /// generated, and is used to hold the constant string. A value of 897 /// false indicates that the constant string will be stored on the 898 /// stack. 899 void generateIntegerPrint(llvm::LLVMContext &context, llvm::Module &module, 900 llvm::IRBuilder<> &builder, 901 llvm::Function &printFunct, llvm::Value *toPrint, 902 std::string format, bool useGlobal = true) { 903 llvm::Constant *stringConstant = 904 llvm::ConstantDataArray::getString(context, format); 905 llvm::Value *stringVar; 906 907 if (useGlobal) { 908 // Note: Does not seem to work without allocation 909 stringVar = 910 new llvm::GlobalVariable(module, 911 stringConstant->getType(), 912 true, 913 llvm::GlobalValue::PrivateLinkage, 914 stringConstant, 915 ""); 916 } 917 else { 918 stringVar = builder.CreateAlloca(stringConstant->getType()); 919 builder.CreateStore(stringConstant, stringVar); 920 } 921 922 llvm::Value *cast = builder.CreateBitCast(stringVar, 923 builder.getPtrTy()); 924 builder.CreateCall(&printFunct, {toPrint, cast}); 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.getPtrTy(); 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( 1001 builder.CreateLoad(ourExceptionNotThrownState->getType(), 1002 *exceptionCaughtFlag), 1003 &terminatorBlock, 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(builder.getPtrTy(), 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( 1210 builder.CreateLoad(ourCaughtResultType, caughtResultStorage)); 1211 1212 // Exception Block 1213 1214 builder.SetInsertPoint(exceptionBlock); 1215 1216 llvm::Function *personality = module.getFunction("ourPersonality"); 1217 ret->setPersonalityFn(personality); 1218 1219 llvm::LandingPadInst *caughtResult = 1220 builder.CreateLandingPad(ourCaughtResultType, 1221 numExceptionsToCatch, 1222 "landingPad"); 1223 1224 caughtResult->setCleanup(true); 1225 1226 for (unsigned i = 0; i < numExceptionsToCatch; ++i) { 1227 // Set up type infos to be caught 1228 caughtResult->addClause(module.getGlobalVariable( 1229 ourTypeInfoNames[exceptionTypesToCatch[i]])); 1230 } 1231 1232 llvm::Value *unwindException = builder.CreateExtractValue(caughtResult, 0); 1233 llvm::Value *retTypeInfoIndex = builder.CreateExtractValue(caughtResult, 1); 1234 1235 // FIXME: Redundant storage which, beyond utilizing value of 1236 // caughtResultStore for unwindException storage, may be alleviated 1237 // altogether with a block rearrangement 1238 builder.CreateStore(caughtResult, caughtResultStorage); 1239 builder.CreateStore(unwindException, exceptionStorage); 1240 builder.CreateStore(ourExceptionThrownState, exceptionCaughtFlag); 1241 1242 // Retrieve exception_class member from thrown exception 1243 // (_Unwind_Exception instance). This member tells us whether or not 1244 // the exception is foreign. 1245 llvm::Value *unwindExceptionClass = builder.CreateLoad( 1246 builder.getInt64Ty(), 1247 builder.CreateStructGEP( 1248 ourUnwindExceptionType, 1249 builder.CreatePointerCast(unwindException, 1250 ourUnwindExceptionType->getPointerTo()), 1251 0)); 1252 1253 // Branch to the externalExceptionBlock if the exception is foreign or 1254 // to a catch router if not. Either way the finally block will be run. 1255 builder.CreateCondBr(builder.CreateICmpEQ(unwindExceptionClass, 1256 llvm::ConstantInt::get(builder.getInt64Ty(), 1257 ourBaseExceptionClass)), 1258 exceptionRouteBlock, 1259 externalExceptionBlock); 1260 1261 // External Exception Block 1262 1263 builder.SetInsertPoint(externalExceptionBlock); 1264 1265 generateStringPrint(context, 1266 module, 1267 builder, 1268 "Gen: Foreign exception received.\n", 1269 USE_GLOBAL_STR_CONSTS); 1270 1271 // Branch to the finally block 1272 builder.CreateBr(finallyBlock); 1273 1274 // Exception Route Block 1275 1276 builder.SetInsertPoint(exceptionRouteBlock); 1277 1278 // Casts exception pointer (_Unwind_Exception instance) to parent 1279 // (OurException instance). 1280 // 1281 // Note: ourBaseFromUnwindOffset is usually negative 1282 llvm::Value *typeInfoThrown = builder.CreatePointerCast( 1283 builder.CreateConstGEP1_64(builder.getPtrTy(), unwindException, 1284 ourBaseFromUnwindOffset), 1285 ourExceptionType->getPointerTo()); 1286 1287 // Retrieve thrown exception type info type 1288 // 1289 // Note: Index is not relative to pointer but instead to structure 1290 // unlike a true getelementptr (GEP) instruction 1291 typeInfoThrown = builder.CreateStructGEP(ourExceptionType, typeInfoThrown, 0); 1292 1293 llvm::Value *typeInfoThrownType = 1294 builder.CreateStructGEP(ourTypeInfoType, typeInfoThrown, 0); 1295 1296 llvm::Value *ti8 = 1297 builder.CreateLoad(builder.getInt8Ty(), typeInfoThrownType); 1298 generateIntegerPrint(context, module, builder, *toPrint32Int, 1299 builder.CreateZExt(ti8, builder.getInt32Ty()), 1300 "Gen: Exception type <%d> received (stack unwound) " 1301 " in " + 1302 ourId + ".\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(builder.getInt32Ty(), i), 1315 catchBlocks[nextTypeToCatch]); 1316 } 1317 1318 llvm::verifyFunction(*ret); 1319 fpm.run(*ret); 1320 1321 return(ret); 1322 } 1323 1324 1325 /// Generates function which throws either an exception matched to a runtime 1326 /// determined type info type (argument to generated function), or if this 1327 /// runtime value matches nativeThrowType, throws a foreign exception by 1328 /// calling nativeThrowFunct. 1329 /// @param module code for module instance 1330 /// @param builder builder instance 1331 /// @param fpm a function pass manager holding optional IR to IR 1332 /// transformations 1333 /// @param ourId id used to printing purposes 1334 /// @param nativeThrowType a runtime argument of this value results in 1335 /// nativeThrowFunct being called to generate/throw exception. 1336 /// @param nativeThrowFunct function which will throw a foreign exception 1337 /// if the above nativeThrowType matches generated function's arg. 1338 /// @returns generated function 1339 static llvm::Function * 1340 createThrowExceptionFunction(llvm::Module &module, llvm::IRBuilder<> &builder, 1341 llvm::legacy::FunctionPassManager &fpm, 1342 std::string ourId, int32_t nativeThrowType, 1343 llvm::Function &nativeThrowFunct) { 1344 llvm::LLVMContext &context = module.getContext(); 1345 namedValues.clear(); 1346 ArgTypes unwindArgTypes; 1347 unwindArgTypes.push_back(builder.getInt32Ty()); 1348 ArgNames unwindArgNames; 1349 unwindArgNames.push_back("exceptTypeToThrow"); 1350 1351 llvm::Function *ret = createFunction(module, 1352 builder.getVoidTy(), 1353 unwindArgTypes, 1354 unwindArgNames, 1355 ourId, 1356 llvm::Function::ExternalLinkage, 1357 false, 1358 false); 1359 1360 // Throws either one of our exception or a native C++ exception depending 1361 // on a runtime argument value containing a type info type. 1362 llvm::BasicBlock *entryBlock = llvm::BasicBlock::Create(context, 1363 "entry", 1364 ret); 1365 // Throws a foreign exception 1366 llvm::BasicBlock *nativeThrowBlock = llvm::BasicBlock::Create(context, 1367 "nativeThrow", 1368 ret); 1369 // Throws one of our Exceptions 1370 llvm::BasicBlock *generatedThrowBlock = llvm::BasicBlock::Create(context, 1371 "generatedThrow", 1372 ret); 1373 // Retrieved runtime type info type to throw 1374 llvm::Value *exceptionType = namedValues["exceptTypeToThrow"]; 1375 1376 // nativeThrowBlock block 1377 1378 builder.SetInsertPoint(nativeThrowBlock); 1379 1380 // Throws foreign exception 1381 builder.CreateCall(&nativeThrowFunct, exceptionType); 1382 builder.CreateUnreachable(); 1383 1384 // entry block 1385 1386 builder.SetInsertPoint(entryBlock); 1387 1388 llvm::Function *toPrint32Int = module.getFunction("print32Int"); 1389 generateIntegerPrint(context, module, builder, *toPrint32Int, 1390 builder.CreateZExt(exceptionType, builder.getInt32Ty()), 1391 "\nGen: About to throw exception type <%d> in " + ourId + 1392 ".\n", 1393 USE_GLOBAL_STR_CONSTS); 1394 1395 // Switches on runtime type info type value to determine whether or not 1396 // a foreign exception is thrown. Defaults to throwing one of our 1397 // generated exceptions. 1398 llvm::SwitchInst *theSwitch = builder.CreateSwitch(exceptionType, 1399 generatedThrowBlock, 1400 1); 1401 1402 theSwitch->addCase(llvm::ConstantInt::get(llvm::Type::getInt32Ty(context), 1403 nativeThrowType), 1404 nativeThrowBlock); 1405 1406 // generatedThrow block 1407 1408 builder.SetInsertPoint(generatedThrowBlock); 1409 1410 llvm::Function *createOurException = module.getFunction("createOurException"); 1411 llvm::Function *raiseOurException = module.getFunction( 1412 "_Unwind_RaiseException"); 1413 1414 // Creates exception to throw with runtime type info type. 1415 llvm::Value *exception = builder.CreateCall(createOurException, 1416 namedValues["exceptTypeToThrow"]); 1417 1418 // Throw generated Exception 1419 builder.CreateCall(raiseOurException, exception); 1420 builder.CreateUnreachable(); 1421 1422 llvm::verifyFunction(*ret); 1423 fpm.run(*ret); 1424 1425 return(ret); 1426 } 1427 1428 static void createStandardUtilityFunctions(unsigned numTypeInfos, 1429 llvm::Module &module, 1430 llvm::IRBuilder<> &builder); 1431 1432 /// Creates test code by generating and organizing these functions into the 1433 /// test case. The test case consists of an outer function setup to invoke 1434 /// an inner function within an environment having multiple catch and single 1435 /// finally blocks. This inner function is also setup to invoke a throw 1436 /// function within an evironment similar in nature to the outer function's 1437 /// catch and finally blocks. Each of these two functions catch mutually 1438 /// exclusive subsets (even or odd) of the type info types configured 1439 /// for this this. All generated functions have a runtime argument which 1440 /// holds a type info type to throw that each function takes and passes it 1441 /// to the inner one if such a inner function exists. This type info type is 1442 /// looked at by the generated throw function to see whether or not it should 1443 /// throw a generated exception with the same type info type, or instead call 1444 /// a supplied a function which in turn will throw a foreign exception. 1445 /// @param module code for module instance 1446 /// @param builder builder instance 1447 /// @param fpm a function pass manager holding optional IR to IR 1448 /// transformations 1449 /// @param nativeThrowFunctName name of external function which will throw 1450 /// a foreign exception 1451 /// @returns outermost generated test function. 1452 llvm::Function * 1453 createUnwindExceptionTest(llvm::Module &module, llvm::IRBuilder<> &builder, 1454 llvm::legacy::FunctionPassManager &fpm, 1455 std::string nativeThrowFunctName) { 1456 // Number of type infos to generate 1457 unsigned numTypeInfos = 6; 1458 1459 // Initialze intrisics and external functions to use along with exception 1460 // and type info globals. 1461 createStandardUtilityFunctions(numTypeInfos, 1462 module, 1463 builder); 1464 llvm::Function *nativeThrowFunct = module.getFunction(nativeThrowFunctName); 1465 1466 // Create exception throw function using the value ~0 to cause 1467 // foreign exceptions to be thrown. 1468 llvm::Function *throwFunct = createThrowExceptionFunction(module, 1469 builder, 1470 fpm, 1471 "throwFunct", 1472 ~0, 1473 *nativeThrowFunct); 1474 // Inner function will catch even type infos 1475 unsigned innerExceptionTypesToCatch[] = {6, 2, 4}; 1476 size_t numExceptionTypesToCatch = sizeof(innerExceptionTypesToCatch) / 1477 sizeof(unsigned); 1478 1479 // Generate inner function. 1480 llvm::Function *innerCatchFunct = createCatchWrappedInvokeFunction(module, 1481 builder, 1482 fpm, 1483 *throwFunct, 1484 "innerCatchFunct", 1485 numExceptionTypesToCatch, 1486 innerExceptionTypesToCatch); 1487 1488 // Outer function will catch odd type infos 1489 unsigned outerExceptionTypesToCatch[] = {3, 1, 5}; 1490 numExceptionTypesToCatch = sizeof(outerExceptionTypesToCatch) / 1491 sizeof(unsigned); 1492 1493 // Generate outer function 1494 llvm::Function *outerCatchFunct = createCatchWrappedInvokeFunction(module, 1495 builder, 1496 fpm, 1497 *innerCatchFunct, 1498 "outerCatchFunct", 1499 numExceptionTypesToCatch, 1500 outerExceptionTypesToCatch); 1501 1502 // Return outer function to run 1503 return(outerCatchFunct); 1504 } 1505 1506 namespace { 1507 /// Represents our foreign exceptions 1508 class OurCppRunException : public std::runtime_error { 1509 public: 1510 OurCppRunException(const std::string reason) : 1511 std::runtime_error(reason) {} 1512 1513 OurCppRunException (const OurCppRunException &toCopy) : 1514 std::runtime_error(toCopy) {} 1515 1516 OurCppRunException &operator = (const OurCppRunException &toCopy) { 1517 return(reinterpret_cast<OurCppRunException&>( 1518 std::runtime_error::operator=(toCopy))); 1519 } 1520 1521 ~OurCppRunException(void) throw() override {} 1522 }; 1523 } // end anonymous namespace 1524 1525 /// Throws foreign C++ exception. 1526 /// @param ignoreIt unused parameter that allows function to match implied 1527 /// generated function contract. 1528 extern "C" 1529 void throwCppException (int32_t ignoreIt) { 1530 throw(OurCppRunException("thrown by throwCppException(...)")); 1531 } 1532 1533 typedef void (*OurExceptionThrowFunctType) (int32_t typeToThrow); 1534 1535 /// This is a test harness which runs test by executing generated 1536 /// function with a type info type to throw. Harness wraps the execution 1537 /// of generated function in a C++ try catch clause. 1538 /// @param engine execution engine to use for executing generated function. 1539 /// This demo program expects this to be a JIT instance for demo 1540 /// purposes. 1541 /// @param function generated test function to run 1542 /// @param typeToThrow type info type of generated exception to throw, or 1543 /// indicator to cause foreign exception to be thrown. 1544 static void runExceptionThrow(llvm::orc::LLJIT *JIT, std::string function, 1545 int32_t typeToThrow) { 1546 1547 // Find test's function pointer 1548 OurExceptionThrowFunctType functPtr = 1549 reinterpret_cast<OurExceptionThrowFunctType>(reinterpret_cast<uintptr_t>( 1550 ExitOnErr(JIT->lookup(function)).getValue())); 1551 1552 try { 1553 // Run test 1554 (*functPtr)(typeToThrow); 1555 } 1556 catch (OurCppRunException exc) { 1557 // Catch foreign C++ exception 1558 fprintf(stderr, 1559 "\nrunExceptionThrow(...):In C++ catch OurCppRunException " 1560 "with reason: %s.\n", 1561 exc.what()); 1562 } 1563 catch (...) { 1564 // Catch all exceptions including our generated ones. This latter 1565 // functionality works according to the example in rules 1.6.4 of 1566 // http://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html (v1.22), 1567 // given that these will be exceptions foreign to C++ 1568 // (the _Unwind_Exception::exception_class should be different from 1569 // the one used by C++). 1570 fprintf(stderr, 1571 "\nrunExceptionThrow(...):In C++ catch all.\n"); 1572 } 1573 } 1574 1575 // 1576 // End test functions 1577 // 1578 1579 /// This initialization routine creates type info globals and 1580 /// adds external function declarations to module. 1581 /// @param numTypeInfos number of linear type info associated type info types 1582 /// to create as GlobalVariable instances, starting with the value 1. 1583 /// @param module code for module instance 1584 /// @param builder builder instance 1585 static void createStandardUtilityFunctions(unsigned numTypeInfos, 1586 llvm::Module &module, 1587 llvm::IRBuilder<> &builder) { 1588 1589 llvm::LLVMContext &context = module.getContext(); 1590 1591 // Exception initializations 1592 1593 // Setup exception catch state 1594 ourExceptionNotThrownState = 1595 llvm::ConstantInt::get(llvm::Type::getInt8Ty(context), 0), 1596 ourExceptionThrownState = 1597 llvm::ConstantInt::get(llvm::Type::getInt8Ty(context), 1), 1598 ourExceptionCaughtState = 1599 llvm::ConstantInt::get(llvm::Type::getInt8Ty(context), 2), 1600 1601 1602 1603 // Create our type info type 1604 ourTypeInfoType = llvm::StructType::get(context, 1605 TypeArray(builder.getInt32Ty())); 1606 1607 llvm::Type *caughtResultFieldTypes[] = { 1608 builder.getPtrTy(), 1609 builder.getInt32Ty() 1610 }; 1611 1612 // Create our landingpad result type 1613 ourCaughtResultType = llvm::StructType::get(context, 1614 TypeArray(caughtResultFieldTypes)); 1615 1616 // Create OurException type 1617 ourExceptionType = llvm::StructType::get(context, 1618 TypeArray(ourTypeInfoType)); 1619 1620 // Create portion of _Unwind_Exception type 1621 // 1622 // Note: Declaring only a portion of the _Unwind_Exception struct. 1623 // Does this cause problems? 1624 ourUnwindExceptionType = 1625 llvm::StructType::get(context, 1626 TypeArray(builder.getInt64Ty())); 1627 1628 struct OurBaseException_t dummyException; 1629 1630 // Calculate offset of OurException::unwindException member. 1631 ourBaseFromUnwindOffset = ((uintptr_t) &dummyException) - 1632 ((uintptr_t) &(dummyException.unwindException)); 1633 1634 #ifdef DEBUG 1635 fprintf(stderr, 1636 "createStandardUtilityFunctions(...):ourBaseFromUnwindOffset " 1637 "= %" PRIi64 ", sizeof(struct OurBaseException_t) - " 1638 "sizeof(struct _Unwind_Exception) = %lu.\n", 1639 ourBaseFromUnwindOffset, 1640 sizeof(struct OurBaseException_t) - 1641 sizeof(struct _Unwind_Exception)); 1642 #endif 1643 1644 size_t numChars = sizeof(ourBaseExcpClassChars) / sizeof(char); 1645 1646 // Create our _Unwind_Exception::exception_class value 1647 ourBaseExceptionClass = genClass(ourBaseExcpClassChars, numChars); 1648 1649 // Type infos 1650 1651 std::string baseStr = "typeInfo", typeInfoName; 1652 std::ostringstream typeInfoNameBuilder; 1653 std::vector<llvm::Constant*> structVals; 1654 1655 llvm::Constant *nextStruct; 1656 1657 // Generate each type info 1658 // 1659 // Note: First type info is not used. 1660 for (unsigned i = 0; i <= numTypeInfos; ++i) { 1661 structVals.clear(); 1662 structVals.push_back(llvm::ConstantInt::get(builder.getInt32Ty(), i)); 1663 nextStruct = llvm::ConstantStruct::get(ourTypeInfoType, structVals); 1664 1665 typeInfoNameBuilder.str(""); 1666 typeInfoNameBuilder << baseStr << i; 1667 typeInfoName = typeInfoNameBuilder.str(); 1668 1669 // Note: Does not seem to work without allocation 1670 new llvm::GlobalVariable(module, 1671 ourTypeInfoType, 1672 true, 1673 llvm::GlobalValue::ExternalLinkage, 1674 nextStruct, 1675 typeInfoName); 1676 1677 ourTypeInfoNames.push_back(typeInfoName); 1678 ourTypeInfoNamesIndex[i] = typeInfoName; 1679 } 1680 1681 ArgNames argNames; 1682 ArgTypes argTypes; 1683 llvm::Function *funct = NULL; 1684 1685 // print32Int 1686 1687 llvm::Type *retType = builder.getVoidTy(); 1688 1689 argTypes.clear(); 1690 argTypes.push_back(builder.getInt32Ty()); 1691 argTypes.push_back(builder.getPtrTy()); 1692 1693 argNames.clear(); 1694 1695 createFunction(module, 1696 retType, 1697 argTypes, 1698 argNames, 1699 "print32Int", 1700 llvm::Function::ExternalLinkage, 1701 true, 1702 false); 1703 1704 // print64Int 1705 1706 retType = builder.getVoidTy(); 1707 1708 argTypes.clear(); 1709 argTypes.push_back(builder.getInt64Ty()); 1710 argTypes.push_back(builder.getPtrTy()); 1711 1712 argNames.clear(); 1713 1714 createFunction(module, 1715 retType, 1716 argTypes, 1717 argNames, 1718 "print64Int", 1719 llvm::Function::ExternalLinkage, 1720 true, 1721 false); 1722 1723 // printStr 1724 1725 retType = builder.getVoidTy(); 1726 1727 argTypes.clear(); 1728 argTypes.push_back(builder.getPtrTy()); 1729 1730 argNames.clear(); 1731 1732 createFunction(module, 1733 retType, 1734 argTypes, 1735 argNames, 1736 "printStr", 1737 llvm::Function::ExternalLinkage, 1738 true, 1739 false); 1740 1741 // throwCppException 1742 1743 retType = builder.getVoidTy(); 1744 1745 argTypes.clear(); 1746 argTypes.push_back(builder.getInt32Ty()); 1747 1748 argNames.clear(); 1749 1750 createFunction(module, 1751 retType, 1752 argTypes, 1753 argNames, 1754 "throwCppException", 1755 llvm::Function::ExternalLinkage, 1756 true, 1757 false); 1758 1759 // deleteOurException 1760 1761 retType = builder.getVoidTy(); 1762 1763 argTypes.clear(); 1764 argTypes.push_back(builder.getPtrTy()); 1765 1766 argNames.clear(); 1767 1768 createFunction(module, 1769 retType, 1770 argTypes, 1771 argNames, 1772 "deleteOurException", 1773 llvm::Function::ExternalLinkage, 1774 true, 1775 false); 1776 1777 // createOurException 1778 1779 retType = builder.getPtrTy(); 1780 1781 argTypes.clear(); 1782 argTypes.push_back(builder.getInt32Ty()); 1783 1784 argNames.clear(); 1785 1786 createFunction(module, 1787 retType, 1788 argTypes, 1789 argNames, 1790 "createOurException", 1791 llvm::Function::ExternalLinkage, 1792 true, 1793 false); 1794 1795 // _Unwind_RaiseException 1796 1797 retType = builder.getInt32Ty(); 1798 1799 argTypes.clear(); 1800 argTypes.push_back(builder.getPtrTy()); 1801 1802 argNames.clear(); 1803 1804 funct = createFunction(module, 1805 retType, 1806 argTypes, 1807 argNames, 1808 "_Unwind_RaiseException", 1809 llvm::Function::ExternalLinkage, 1810 true, 1811 false); 1812 1813 funct->setDoesNotReturn(); 1814 1815 // _Unwind_Resume 1816 1817 retType = builder.getInt32Ty(); 1818 1819 argTypes.clear(); 1820 argTypes.push_back(builder.getPtrTy()); 1821 1822 argNames.clear(); 1823 1824 funct = createFunction(module, 1825 retType, 1826 argTypes, 1827 argNames, 1828 "_Unwind_Resume", 1829 llvm::Function::ExternalLinkage, 1830 true, 1831 false); 1832 1833 funct->setDoesNotReturn(); 1834 1835 // ourPersonality 1836 1837 retType = builder.getInt32Ty(); 1838 1839 argTypes.clear(); 1840 argTypes.push_back(builder.getInt32Ty()); 1841 argTypes.push_back(builder.getInt32Ty()); 1842 argTypes.push_back(builder.getInt64Ty()); 1843 argTypes.push_back(builder.getPtrTy()); 1844 argTypes.push_back(builder.getPtrTy()); 1845 1846 argNames.clear(); 1847 1848 createFunction(module, 1849 retType, 1850 argTypes, 1851 argNames, 1852 "ourPersonality", 1853 llvm::Function::ExternalLinkage, 1854 true, 1855 false); 1856 1857 // llvm.eh.typeid.for intrinsic 1858 1859 getDeclaration(&module, llvm::Intrinsic::eh_typeid_for); 1860 } 1861 1862 1863 //===----------------------------------------------------------------------===// 1864 // Main test driver code. 1865 //===----------------------------------------------------------------------===// 1866 1867 /// Demo main routine which takes the type info types to throw. A test will 1868 /// be run for each given type info type. While type info types with the value 1869 /// of -1 will trigger a foreign C++ exception to be thrown; type info types 1870 /// <= 6 and >= 1 will be caught by test functions; and type info types > 6 1871 /// will result in exceptions which pass through to the test harness. All other 1872 /// type info types are not supported and could cause a crash. 1873 int main(int argc, char *argv[]) { 1874 if (argc == 1) { 1875 fprintf(stderr, 1876 "\nUsage: ExceptionDemo <exception type to throw> " 1877 "[<type 2>...<type n>].\n" 1878 " Each type must have the value of 1 - 6 for " 1879 "generated exceptions to be caught;\n" 1880 " the value -1 for foreign C++ exceptions to be " 1881 "generated and thrown;\n" 1882 " or the values > 6 for exceptions to be ignored.\n" 1883 "\nTry: ExceptionDemo 2 3 7 -1\n" 1884 " for a full test.\n\n"); 1885 return(0); 1886 } 1887 1888 llvm::InitializeNativeTarget(); 1889 llvm::InitializeNativeTargetAsmPrinter(); 1890 auto Context = std::make_unique<llvm::LLVMContext>(); 1891 llvm::IRBuilder<> theBuilder(*Context); 1892 1893 // Make the module, which holds all the code. 1894 std::unique_ptr<llvm::Module> Owner = 1895 std::make_unique<llvm::Module>("my cool jit", *Context); 1896 llvm::Module *module = Owner.get(); 1897 1898 // Build LLJIT 1899 std::unique_ptr<llvm::orc::LLJIT> JIT = 1900 ExitOnErr(llvm::orc::LLJITBuilder().create()); 1901 1902 // Set up the optimizer pipeline. 1903 llvm::legacy::FunctionPassManager fpm(module); 1904 1905 // Optimizations turned on 1906 #ifdef ADD_OPT_PASSES 1907 1908 // Basic AliasAnslysis support for GVN. 1909 fpm.add(llvm::createBasicAliasAnalysisPass()); 1910 1911 // Promote allocas to registers. 1912 fpm.add(llvm::createPromoteMemoryToRegisterPass()); 1913 1914 // Do simple "peephole" optimizations and bit-twiddling optzns. 1915 fpm.add(llvm::createInstructionCombiningPass()); 1916 1917 // Reassociate expressions. 1918 fpm.add(llvm::createReassociatePass()); 1919 1920 // Eliminate Common SubExpressions. 1921 fpm.add(llvm::createGVNPass()); 1922 1923 // Simplify the control flow graph (deleting unreachable 1924 // blocks, etc). 1925 fpm.add(llvm::createCFGSimplificationPass()); 1926 #endif // ADD_OPT_PASSES 1927 1928 fpm.doInitialization(); 1929 1930 // Generate test code using function throwCppException(...) as 1931 // the function which throws foreign exceptions. 1932 createUnwindExceptionTest(*module, theBuilder, fpm, "throwCppException"); 1933 1934 ExitOnErr(JIT->addIRModule( 1935 llvm::orc::ThreadSafeModule(std::move(Owner), std::move(Context)))); 1936 1937 #ifndef NDEBUG 1938 fprintf(stderr, "\nBegin module dump:\n\n"); 1939 1940 module->print(llvm::errs(), nullptr); 1941 1942 fprintf(stderr, "\nEnd module dump:\n"); 1943 #endif 1944 1945 fprintf(stderr, "\n\nBegin Test:\n"); 1946 std::string toRun = "outerCatchFunct"; 1947 1948 for (int i = 1; i < argc; ++i) { 1949 // Run test for each argument whose value is the exception 1950 // type to throw. 1951 runExceptionThrow(JIT.get(), toRun, (unsigned)strtoul(argv[i], NULL, 10)); 1952 } 1953 1954 fprintf(stderr, "\nEnd Test:\n\n"); 1955 1956 return 0; 1957 } 1958