Merge pull request #11191 from BruceForstall/FixFuncletAssert
[platform/upstream/coreclr.git] / src / jit / error.h
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 /*****************************************************************************/
5
6 #ifndef _ERROR_H_
7 #define _ERROR_H_
8 /*****************************************************************************/
9
10 #include <corjit.h>   // for CORJIT_INTERNALERROR
11 #include <safemath.h> // For FitsIn, used by SafeCvt methods.
12
13 #define FATAL_JIT_EXCEPTION 0x02345678
14 class Compiler;
15
16 struct ErrorTrapParam
17 {
18     int                errc;
19     ICorJitInfo*       jitInfo;
20     EXCEPTION_POINTERS exceptionPointers;
21     ErrorTrapParam()
22     {
23         jitInfo = nullptr;
24     }
25 };
26
27 // Only catch JIT internal errors (will not catch EE generated Errors)
28 extern LONG __JITfilter(PEXCEPTION_POINTERS pExceptionPointers, LPVOID lpvParam);
29
30 #define setErrorTrap(compHnd, ParamType, paramDef, paramRef)                                                           \
31     struct __JITParam : ErrorTrapParam                                                                                 \
32     {                                                                                                                  \
33         ParamType param;                                                                                               \
34     } __JITparam;                                                                                                      \
35     __JITparam.errc    = CORJIT_INTERNALERROR;                                                                         \
36     __JITparam.jitInfo = compHnd;                                                                                      \
37     __JITparam.param   = paramRef;                                                                                     \
38     PAL_TRY(__JITParam*, __JITpParam, &__JITparam)                                                                     \
39     {                                                                                                                  \
40         ParamType paramDef = __JITpParam->param;
41
42 // Only catch JIT internal errors (will not catch EE generated Errors)
43 #define impJitErrorTrap()                                                                                              \
44     }                                                                                                                  \
45     PAL_EXCEPT_FILTER(__JITfilter)                                                                                     \
46     {                                                                                                                  \
47         int __errc = __JITparam.errc;                                                                                  \
48         (void)__errc;
49
50 #define endErrorTrap()                                                                                                 \
51     }                                                                                                                  \
52     PAL_ENDTRY
53
54 #define finallyErrorTrap()                                                                                             \
55     }                                                                                                                  \
56     PAL_FINALLY                                                                                                        \
57     {
58
59 /*****************************************************************************/
60
61 // clang-format off
62
63 extern void debugError(const char* msg, const char* file, unsigned line);
64 extern void DECLSPEC_NORETURN badCode();
65 extern void DECLSPEC_NORETURN badCode3(const char* msg, const char* msg2, int arg, __in_z const char* file, unsigned line);
66 extern void DECLSPEC_NORETURN noWay();
67 extern void DECLSPEC_NORETURN NOMEM();
68 extern void DECLSPEC_NORETURN fatal(int errCode);
69
70 extern void DECLSPEC_NORETURN noWayAssertBody();
71 extern void DECLSPEC_NORETURN noWayAssertBody(const char* cond, const char* file, unsigned line);
72
73 // Conditionally invoke the noway assert body. The conditional predicate is evaluated using a method on the tlsCompiler.
74 // If a noway_assert is hit, we ask the Compiler whether to raise an exception (i.e., conditionally raise exception.)
75 // To have backward compatibility between v4.5 and v4.0, in min-opts we take a shot at codegen rather than rethrow.
76 extern void noWayAssertBodyConditional(
77 #ifdef FEATURE_TRACELOGGING
78     const char* file, unsigned line
79 #endif
80     );
81 extern void noWayAssertBodyConditional(const char* cond, const char* file, unsigned line);
82
83 // Define MEASURE_NOWAY to 1 to enable code to count and rank individual noway_assert calls by occurrence.
84 // These asserts would be dynamically executed, but not necessarily fail. The provides some insight into
85 // the dynamic prevalence of these (if not a direct measure of their cost), which exist in non-DEBUG as
86 // well as DEBUG builds.
87 #ifdef DEBUG
88 #define MEASURE_NOWAY 1
89 #else // !DEBUG
90 #define MEASURE_NOWAY 0
91 #endif // !DEBUG
92
93 #if MEASURE_NOWAY
94 extern void RecordNowayAssertGlobal(const char* filename, unsigned line, const char* condStr);
95 #define RECORD_NOWAY_ASSERT(condStr) RecordNowayAssertGlobal(__FILE__, __LINE__, condStr);
96 #else
97 #define RECORD_NOWAY_ASSERT(condStr)
98 #endif
99
100 #ifdef DEBUG
101
102 #define NO_WAY(msg) (debugError(msg, __FILE__, __LINE__), noWay())
103 // Used for fallback stress mode
104 #define NO_WAY_NOASSERT(msg) noWay()
105 #define BADCODE(msg) (debugError(msg, __FILE__, __LINE__), badCode())
106 #define BADCODE3(msg, msg2, arg) badCode3(msg, msg2, arg, __FILE__, __LINE__)
107 // Used for an assert that we want to convert into BADCODE to force minopts, or in minopts to force codegen.
108 #define noway_assert(cond)                                                                                             \
109     do                                                                                                                 \
110     {                                                                                                                  \
111         RECORD_NOWAY_ASSERT(#cond)                                                                                     \
112         if (!(cond))                                                                                                   \
113         {                                                                                                              \
114             noWayAssertBodyConditional(#cond, __FILE__, __LINE__);                                                     \
115         }                                                                                                              \
116     } while (0)
117 #define unreached() noWayAssertBody("unreached", __FILE__, __LINE__)
118
119 #define NOWAY_MSG(msg) noWayAssertBodyConditional(msg, __FILE__, __LINE__)
120
121 #else // !DEBUG
122
123 #define NO_WAY(msg) noWay()
124 #define BADCODE(msg) badCode()
125 #define BADCODE3(msg, msg2, arg) badCode()
126
127 #ifdef FEATURE_TRACELOGGING
128 #define NOWAY_ASSERT_BODY_ARGUMENTS __FILE__, __LINE__
129 #else
130 #define NOWAY_ASSERT_BODY_ARGUMENTS
131 #endif
132
133 #define noway_assert(cond)                                                                                             \
134     do                                                                                                                 \
135     {                                                                                                                  \
136         RECORD_NOWAY_ASSERT(#cond)                                                                                     \
137         if (!(cond))                                                                                                   \
138         {                                                                                                              \
139             noWayAssertBodyConditional(NOWAY_ASSERT_BODY_ARGUMENTS);                                                   \
140         }                                                                                                              \
141     } while (0)
142 #define unreached() noWayAssertBody()
143
144 #define NOWAY_MSG(msg) noWayAssertBodyConditional(NOWAY_ASSERT_BODY_ARGUMENTS)
145
146 #endif // !DEBUG
147
148 // IMPL_LIMITATION is called when we encounter valid IL that is not
149 // supported by our current implementation because of various
150 // limitations (that could be removed in the future)
151 #define IMPL_LIMITATION(msg) NO_WAY(msg)
152
153 #if !defined(_TARGET_X86_) || !defined(LEGACY_BACKEND)
154
155 #if defined(ALT_JIT)
156
157 // This guy can return based on Config flag/Debugger
158 extern void notYetImplemented(const char* msg, const char* file, unsigned line);
159 #define NYIRAW(msg) notYetImplemented(msg, __FILE__, __LINE__)
160
161 #else // !defined(ALT_JIT)
162
163 #define NYIRAW(msg) NOWAY_MSG(msg)
164
165 #endif // !defined(ALT_JIT)
166
167 #define NYI(msg)                    NYIRAW("NYI: " msg)
168 #define NYI_IF(cond, msg) if (cond) NYIRAW("NYI: " msg)
169
170 #ifdef _TARGET_AMD64_
171
172 #define NYI_AMD64(msg)  NYIRAW("NYI_AMD64: " msg)
173 #define NYI_X86(msg)    do { } while (0)
174 #define NYI_ARM(msg)    do { } while (0)
175 #define NYI_ARM64(msg)  do { } while (0)
176
177 #elif defined(_TARGET_X86_)
178
179 #define NYI_AMD64(msg)  do { } while (0)
180 #define NYI_X86(msg)    NYIRAW("NYI_X86: " msg)
181 #define NYI_ARM(msg)    do { } while (0)
182 #define NYI_ARM64(msg)  do { } while (0)
183
184 #elif defined(_TARGET_ARM_)
185
186 #define NYI_AMD64(msg)  do { } while (0)
187 #define NYI_X86(msg)    do { } while (0)
188 #define NYI_ARM(msg)    NYIRAW("NYI_ARM: " msg)
189 #define NYI_ARM64(msg)  do { } while (0)
190
191 #elif defined(_TARGET_ARM64_)
192
193 #define NYI_AMD64(msg)  do { } while (0)
194 #define NYI_X86(msg)    do { } while (0)
195 #define NYI_ARM(msg)    do { } while (0)
196 #define NYI_ARM64(msg)  NYIRAW("NYI_ARM64: " msg)
197
198 #else
199
200 #error "Unknown platform, not x86, ARM, or AMD64?"
201
202 #endif
203
204 #else // NYI not available; make it an assert.
205
206 #define NYI(msg)        assert(!msg)
207 #define NYI_AMD64(msg)  do { } while (0)
208 #define NYI_ARM(msg)    do { } while (0)
209 #define NYI_ARM64(msg)  do { } while (0)
210
211 #endif // NYI not available
212
213 #if !defined(_TARGET_X86_) && !defined(FEATURE_STACK_FP_X87)
214
215 #define NYI_FLAT_FP_X87(msg)    NYI(msg)
216 #define NYI_FLAT_FP_X87_NC(msg) NYI(msg)
217
218 #else
219
220 #define NYI_FLAT_FP_X87(msg)    do { } while (0)
221 #define NYI_FLAT_FP_X87_NC(msg) do { } while (0)
222
223 #endif // !_TARGET_X86_ && !FEATURE_STACK_FP_X87
224
225 // clang-format on
226
227 #if defined(_HOST_X86_) && !defined(FEATURE_PAL)
228
229 // While debugging in an Debugger, the "int 3" will cause the program to break
230 // Outside, the exception handler will just filter out the "int 3".
231
232 #define BreakIfDebuggerPresent()                                                                                       \
233     do                                                                                                                 \
234     {                                                                                                                  \
235         __try                                                                                                          \
236         {                                                                                                              \
237             __asm {int 3}                                                                                              \
238         }                                                                                                              \
239         __except (EXCEPTION_EXECUTE_HANDLER)                                                                           \
240         {                                                                                                              \
241         }                                                                                                              \
242     } while (0)
243
244 #else
245 #define BreakIfDebuggerPresent()                                                                                       \
246     do                                                                                                                 \
247     {                                                                                                                  \
248         if (IsDebuggerPresent())                                                                                       \
249             DebugBreak();                                                                                              \
250     } while (0)
251 #endif
252
253 #ifdef DEBUG
254 DWORD getBreakOnBadCode();
255 #endif
256
257 // For narrowing numeric conversions, the following two methods ensure that the
258 // source value fits in the destination type, using either "assert" or
259 // "noway_assert" to validate the conversion.  Obviously, each returns the source value as
260 // the destination type.
261
262 // (There is an argument that these should be macros, to let the preprocessor capture
263 // a more useful file/line for the error message.  But then we have to use comma expressions
264 // so that these can be used in expressions, etc., which is ugly.  So I propose we rely on
265 // getting stack traces in other ways.)
266 template <typename Dst, typename Src>
267 inline Dst SafeCvtAssert(Src val)
268 {
269     assert(FitsIn<Dst>(val));
270     return static_cast<Dst>(val);
271 }
272
273 template <typename Dst, typename Src>
274 inline Dst SafeCvtNowayAssert(Src val)
275 {
276     noway_assert(FitsIn<Dst>(val));
277     return static_cast<Dst>(val);
278 }
279
280 #endif