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

source code of llvm/examples/ExceptionDemo/ExceptionDemo.cpp