1 | //===-- ExceptionDemo.cpp - An example using llvm Exceptions --------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // Demo program which implements an example LLVM exception implementation, and |
10 | // shows several test cases including the handling of foreign exceptions. |
11 | // It is run with type info types arguments to throw. A test will |
12 | // be run for each given type info type. While type info types with the value |
13 | // of -1 will trigger a foreign C++ exception to be thrown; type info types |
14 | // <= 6 and >= 1 will cause the associated generated exceptions to be thrown |
15 | // and caught by generated test functions; and type info types > 6 |
16 | // will result in exceptions which pass through to the test harness. All other |
17 | // type info types are not supported and could cause a crash. In all cases, |
18 | // the "finally" blocks of every generated test functions will executed |
19 | // regardless of whether or not that test function ignores or catches the |
20 | // thrown exception. |
21 | // |
22 | // examples: |
23 | // |
24 | // ExceptionDemo |
25 | // |
26 | // causes a usage to be printed to stderr |
27 | // |
28 | // ExceptionDemo 2 3 7 -1 |
29 | // |
30 | // results in the following cases: |
31 | // - Value 2 causes an exception with a type info type of 2 to be |
32 | // thrown and caught by an inner generated test function. |
33 | // - Value 3 causes an exception with a type info type of 3 to be |
34 | // thrown and caught by an outer generated test function. |
35 | // - Value 7 causes an exception with a type info type of 7 to be |
36 | // thrown and NOT be caught by any generated function. |
37 | // - Value -1 causes a foreign C++ exception to be thrown and not be |
38 | // caught by any generated function |
39 | // |
40 | // Cases -1 and 7 are caught by a C++ test harness where the validity of |
41 | // of a C++ catch(...) clause catching a generated exception with a |
42 | // type info type of 7 is explained by: example in rules 1.6.4 in |
43 | // http://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html (v1.22) |
44 | // |
45 | // This code uses code from the llvm compiler-rt project and the llvm |
46 | // Kaleidoscope project. |
47 | // |
48 | //===----------------------------------------------------------------------===// |
49 | |
50 | #include "llvm/ADT/STLExtras.h" |
51 | #include "llvm/BinaryFormat/Dwarf.h" |
52 | #include "llvm/ExecutionEngine/MCJIT.h" |
53 | #include "llvm/ExecutionEngine/SectionMemoryManager.h" |
54 | #include "llvm/IR/DataLayout.h" |
55 | #include "llvm/IR/DerivedTypes.h" |
56 | #include "llvm/IR/IRBuilder.h" |
57 | #include "llvm/IR/Intrinsics.h" |
58 | #include "llvm/IR/LLVMContext.h" |
59 | #include "llvm/IR/LegacyPassManager.h" |
60 | #include "llvm/IR/Module.h" |
61 | #include "llvm/IR/Verifier.h" |
62 | #include "llvm/Support/TargetSelect.h" |
63 | #include "llvm/Target/TargetOptions.h" |
64 | #include "llvm/Transforms/Scalar.h" |
65 | |
66 | // FIXME: Although all systems tested with (Linux, OS X), do not need this |
67 | // header file included. A user on ubuntu reported, undefined symbols |
68 | // for stderr, and fprintf, and the addition of this include fixed the |
69 | // issue for them. Given that LLVM's best practices include the goal |
70 | // of reducing the number of redundant header files included, the |
71 | // correct solution would be to find out why these symbols are not |
72 | // defined for the system in question, and fix the issue by finding out |
73 | // which LLVM header file, if any, would include these symbols. |
74 | #include <cstdio> |
75 | |
76 | #include <sstream> |
77 | #include <stdexcept> |
78 | |
79 | #include <inttypes.h> |
80 | |
81 | #include <unwind.h> |
82 | |
83 | #ifndef USE_GLOBAL_STR_CONSTS |
84 | #define USE_GLOBAL_STR_CONSTS true |
85 | #endif |
86 | |
87 | // |
88 | // Example types |
89 | // |
90 | |
91 | /// This is our simplistic type info |
92 | struct OurExceptionType_t { |
93 | /// type info type |
94 | int type; |
95 | }; |
96 | |
97 | |
98 | /// This is our Exception class which relies on a negative offset to calculate |
99 | /// pointers to its instances from pointers to its unwindException member. |
100 | /// |
101 | /// Note: The above unwind.h defines struct _Unwind_Exception to be aligned |
102 | /// on a double word boundary. This is necessary to match the standard: |
103 | /// http://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html |
104 | struct OurBaseException_t { |
105 | struct OurExceptionType_t type; |
106 | |
107 | // Note: This is properly aligned in unwind.h |
108 | struct _Unwind_Exception unwindException; |
109 | }; |
110 | |
111 | |
112 | // Note: Not needed since we are C++ |
113 | typedef struct OurBaseException_t OurException; |
114 | typedef struct _Unwind_Exception OurUnwindException; |
115 | |
116 | // |
117 | // Various globals used to support typeinfo and generatted exceptions in |
118 | // general |
119 | // |
120 | |
121 | static std::map<std::string, llvm::Value*> namedValues; |
122 | |
123 | int64_t ourBaseFromUnwindOffset; |
124 | |
125 | const unsigned char ourBaseExcpClassChars[] = |
126 | {'o', 'b', 'j', '\0', 'b', 'a', 's', '\0'}; |
127 | |
128 | |
129 | static uint64_t ourBaseExceptionClass = 0; |
130 | |
131 | static std::vector<std::string> ourTypeInfoNames; |
132 | static std::map<int, std::string> ourTypeInfoNamesIndex; |
133 | |
134 | static llvm::StructType *ourTypeInfoType; |
135 | static llvm::StructType *ourCaughtResultType; |
136 | static llvm::StructType *ourExceptionType; |
137 | static llvm::StructType *ourUnwindExceptionType; |
138 | |
139 | static llvm::ConstantInt *ourExceptionNotThrownState; |
140 | static llvm::ConstantInt *ourExceptionThrownState; |
141 | static llvm::ConstantInt *ourExceptionCaughtState; |
142 | |
143 | typedef std::vector<std::string> ArgNames; |
144 | typedef std::vector<llvm::Type*> ArgTypes; |
145 | |
146 | // |
147 | // Code Generation Utilities |
148 | // |
149 | |
150 | /// Utility used to create a function, both declarations and definitions |
151 | /// @param module for module instance |
152 | /// @param retType function return type |
153 | /// @param theArgTypes function's ordered argument types |
154 | /// @param theArgNames function's ordered arguments needed if use of this |
155 | /// function corresponds to a function definition. Use empty |
156 | /// aggregate for function declarations. |
157 | /// @param functName function name |
158 | /// @param linkage function linkage |
159 | /// @param declarationOnly for function declarations |
160 | /// @param isVarArg function uses vararg arguments |
161 | /// @returns function instance |
162 | llvm::Function *createFunction(llvm::Module &module, |
163 | llvm::Type *retType, |
164 | const ArgTypes &theArgTypes, |
165 | const ArgNames &theArgNames, |
166 | const std::string &functName, |
167 | llvm::GlobalValue::LinkageTypes linkage, |
168 | bool declarationOnly, |
169 | bool isVarArg) { |
170 | llvm::FunctionType *functType = |
171 | llvm::FunctionType::get(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 |
198 | static llvm::AllocaInst *createEntryBlockAlloca(llvm::Function &function, |
199 | const std::string &varName, |
200 | llvm::Type *type, |
201 | llvm::Constant *initWith = 0) { |
202 | llvm::BasicBlock &block = function.getEntryBlock(); |
203 | llvm::IRBuilder<> tmp(&block, block.begin()); |
204 | llvm::AllocaInst *ret = tmp.CreateAlloca(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 | |
221 | namespace { |
222 | template <typename Type_> |
223 | uintptr_t ReadType(const uint8_t *&p) { |
224 | Type_ value; |
225 | memcpy(&value, p, sizeof(Type_)); |
226 | p += sizeof(Type_); |
227 | return static_cast<uintptr_t>(value); |
228 | } |
229 | } |
230 | |
231 | // Note: using an extern "C" block so that static functions can be used |
232 | extern "C" { |
233 | |
234 | // Note: Better ways to decide on bit width |
235 | // |
236 | /// Prints a 32 bit number, according to the format, to stderr. |
237 | /// @param intToPrint integer to print |
238 | /// @param format printf like format to use when printing |
239 | void print32Int(int intToPrint, const char *format) { |
240 | if (format) { |
241 | // Note: No NULL check |
242 | fprintf(stderr, format: 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 |
256 | void 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 |
270 | void 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 |
284 | void deleteOurException(OurUnwindException *expToDelete) { |
285 | #ifdef DEBUG |
286 | fprintf(stderr, |
287 | "deleteOurException(...).\n" ); |
288 | #endif |
289 | |
290 | if (expToDelete && |
291 | (expToDelete->exception_class == ourBaseExceptionClass)) { |
292 | |
293 | free(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 |
304 | void deleteFromUnwindOurException(_Unwind_Reason_Code reason, |
305 | OurUnwindException *expToDelete) { |
306 | #ifdef DEBUG |
307 | fprintf(stderr, |
308 | "deleteFromUnwindOurException(...).\n" ); |
309 | #endif |
310 | |
311 | deleteOurException(expToDelete); |
312 | } |
313 | |
314 | |
315 | /// Creates (allocates on the heap), an exception (OurException instance), |
316 | /// of the supplied type info type. |
317 | /// @param type type info type |
318 | OurUnwindException *createOurException(int type) { |
319 | size_t size = sizeof(OurException); |
320 | OurException *ret = (OurException*) memset(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 |
334 | static uintptr_t readULEB128(const uint8_t **data) { |
335 | uintptr_t result = 0; |
336 | uintptr_t shift = 0; |
337 | unsigned char byte; |
338 | const uint8_t *p = *data; |
339 | |
340 | do { |
341 | byte = *p++; |
342 | result |= (byte & 0x7f) << shift; |
343 | shift += 7; |
344 | } |
345 | while (byte & 0x80); |
346 | |
347 | *data = p; |
348 | |
349 | return result; |
350 | } |
351 | |
352 | |
353 | /// Read a sleb128 encoded value and advance pointer |
354 | /// See Variable Length Data in: |
355 | /// @link http://dwarfstd.org/Dwarf3.pdf @unlink |
356 | /// @param data reference variable holding memory pointer to decode from |
357 | /// @returns decoded value |
358 | static uintptr_t readSLEB128(const uint8_t **data) { |
359 | uintptr_t result = 0; |
360 | uintptr_t shift = 0; |
361 | unsigned char byte; |
362 | const uint8_t *p = *data; |
363 | |
364 | do { |
365 | byte = *p++; |
366 | result |= (byte & 0x7f) << shift; |
367 | shift += 7; |
368 | } |
369 | while (byte & 0x80); |
370 | |
371 | *data = p; |
372 | |
373 | if ((byte & 0x40) && (shift < (sizeof(result) << 3))) { |
374 | result |= (~0 << shift); |
375 | } |
376 | |
377 | return result; |
378 | } |
379 | |
380 | unsigned getEncodingSize(uint8_t Encoding) { |
381 | if (Encoding == llvm::dwarf::DW_EH_PE_omit) |
382 | return 0; |
383 | |
384 | switch (Encoding & 0x0F) { |
385 | case llvm::dwarf::DW_EH_PE_absptr: |
386 | return sizeof(uintptr_t); |
387 | case llvm::dwarf::DW_EH_PE_udata2: |
388 | return sizeof(uint16_t); |
389 | case llvm::dwarf::DW_EH_PE_udata4: |
390 | return sizeof(uint32_t); |
391 | case llvm::dwarf::DW_EH_PE_udata8: |
392 | return sizeof(uint64_t); |
393 | case llvm::dwarf::DW_EH_PE_sdata2: |
394 | return sizeof(int16_t); |
395 | case llvm::dwarf::DW_EH_PE_sdata4: |
396 | return sizeof(int32_t); |
397 | case llvm::dwarf::DW_EH_PE_sdata8: |
398 | return sizeof(int64_t); |
399 | default: |
400 | // not supported |
401 | abort(); |
402 | } |
403 | } |
404 | |
405 | /// Read a pointer encoded value and advance pointer |
406 | /// See Variable Length Data in: |
407 | /// @link http://dwarfstd.org/Dwarf3.pdf @unlink |
408 | /// @param data reference variable holding memory pointer to decode from |
409 | /// @param encoding dwarf encoding type |
410 | /// @returns decoded value |
411 | static uintptr_t readEncodedPointer(const uint8_t **data, uint8_t encoding) { |
412 | uintptr_t result = 0; |
413 | const uint8_t *p = *data; |
414 | |
415 | if (encoding == llvm::dwarf::DW_EH_PE_omit) |
416 | return(result); |
417 | |
418 | // first get value |
419 | switch (encoding & 0x0F) { |
420 | case llvm::dwarf::DW_EH_PE_absptr: |
421 | result = ReadType<uintptr_t>(p); |
422 | break; |
423 | case llvm::dwarf::DW_EH_PE_uleb128: |
424 | result = readULEB128(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 |
501 | static bool handleActionValue(int64_t *resultAction, |
502 | uint8_t TTypeEncoding, |
503 | const uint8_t *ClassInfo, |
504 | uintptr_t actionEntry, |
505 | uint64_t exceptionClass, |
506 | struct _Unwind_Exception *exceptionObject) { |
507 | bool ret = false; |
508 | |
509 | if (!resultAction || |
510 | !exceptionObject || |
511 | (exceptionClass != ourBaseExceptionClass)) |
512 | return(ret); |
513 | |
514 | struct OurBaseException_t *excp = (struct OurBaseException_t*) |
515 | (((char*) exceptionObject) + ourBaseFromUnwindOffset); |
516 | struct OurExceptionType_t *excpType = &(excp->type); |
517 | int type = excpType->type; |
518 | |
519 | #ifdef DEBUG |
520 | fprintf(stderr, |
521 | "handleActionValue(...): exceptionObject = <%p>, " |
522 | "excp = <%p>.\n" , |
523 | (void*)exceptionObject, |
524 | (void*)excp); |
525 | #endif |
526 | |
527 | const uint8_t *actionPos = (uint8_t*) actionEntry, |
528 | *tempActionPos; |
529 | int64_t typeOffset = 0, |
530 | actionOffset; |
531 | |
532 | for (int i = 0; true; ++i) { |
533 | // Each emitted dwarf action corresponds to a 2 tuple of |
534 | // type info address offset, and action offset to the next |
535 | // emitted action. |
536 | typeOffset = readSLEB128(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 |
595 | static _Unwind_Reason_Code handleLsda(int version, const uint8_t *lsda, |
596 | _Unwind_Action actions, |
597 | _Unwind_Exception_Class exceptionClass, |
598 | struct _Unwind_Exception *exceptionObject, |
599 | struct _Unwind_Context *context) { |
600 | _Unwind_Reason_Code ret = _URC_CONTINUE_UNWIND; |
601 | |
602 | if (!lsda) |
603 | return(ret); |
604 | |
605 | #ifdef DEBUG |
606 | fprintf(stderr, |
607 | "handleLsda(...):lsda is non-zero.\n" ); |
608 | #endif |
609 | |
610 | // Get the current instruction pointer and offset it before next |
611 | // instruction in the current frame which threw the exception. |
612 | uintptr_t pc = _Unwind_GetIP(context)-1; |
613 | |
614 | // Get beginning current frame's code (as defined by the |
615 | // emitted dwarf code) |
616 | uintptr_t funcStart = _Unwind_GetRegionStart(context); |
617 | uintptr_t pcOffset = pc - funcStart; |
618 | const uint8_t *ClassInfo = NULL; |
619 | |
620 | // Note: See JITDwarfEmitter::EmitExceptionTable(...) for corresponding |
621 | // dwarf emission |
622 | |
623 | // Parse LSDA header. |
624 | uint8_t lpStartEncoding = *lsda++; |
625 | |
626 | if (lpStartEncoding != llvm::dwarf::DW_EH_PE_omit) { |
627 | readEncodedPointer(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 |
820 | uint64_t genClass(const unsigned char classChars[], size_t ) |
821 | { |
822 | uint64_t ret = classChars[0]; |
823 | |
824 | for (unsigned i = 1; i < classCharsSize; ++i) { |
825 | ret <<= 8; |
826 | ret += classChars[i]; |
827 | } |
828 | |
829 | return(ret); |
830 | } |
831 | |
832 | } // extern "C" |
833 | |
834 | // |
835 | // Runtime C Library functions End |
836 | // |
837 | |
838 | // |
839 | // Code generation functions |
840 | // |
841 | |
842 | /// Generates code to print given constant string |
843 | /// @param context llvm context |
844 | /// @param module code for module instance |
845 | /// @param builder builder instance |
846 | /// @param toPrint string to print |
847 | /// @param useGlobal A value of true (default) indicates a GlobalValue is |
848 | /// generated, and is used to hold the constant string. A value of |
849 | /// false indicates that the constant string will be stored on the |
850 | /// stack. |
851 | void generateStringPrint(llvm::LLVMContext &context, |
852 | llvm::Module &module, |
853 | llvm::IRBuilder<> &builder, |
854 | std::string toPrint, |
855 | bool useGlobal = true) { |
856 | llvm::Function *printFunct = module.getFunction(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. |
895 | void generateIntegerPrint(llvm::LLVMContext &context, |
896 | llvm::Module &module, |
897 | llvm::IRBuilder<> &builder, |
898 | llvm::Function &printFunct, |
899 | llvm::Value &toPrint, |
900 | std::string format, |
901 | bool useGlobal = true) { |
902 | llvm::Constant *stringConstant = |
903 | llvm::ConstantDataArray::getString(Context&: 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 |
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(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 |
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&: 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 |
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(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 |
1340 | static llvm::Function * |
1341 | createThrowExceptionFunction(llvm::Module &module, llvm::IRBuilder<> &builder, |
1342 | llvm::legacy::FunctionPassManager &fpm, |
1343 | std::string ourId, int32_t nativeThrowType, |
1344 | llvm::Function &nativeThrowFunct) { |
1345 | llvm::LLVMContext &context = module.getContext(); |
1346 | namedValues.clear(); |
1347 | ArgTypes unwindArgTypes; |
1348 | unwindArgTypes.push_back(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 | |
1433 | static void createStandardUtilityFunctions(unsigned numTypeInfos, |
1434 | llvm::Module &module, |
1435 | llvm::IRBuilder<> &builder); |
1436 | |
1437 | /// Creates test code by generating and organizing these functions into the |
1438 | /// test case. The test case consists of an outer function setup to invoke |
1439 | /// an inner function within an environment having multiple catch and single |
1440 | /// finally blocks. This inner function is also setup to invoke a throw |
1441 | /// function within an evironment similar in nature to the outer function's |
1442 | /// catch and finally blocks. Each of these two functions catch mutually |
1443 | /// exclusive subsets (even or odd) of the type info types configured |
1444 | /// for this this. All generated functions have a runtime argument which |
1445 | /// holds a type info type to throw that each function takes and passes it |
1446 | /// to the inner one if such a inner function exists. This type info type is |
1447 | /// looked at by the generated throw function to see whether or not it should |
1448 | /// throw a generated exception with the same type info type, or instead call |
1449 | /// a supplied a function which in turn will throw a foreign exception. |
1450 | /// @param module code for module instance |
1451 | /// @param builder builder instance |
1452 | /// @param fpm a function pass manager holding optional IR to IR |
1453 | /// transformations |
1454 | /// @param nativeThrowFunctName name of external function which will throw |
1455 | /// a foreign exception |
1456 | /// @returns outermost generated test function. |
1457 | llvm::Function * |
1458 | createUnwindExceptionTest(llvm::Module &module, llvm::IRBuilder<> &builder, |
1459 | llvm::legacy::FunctionPassManager &fpm, |
1460 | std::string nativeThrowFunctName) { |
1461 | // Number of type infos to generate |
1462 | unsigned numTypeInfos = 6; |
1463 | |
1464 | // Initialze intrisics and external functions to use along with exception |
1465 | // and type info globals. |
1466 | createStandardUtilityFunctions(numTypeInfos, |
1467 | module, |
1468 | builder); |
1469 | llvm::Function *nativeThrowFunct = module.getFunction(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 | |
1511 | namespace { |
1512 | /// Represents our foreign exceptions |
1513 | class OurCppRunException : public std::runtime_error { |
1514 | public: |
1515 | OurCppRunException(const std::string reason) : |
1516 | std::runtime_error(reason) {} |
1517 | |
1518 | OurCppRunException (const OurCppRunException &toCopy) : |
1519 | std::runtime_error(toCopy) {} |
1520 | |
1521 | OurCppRunException &operator = (const OurCppRunException &toCopy) { |
1522 | return(reinterpret_cast<OurCppRunException&>( |
1523 | std::runtime_error::operator=(toCopy))); |
1524 | } |
1525 | |
1526 | ~OurCppRunException(void) throw() override {} |
1527 | }; |
1528 | } // end anonymous namespace |
1529 | |
1530 | /// Throws foreign C++ exception. |
1531 | /// @param ignoreIt unused parameter that allows function to match implied |
1532 | /// generated function contract. |
1533 | extern "C" |
1534 | void throwCppException (int32_t ignoreIt) { |
1535 | throw(OurCppRunException("thrown by throwCppException(...)" )); |
1536 | } |
1537 | |
1538 | typedef void (*OurExceptionThrowFunctType) (int32_t typeToThrow); |
1539 | |
1540 | /// This is a test harness which runs test by executing generated |
1541 | /// function with a type info type to throw. Harness wraps the execution |
1542 | /// of generated function in a C++ try catch clause. |
1543 | /// @param engine execution engine to use for executing generated function. |
1544 | /// This demo program expects this to be a JIT instance for demo |
1545 | /// purposes. |
1546 | /// @param function generated test function to run |
1547 | /// @param typeToThrow type info type of generated exception to throw, or |
1548 | /// indicator to cause foreign exception to be thrown. |
1549 | static |
1550 | void runExceptionThrow(llvm::ExecutionEngine *engine, |
1551 | llvm::Function *function, |
1552 | int32_t typeToThrow) { |
1553 | |
1554 | // Find test's function pointer |
1555 | OurExceptionThrowFunctType functPtr = |
1556 | reinterpret_cast<OurExceptionThrowFunctType>( |
1557 | reinterpret_cast<intptr_t>(engine->getPointerToFunction(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 | |
1586 | typedef llvm::ArrayRef<llvm::Type*> TypeArray; |
1587 | |
1588 | /// This initialization routine creates type info globals and |
1589 | /// adds external function declarations to module. |
1590 | /// @param numTypeInfos number of linear type info associated type info types |
1591 | /// to create as GlobalVariable instances, starting with the value 1. |
1592 | /// @param module code for module instance |
1593 | /// @param builder builder instance |
1594 | static void createStandardUtilityFunctions(unsigned numTypeInfos, |
1595 | llvm::Module &module, |
1596 | llvm::IRBuilder<> &builder) { |
1597 | |
1598 | llvm::LLVMContext &context = module.getContext(); |
1599 | |
1600 | // Exception initializations |
1601 | |
1602 | // Setup exception catch state |
1603 | ourExceptionNotThrownState = |
1604 | llvm::ConstantInt::get(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. |
1882 | int 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 | |