xref: /llvm-project/flang/runtime/terminator.h (revision 208544fc70d2cfd5b2c13232a267048108da1978)
1 //===-- runtime/terminator.h ------------------------------------*- C++ -*-===//
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 // Termination of the image
10 
11 #ifndef FORTRAN_RUNTIME_TERMINATOR_H_
12 #define FORTRAN_RUNTIME_TERMINATOR_H_
13 
14 #include "flang/Common/api-attrs.h"
15 #include <cstdarg>
16 #include <cstdio>
17 #include <cstdlib>
18 
19 namespace Fortran::runtime {
20 
21 // A mixin class for statement-specific image error termination
22 // for errors detected in the runtime library
23 class Terminator {
24 public:
Terminator()25   RT_API_ATTRS Terminator() {}
26   Terminator(const Terminator &) = default;
27   explicit RT_API_ATTRS Terminator(
28       const char *sourceFileName, int sourceLine = 0)
29       : sourceFileName_{sourceFileName}, sourceLine_{sourceLine} {}
30 
sourceFileName()31   RT_API_ATTRS const char *sourceFileName() const { return sourceFileName_; }
sourceLine()32   RT_API_ATTRS int sourceLine() const { return sourceLine_; }
33 
34   RT_API_ATTRS void SetLocation(
35       const char *sourceFileName = nullptr, int sourceLine = 0) {
36     sourceFileName_ = sourceFileName;
37     sourceLine_ = sourceLine;
38   }
39 
40   // Silence compiler warnings about the format string being
41   // non-literal. A more precise control would be
42   // __attribute__((format_arg(2))), but it requires the function
43   // to return 'char *', which does not work well with noreturn.
44 #if defined(__clang__)
45 #pragma clang diagnostic push
46 #pragma clang diagnostic ignored "-Wformat-security"
47 #elif defined(__GNUC__)
48 #pragma GCC diagnostic push
49 #pragma GCC diagnostic ignored "-Wformat-security"
50 #endif
51 
52   // Device offload compilers do not normally support varargs and va_list,
53   // so use C++ variadic templates to forward the crash arguments
54   // to regular printf for the device compilation.
55   // Try to keep the inline implementations as small as possible.
56   template <typename... Args>
Crash(const char * message,Args...args)57   [[noreturn]] RT_DEVICE_NOINLINE RT_API_ATTRS const char *Crash(
58       const char *message, Args... args) const {
59 #if !defined(RT_DEVICE_COMPILATION)
60     // Invoke handler set up by the test harness.
61     InvokeCrashHandler(message, args...);
62 #endif
63     CrashHeader();
64     PrintCrashArgs(message, args...);
65     CrashFooter();
66   }
67 
68   template <typename... Args>
PrintCrashArgs(const char * message,Args...args)69   RT_API_ATTRS void PrintCrashArgs(const char *message, Args... args) const {
70 #if defined(RT_DEVICE_COMPILATION)
71     std::printf(message, args...);
72 #else
73     std::fprintf(stderr, message, args...);
74 #endif
75   }
76 
77 #if defined(__clang__)
78 #pragma clang diagnostic pop
79 #elif defined(__GNUC__)
80 #pragma GCC diagnostic pop
81 #endif
82 
83   RT_API_ATTRS void CrashHeader() const;
84   [[noreturn]] RT_API_ATTRS void CrashFooter() const;
85 #if !defined(RT_DEVICE_COMPILATION)
86   void InvokeCrashHandler(const char *message, ...) const;
87   [[noreturn]] void CrashArgs(const char *message, va_list &) const;
88 #endif
89   [[noreturn]] RT_API_ATTRS void CheckFailed(
90       const char *predicate, const char *file, int line) const;
91   [[noreturn]] RT_API_ATTRS void CheckFailed(const char *predicate) const;
92 
93   // For test harnessing - overrides CrashArgs().
94   static void RegisterCrashHandler(void (*)(const char *sourceFile,
95       int sourceLine, const char *message, va_list &ap));
96 
97 private:
98   const char *sourceFileName_{nullptr};
99   int sourceLine_{0};
100 };
101 
102 // RUNTIME_CHECK() guarantees evaluation of its predicate.
103 #define RUNTIME_CHECK(terminator, pred) \
104   if (pred) \
105     ; \
106   else \
107     (terminator).CheckFailed(#pred, __FILE__, __LINE__)
108 
109 #define INTERNAL_CHECK(pred) \
110   if (pred) \
111     ; \
112   else \
113     Terminator{__FILE__, __LINE__}.CheckFailed(#pred)
114 
115 RT_API_ATTRS void NotifyOtherImagesOfNormalEnd();
116 RT_API_ATTRS void NotifyOtherImagesOfFailImageStatement();
117 RT_API_ATTRS void NotifyOtherImagesOfErrorTermination();
118 } // namespace Fortran::runtime
119 
120 namespace Fortran::runtime::io {
121 RT_API_ATTRS void FlushOutputOnCrash(const Terminator &);
122 }
123 
124 #endif // FORTRAN_RUNTIME_TERMINATOR_H_
125