1/* @title: Enforcement */
2#include <compiler/core.h>
3#include <compiler/intrinsic.h>
4#include <console/crash.h>
5#include <console/panic.h>
6
7#define _enforce_msg(s) "Enforcement \"" s "\" failed"
8
9#define _enforce_eval(x, ...) \
10 _enforce_eval_as(x, PP_CONCAT(_enforce_res_, __COUNTER__), __VA_ARGS__)
11
12#define _enforce_eval_as(x, res_, ...) \
13 ({ \
14 ct_decay(x) res_ = (x); \
15 if (cc_unlikely(!(res_))) { \
16 __VA_ARGS__; \
17 ci_unreachable(); \
18 } \
19 res_; \
20 })
21
22#define _enforce_pick(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, \
23 _13, _14, _15, _16, NAME, ...) \
24 NAME
25
26#define _enforce_arity(...) \
27 _enforce_pick(_, ##__VA_ARGS__, _enforce_n, _enforce_n, _enforce_n, \
28 _enforce_n, _enforce_n, _enforce_n, _enforce_n, _enforce_n, \
29 _enforce_n, _enforce_n, _enforce_n, _enforce_n, _enforce_n, \
30 _enforce_n, _enforce_n, _enforce_1, _enforce_0)
31
32#define _enforce_fn_as(a, msg_fn, code_fn) \
33 cc_generic((a), char * : msg_fn, const char * : msg_fn, default : code_fn)
34
35#define _enforce_fn(a, code_fn) _enforce_fn_as(a, enforce_impl_msg, code_fn)
36
37#define _enforce_ctx(prefix, s) \
38 __FILE__, __LINE__, __func__, prefix, _enforce_msg(s)
39
40/* enforce(x) */
41#define _enforce_0(prefix, s, x) \
42 _enforce_eval(x, enforce_impl_code_only(_enforce_ctx(prefix, s), NULL))
43
44/*
45 * enforce(x, "msg")
46 * enforce(x, INVALID_ARGUMENT)
47 * enforce(x, (asan, POISONED))
48 *
49 * Code forms are documented at CRASH_CODE_ARG in console/crash.h
50 */
51#define _enforce_1(prefix, s, x, a) \
52 _enforce_eval( \
53 x, CRASH_CODE_SCOPED( \
54 a, _enforce_fn(CRASH_CODE_ARG(a), enforce_impl_code_only)( \
55 _enforce_ctx(prefix, s), CRASH_CODE_ARG(a))))
56
57/*
58 * enforce(x, "msg %d", 12)
59 * enforce(x, INVALID_ARGUMENT, "msg %d", 12)
60 * enforce(x, (asan, POISONED), "msg %d", 12)
61 */
62#define _enforce_n(prefix, s, x, a, ...) \
63 _enforce_eval(x, CRASH_CODE_SCOPED( \
64 a, _enforce_fn(CRASH_CODE_ARG(a), enforce_impl_code)( \
65 _enforce_ctx(prefix, s), CRASH_CODE_ARG(a), \
66 __VA_ARGS__)))
67
68#define _enforce_fail(prefix, ...) \
69 enforce_impl_msg(__FILE__, __LINE__, __func__, prefix, NULL, "" __VA_ARGS__)
70
71#define enforce(x, ...) _enforce_arity(__VA_ARGS__)("", #x, x, ##__VA_ARGS__)
72
73/*
74 * enforce_precond(x)
75 * enforce_precond(x, "msg %d", 12)
76 * enforce_precond(x, CODE)
77 * enforce_precond(x, CODE, "msg %d", 12)
78 *
79 * enforce() sugar for precondition validation and messages. Failure
80 * message doesn't have the expression unless DEBUG_ASSUME is on
81 */
82#ifdef DEBUG_ASSUME
83#define _precond_msg(s) "Precondition \"" s "\" failed"
84#else
85#define _precond_msg(s) "Precondition failed"
86#endif
87
88#define _precond_ctx(s) __FILE__, __LINE__, __func__, "", _precond_msg(s)
89
90#define _precond_arity(...) \
91 _enforce_pick(_, ##__VA_ARGS__, _precond_n, _precond_n, _precond_n, \
92 _precond_n, _precond_n, _precond_n, _precond_n, _precond_n, \
93 _precond_n, _precond_n, _precond_n, _precond_n, _precond_n, \
94 _precond_n, _precond_n, _precond_1, _precond_0)
95
96#define _precond_0(s, x) \
97 _enforce_eval( \
98 x, enforce_impl_code_only(_precond_ctx(s), \
99 CRASH_CODE(global, INVALID_ARGUMENT)))
100
101#define _precond_1(s, x, a) \
102 _enforce_eval( \
103 x, CRASH_CODE_SCOPED( \
104 a, _enforce_fn_as(CRASH_CODE_ARG(a), enforce_impl_precond_msg, \
105 enforce_impl_code_only)(_precond_ctx(s), \
106 CRASH_CODE_ARG(a))))
107
108#define _precond_n(s, x, a, ...) \
109 _enforce_eval( \
110 x, CRASH_CODE_SCOPED( \
111 a, _enforce_fn_as(CRASH_CODE_ARG(a), enforce_impl_precond_msg, \
112 enforce_impl_code)( \
113 _precond_ctx(s), CRASH_CODE_ARG(a), __VA_ARGS__)))
114
115#define enforce_precond(x, ...) \
116 _precond_arity(__VA_ARGS__)(#x, x, ##__VA_ARGS__)
117
118#define unimplemented(...) _enforce_fail("unimplemented! ", ##__VA_ARGS__)
119
120#define todo(...) _enforce_fail("TODO: ", ##__VA_ARGS__)
121
122#define unreachable(...) \
123 enforce_impl_code(__FILE__, __LINE__, __func__, "unreachable! ", NULL, \
124 CRASH_CODE(global, UNREACHABLE), "" __VA_ARGS__)
125
126/*
127 * assume(x)
128 * assume(x, "msg %d", 12)
129 *
130 * No crash codes for assume()
131 */
132void _assume_not_constant_false(bool cond)
133 cc_diagnose_if(!cond, "assume() on a constant false, use unreachable()",
134 "error");
135
136#define _assume_static_checks(x) \
137 ((void) sizeof(({ \
138 cc_assume(x); \
139 _assume_not_constant_false(x); \
140 0; \
141 })))
142
143/* Only used in sizeof for a format check, not a real function anywhere */
144int _assume_fmt(const char *fmt, ...) cc_printf_like(1, 2);
145
146#define _assume_msg_only(...) \
147 static_assert(!PP_IS_PAREN(PP_GET_1(__VA_ARGS__, ~)), \
148 "assume() takes no crash code"); \
149 (void) sizeof(_assume_fmt("" __VA_ARGS__))
150
151#ifdef DEBUG_ASSUME
152
153#define assume(x, ...) \
154 ({ \
155 _assume_static_checks(x); \
156 _assume_msg_only(__VA_ARGS__); \
157 if (cc_unlikely(!(x))) \
158 enforce_impl_msg(_enforce_ctx("assume! ", #x), "" __VA_ARGS__); \
159 })
160
161#else
162
163#define assume(x, ...) \
164 ({ \
165 _assume_static_checks(x); \
166 _assume_msg_only(__VA_ARGS__); \
167 cc_assume(x); \
168 })
169
170#endif
171