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 1 // All platforms currently enable NYI; this should be a tighter condition to exclude some platforms from NYI |
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 | // clang-format on |
214 | |
215 | #if defined(_HOST_X86_) && !defined(FEATURE_PAL) |
216 | |
217 | // While debugging in an Debugger, the "int 3" will cause the program to break |
218 | // Outside, the exception handler will just filter out the "int 3". |
219 | |
220 | #define BreakIfDebuggerPresent() \ |
221 | do \ |
222 | { \ |
223 | __try \ |
224 | { \ |
225 | __asm {int 3} \ |
226 | } \ |
227 | __except (EXCEPTION_EXECUTE_HANDLER) \ |
228 | { \ |
229 | } \ |
230 | } while (0) |
231 | |
232 | #else |
233 | #define BreakIfDebuggerPresent() \ |
234 | do \ |
235 | { \ |
236 | if (IsDebuggerPresent()) \ |
237 | DebugBreak(); \ |
238 | } while (0) |
239 | #endif |
240 | |
241 | #ifdef DEBUG |
242 | DWORD getBreakOnBadCode(); |
243 | #endif |
244 | |
245 | // For narrowing numeric conversions, the following two methods ensure that the |
246 | // source value fits in the destination type, using either "assert" or |
247 | // "noway_assert" to validate the conversion. Obviously, each returns the source value as |
248 | // the destination type. |
249 | |
250 | // (There is an argument that these should be macros, to let the preprocessor capture |
251 | // a more useful file/line for the error message. But then we have to use comma expressions |
252 | // so that these can be used in expressions, etc., which is ugly. So I propose we rely on |
253 | // getting stack traces in other ways.) |
254 | template <typename Dst, typename Src> |
255 | inline Dst SafeCvtAssert(Src val) |
256 | { |
257 | assert(FitsIn<Dst>(val)); |
258 | return static_cast<Dst>(val); |
259 | } |
260 | |
261 | template <typename Dst, typename Src> |
262 | inline Dst SafeCvtNowayAssert(Src val) |
263 | { |
264 | noway_assert(FitsIn<Dst>(val)); |
265 | return static_cast<Dst>(val); |
266 | } |
267 | |
268 | #endif |
269 | |