summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/common/assert.cpp10
-rw-r--r--src/common/assert.h58
2 files changed, 36 insertions, 32 deletions
diff --git a/src/common/assert.cpp b/src/common/assert.cpp
index a27a025ae..b20c19123 100644
--- a/src/common/assert.cpp
+++ b/src/common/assert.cpp
@@ -6,9 +6,13 @@
6 6
7#include "common/settings.h" 7#include "common/settings.h"
8 8
9void assert_handle_failure() { 9void assert_check_condition(bool cond, std::function<void()>&& on_failure) {
10 if (Settings::values.use_debug_asserts) { 10 if (!cond) {
11 Crash(); 11 on_failure();
12
13 if (Settings::values.use_debug_asserts) {
14 Crash();
15 }
12 } 16 }
13} 17}
14 18
diff --git a/src/common/assert.h b/src/common/assert.h
index 478bfa856..fb7808657 100644
--- a/src/common/assert.h
+++ b/src/common/assert.h
@@ -4,57 +4,57 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <functional>
8
7#include "common/logging/log.h" 9#include "common/logging/log.h"
8 10
9// Sometimes we want to try to continue even after hitting an assert. 11// Sometimes we want to try to continue even after hitting an assert.
10// However touching this file yields a global recompilation as this header is included almost 12// However touching this file yields a global recompilation as this header is included almost
11// everywhere. So let's just move the handling of the failed assert to a single cpp file. 13// everywhere. So let's just move the handling of the failed assert to a single cpp file.
12void assert_handle_failure();
13
14[[noreturn]] void unreachable_impl();
15 14
16// For asserts we'd like to keep all the junk executed when an assert happens away from the 15// For asserts we'd like to keep all the junk executed when an assert happens away from the
17// important code in the function. One way of doing this is to put all the relevant code inside a 16// important code in the function. One way of doing this is to put all the relevant code inside a
18// lambda and force the compiler to not inline it. Unfortunately, MSVC seems to have no syntax to 17// lambda and force the compiler to not inline it.
19// specify __declspec on lambda functions, so what we do instead is define a noinline wrapper 18void assert_check_condition(bool cond, std::function<void()>&& on_failure);
20// template that calls the lambda. This seems to generate an extra instruction at the call-site 19
21// compared to the ideal implementation (which wouldn't support ASSERT_MSG parameters), but is good 20[[noreturn]] void unreachable_impl();
22// enough for our purposes.
23template <typename Fn>
24#if defined(_MSC_VER)
25[[msvc::noinline]]
26#elif defined(__GNUC__)
27[[gnu::cold, gnu::noinline]]
28#endif
29static void
30assert_noinline_call(const Fn& fn) {
31 fn();
32 assert_handle_failure();
33}
34 21
35#define ASSERT(_a_) \ 22#define ASSERT(_a_) \
36 do \ 23 do { \
37 if (!(_a_)) { \ 24 if (std::is_constant_evaluated()) { \
38 assert_noinline_call([] { LOG_CRITICAL(Debug, "Assertion Failed!"); }); \ 25 if (!(_a_)) { \
26 /* Will trigger compile error here */ \
27 assert_check_condition(bool(_a_), \
28 [] { LOG_CRITICAL(Debug, "Assertion Failed!"); }); \
29 } \
30 } else { \
31 assert_check_condition(bool(_a_), [] { LOG_CRITICAL(Debug, "Assertion Failed!"); }); \
39 } \ 32 } \
40 while (0) 33 } while (0)
41 34
42#define ASSERT_MSG(_a_, ...) \ 35#define ASSERT_MSG(_a_, ...) \
43 do \ 36 do { \
44 if (!(_a_)) { \ 37 if (std::is_constant_evaluated()) { \
45 assert_noinline_call([&] { LOG_CRITICAL(Debug, "Assertion Failed!\n" __VA_ARGS__); }); \ 38 if (!(_a_)) { \
39 /* Will trigger compile error here */ \
40 assert_check_condition(bool(_a_), \
41 [] { LOG_CRITICAL(Debug, "Assertion Failed!"); }); \
42 } \
43 } else { \
44 assert_check_condition( \
45 bool(_a_), [&] { LOG_CRITICAL(Debug, "Assertion Failed!\n" __VA_ARGS__); }); \
46 } \ 46 } \
47 while (0) 47 } while (0)
48 48
49#define UNREACHABLE() \ 49#define UNREACHABLE() \
50 do { \ 50 do { \
51 assert_noinline_call([] { LOG_CRITICAL(Debug, "Unreachable code!"); }); \ 51 LOG_CRITICAL(Debug, "Unreachable code!"); \
52 unreachable_impl(); \ 52 unreachable_impl(); \
53 } while (0) 53 } while (0)
54 54
55#define UNREACHABLE_MSG(...) \ 55#define UNREACHABLE_MSG(...) \
56 do { \ 56 do { \
57 assert_noinline_call([&] { LOG_CRITICAL(Debug, "Unreachable code!\n" __VA_ARGS__); }); \ 57 LOG_CRITICAL(Debug, "Unreachable code!\n" __VA_ARGS__); \
58 unreachable_impl(); \ 58 unreachable_impl(); \
59 } while (0) 59 } while (0)
60 60