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
14class Compiler;
15
16struct 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)
28extern 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
63extern void debugError(const char* msg, const char* file, unsigned line);
64extern void DECLSPEC_NORETURN badCode();
65extern void DECLSPEC_NORETURN badCode3(const char* msg, const char* msg2, int arg, __in_z const char* file, unsigned line);
66extern void DECLSPEC_NORETURN noWay();
67extern void DECLSPEC_NORETURN NOMEM();
68extern void DECLSPEC_NORETURN fatal(int errCode);
69
70extern void DECLSPEC_NORETURN noWayAssertBody();
71extern 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.
76extern void noWayAssertBodyConditional(
77#ifdef FEATURE_TRACELOGGING
78 const char* file, unsigned line
79#endif
80 );
81extern 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
94extern 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
158extern 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
242DWORD 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.)
254template <typename Dst, typename Src>
255inline Dst SafeCvtAssert(Src val)
256{
257 assert(FitsIn<Dst>(val));
258 return static_cast<Dst>(val);
259}
260
261template <typename Dst, typename Src>
262inline Dst SafeCvtNowayAssert(Src val)
263{
264 noway_assert(FitsIn<Dst>(val));
265 return static_cast<Dst>(val);
266}
267
268#endif
269