xref: /aosp_15_r20/external/swiftshader/src/Reactor/Print.hpp (revision 03ce13f70fcc45d86ee91b7ee4cab1936a95046e)
1 // Copyright 2019 The SwiftShader Authors. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //    http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifndef rr_Print_hpp
16 #define rr_Print_hpp
17 
18 #ifdef ENABLE_RR_PRINT
19 
20 #	include "Reactor.hpp"
21 #	include "SIMD.hpp"
22 
23 #	include <string>
24 #	include <vector>
25 
26 namespace rr {
27 
28 // PrintValue holds the printf format and value(s) for a single argument
29 // to Print(). A single argument can be expanded into multiple printf
30 // values - for example a Float4 will expand to "%f %f %f %f" and four
31 // scalar values.
32 // The PrintValue constructor accepts the following:
33 //   * Reactor LValues, RValues, Pointers.
34 //   * Standard Plain-Old-Value types (int, float, bool, etc)
35 //   * Custom types that specialize the PrintValue::Ty template struct.
36 //   * Static arrays in the form T[N] where T can be any of the above.
37 class PrintValue
38 {
39 public:
40 	// Ty is a template that can be specialized for printing type T.
41 	// Each specialization must expose:
42 	//  * A 'static std::string fmt(const T& v)' method that provides the
43 	//    printf format specifier.
44 	//  * A 'static std::vector<rr::Value*> val(const T& v)' method that
45 	//    returns all the printf format values.
46 	template<typename T>
47 	struct Ty
48 	{
49 		// static std::string fmt(const T& v);
50 		// static std::vector<rr::Value*> val(const T& v);
51 	};
52 
53 	// returns the printf values for all the values in the given array.
54 	template<typename T>
val(const T * list,int count)55 	static std::vector<Value *> val(const T *list, int count)
56 	{
57 		std::vector<Value *> values;
58 		values.reserve(count);
59 		for(int i = 0; i < count; i++)
60 		{
61 			auto v = val(list[i]);
62 			values.insert(values.end(), v.begin(), v.end());
63 		}
64 		return values;
65 	}
66 
67 	// fmt returns the comma-delimited list of printf format strings for
68 	// every element in the provided list, all enclosed in square brackets.
69 	template<typename T>
fmt(const T * list,int count)70 	static std::string fmt(const T *list, int count)
71 	{
72 		std::string out = "[";
73 		for(int i = 0; i < count; i++)
74 		{
75 			if(i > 0) { out += ", "; }
76 			out += fmt(list[i]);
77 		}
78 		return out + "]";
79 	}
80 
addr(const void * ptr)81 	static std::string addr(const void *ptr)
82 	{
83 		char buf[32];
84 		snprintf(buf, sizeof(buf), "%p", ptr);
85 		return buf;
86 	}
87 
88 	const std::string format;
89 	const std::vector<Value *> values;
90 
91 	// Constructs a PrintValue for the given value.
92 	template<typename T>
PrintValue(const T & v)93 	PrintValue(const T &v)
94 	    : format(fmt(v))
95 	    , values(val(v))
96 	{}
97 
98 	// Constructs a PrintValue for the given static array.
99 	template<typename T, int N>
PrintValue(const T (& v)[N])100 	PrintValue(const T (&v)[N])
101 	    : format(fmt(&v[0], N))
102 	    , values(val(&v[0], N))
103 	{}
104 
105 	// Constructs a PrintValue for the given array starting at arr of length
106 	// len.
107 	template<typename T>
PrintValue(const T * arr,int len)108 	PrintValue(const T *arr, int len)
109 	    : format(fmt(arr, len))
110 	    , values(val(arr, len))
111 	{}
112 
113 	// PrintValue constructors for plain-old-data values.
PrintValue(bool v)114 	PrintValue(bool v)
115 	    : format(v ? "true" : "false")
116 	{}
PrintValue(int8_t v)117 	PrintValue(int8_t v)
118 	    : format(std::to_string(v))
119 	{}
PrintValue(uint8_t v)120 	PrintValue(uint8_t v)
121 	    : format(std::to_string(v))
122 	{}
PrintValue(int16_t v)123 	PrintValue(int16_t v)
124 	    : format(std::to_string(v))
125 	{}
PrintValue(uint16_t v)126 	PrintValue(uint16_t v)
127 	    : format(std::to_string(v))
128 	{}
PrintValue(int32_t v)129 	PrintValue(int32_t v)
130 	    : format(std::to_string(v))
131 	{}
PrintValue(uint32_t v)132 	PrintValue(uint32_t v)
133 	    : format(std::to_string(v))
134 	{}
PrintValue(int64_t v)135 	PrintValue(int64_t v)
136 	    : format(std::to_string(v))
137 	{}
PrintValue(uint64_t v)138 	PrintValue(uint64_t v)
139 	    : format(std::to_string(v))
140 	{}
PrintValue(float v)141 	PrintValue(float v)
142 	    : format(std::to_string(v))
143 	{}
PrintValue(double v)144 	PrintValue(double v)
145 	    : format(std::to_string(v))
146 	{}
PrintValue(const char * v)147 	PrintValue(const char *v)
148 	    : format(v)
149 	{}
150 
151 	template<typename T>
PrintValue(T * v)152 	PrintValue(T *v)
153 	    : format(addr(v))
154 	{}
155 
156 	// vals is a helper to build composite value lists.
157 	// vals returns the full, sequential list of printf argument values used
158 	// to print all the provided variadic values.
159 	// vals() is intended to be used by implementations of
160 	// PrintValue::Ty<>::vals() to help declare aggregate types.
161 	// For example, if you were declaring a PrintValue::Ty<> specialization
162 	// for a custom Mat4x4 matrix formed from four Vector4 values, you'd
163 	// write:
164 	//
165 	// namespace rr
166 	// {
167 	//		template <> struct PrintValue::Ty<Mat4x4>
168 	//		{
169 	//			static std::string fmt(const Mat4x4& v)
170 	//			{
171 	//				return	"[a: <%f, %f, %f, %f>,"
172 	//				        " b: <%f, %f, %f, %f>,"
173 	//				        " c: <%f, %f, %f, %f>,"
174 	//				        " d: <%f, %f, %f, %f>]";
175 	//			}
176 	//			static std::vector<rr::Value*> val(const Mat4x4& v)
177 	//			{
178 	//				return PrintValue::vals(v.a, v.b, v.c, v.d);
179 	//			}
180 	//		};
181 	//	}
182 	template<typename... ARGS>
vals(ARGS...v)183 	static std::vector<Value *> vals(ARGS... v)
184 	{
185 		std::vector<std::vector<Value *>> lists = { val(v)... };
186 		std::vector<Value *> joined;
187 		for(const auto &list : lists)
188 		{
189 			joined.insert(joined.end(), list.begin(), list.end());
190 		}
191 		return joined;
192 	}
193 
194 	// returns the printf format specifier for the given type via the
195 	// PrintValue::Ty<T> specialization.
196 	template<typename T>
fmt(const T & v)197 	static std::string fmt(const T &v)
198 	{
199 		return Ty<T>::fmt(v);
200 	}
201 
202 	// returns the printf value for the given type with a
203 	// PrintValue::Ty<T> specialization.
204 	template<typename T>
val(const T & v)205 	static std::vector<Value *> val(const T &v)
206 	{
207 		return Ty<T>::val(v);
208 	}
209 };
210 
211 // PrintValue::Ty<T> specializations for basic types.
212 template<>
213 struct PrintValue::Ty<const char *>
214 {
fmtrr::PrintValue::Ty215 	static std::string fmt(const char *v) { return "%s"; }
216 	static std::vector<Value *> val(const char *v);
217 };
218 template<>
219 struct PrintValue::Ty<std::string>
220 {
fmtrr::PrintValue::Ty221 	static std::string fmt(const std::string &v) { return PrintValue::Ty<const char *>::fmt(v.c_str()); }
valrr::PrintValue::Ty222 	static std::vector<Value *> val(const std::string &v) { return PrintValue::Ty<const char *>::val(v.c_str()); }
223 };
224 
225 // PrintValue::Ty<T> specializations for standard Reactor types.
226 template<>
227 struct PrintValue::Ty<Bool>
228 {
fmtrr::PrintValue::Ty229 	static std::string fmt(const RValue<Bool> &v) { return "%s"; }
230 	static std::vector<Value *> val(const RValue<Bool> &v);
231 };
232 template<>
233 struct PrintValue::Ty<Byte>
234 {
fmtrr::PrintValue::Ty235 	static std::string fmt(const RValue<Byte> &v) { return "%d"; }
236 	static std::vector<Value *> val(const RValue<Byte> &v);
237 };
238 template<>
239 struct PrintValue::Ty<Byte4>
240 {
fmtrr::PrintValue::Ty241 	static std::string fmt(const RValue<Byte4> &v) { return "[%d, %d, %d, %d]"; }
242 	static std::vector<Value *> val(const RValue<Byte4> &v);
243 };
244 template<>
245 struct PrintValue::Ty<Int>
246 {
fmtrr::PrintValue::Ty247 	static std::string fmt(const RValue<Int> &v) { return "%d"; }
248 	static std::vector<Value *> val(const RValue<Int> &v);
249 };
250 template<>
251 struct PrintValue::Ty<Int2>
252 {
fmtrr::PrintValue::Ty253 	static std::string fmt(const RValue<Int2> &v) { return "[%d, %d]"; }
254 	static std::vector<Value *> val(const RValue<Int2> &v);
255 };
256 template<>
257 struct PrintValue::Ty<Int4>
258 {
fmtrr::PrintValue::Ty259 	static std::string fmt(const RValue<Int4> &v) { return "[%d, %d, %d, %d]"; }
260 	static std::vector<Value *> val(const RValue<Int4> &v);
261 };
262 template<>
263 struct PrintValue::Ty<UInt>
264 {
fmtrr::PrintValue::Ty265 	static std::string fmt(const RValue<UInt> &v) { return "%u"; }
266 	static std::vector<Value *> val(const RValue<UInt> &v);
267 };
268 template<>
269 struct PrintValue::Ty<UInt2>
270 {
fmtrr::PrintValue::Ty271 	static std::string fmt(const RValue<UInt2> &v) { return "[%u, %u]"; }
272 	static std::vector<Value *> val(const RValue<UInt2> &v);
273 };
274 template<>
275 struct PrintValue::Ty<UInt4>
276 {
fmtrr::PrintValue::Ty277 	static std::string fmt(const RValue<UInt4> &v) { return "[%u, %u, %u, %u]"; }
278 	static std::vector<Value *> val(const RValue<UInt4> &v);
279 };
280 template<>
281 struct PrintValue::Ty<Short>
282 {
fmtrr::PrintValue::Ty283 	static std::string fmt(const RValue<Short> &v) { return "%d"; }
284 	static std::vector<Value *> val(const RValue<Short> &v);
285 };
286 template<>
287 struct PrintValue::Ty<Short4>
288 {
fmtrr::PrintValue::Ty289 	static std::string fmt(const RValue<Short4> &v) { return "[%d, %d, %d, %d]"; }
290 	static std::vector<Value *> val(const RValue<Short4> &v);
291 };
292 template<>
293 struct PrintValue::Ty<UShort>
294 {
fmtrr::PrintValue::Ty295 	static std::string fmt(const RValue<UShort> &v) { return "%u"; }
296 	static std::vector<Value *> val(const RValue<UShort> &v);
297 };
298 template<>
299 struct PrintValue::Ty<UShort4>
300 {
fmtrr::PrintValue::Ty301 	static std::string fmt(const RValue<UShort4> &v) { return "[%u, %u, %u, %u]"; }
302 	static std::vector<Value *> val(const RValue<UShort4> &v);
303 };
304 template<>
305 struct PrintValue::Ty<Float>
306 {
fmtrr::PrintValue::Ty307 	static std::string fmt(const RValue<Float> &v) { return "%f"; }
308 	static std::vector<Value *> val(const RValue<Float> &v);
309 };
310 template<>
311 struct PrintValue::Ty<Float4>
312 {
fmtrr::PrintValue::Ty313 	static std::string fmt(const RValue<Float4> &v) { return "[%f, %f, %f, %f]"; }
314 	static std::vector<Value *> val(const RValue<Float4> &v);
315 };
316 template<>
317 struct PrintValue::Ty<Long>
318 {
fmtrr::PrintValue::Ty319 	static std::string fmt(const RValue<Long> &v) { return "%lld"; }
valrr::PrintValue::Ty320 	static std::vector<Value *> val(const RValue<Long> &v) { return { v.value() }; }
321 };
322 template<typename T>
323 struct PrintValue::Ty<Pointer<T>>
324 {
fmtrr::PrintValue::Ty325 	static std::string fmt(const RValue<Pointer<T>> &v) { return "%p"; }
valrr::PrintValue::Ty326 	static std::vector<Value *> val(const RValue<Pointer<T>> &v) { return { v.value() }; }
327 };
328 template<>
329 struct PrintValue::Ty<SIMD::Pointer>
330 {
fmtrr::PrintValue::Ty331 	static std::string fmt(const SIMD::Pointer &v)
332 	{
333 		if(v.isBasePlusOffset)
334 		{
335 			std::string format;
336 			for(int i = 1; i < SIMD::Width; i++) { format += ", %d"; }
337 			return "{%p + [%d" + format + "]}";
338 		}
339 		else
340 		{
341 			std::string format;
342 			for(int i = 1; i < SIMD::Width; i++) { format += ", %p"; }
343 			return "{%p" + format + "}";
344 		}
345 	}
346 
valrr::PrintValue::Ty347 	static std::vector<Value *> val(const SIMD::Pointer &v)
348 	{
349 		return v.getPrintValues();
350 	}
351 };
352 template<>
353 struct PrintValue::Ty<SIMD::Int>
354 {
fmtrr::PrintValue::Ty355 	static std::string fmt(const RValue<SIMD::Int> &v)
356 	{
357 		std::string format;
358 		for(int i = 1; i < SIMD::Width; i++) { format += ", %d"; }
359 		return "[%d" + format + "]";
360 	}
361 	static std::vector<Value *> val(const RValue<SIMD::Int> &v);
362 };
363 template<>
364 struct PrintValue::Ty<SIMD::UInt>
365 {
fmtrr::PrintValue::Ty366 	static std::string fmt(const RValue<SIMD::UInt> &v)
367 	{
368 		std::string format;
369 		for(int i = 1; i < SIMD::Width; i++) { format += ", %u"; }
370 		return "[%u" + format + "]";
371 	}
372 	static std::vector<Value *> val(const RValue<SIMD::UInt> &v);
373 };
374 template<>
375 struct PrintValue::Ty<SIMD::Float>
376 {
fmtrr::PrintValue::Ty377 	static std::string fmt(const RValue<SIMD::Float> &v)
378 	{
379 		std::string format;
380 		for(int i = 1; i < SIMD::Width; i++) { format += ", %f"; }
381 		return "[%f" + format + "]";
382 	}
383 	static std::vector<Value *> val(const RValue<SIMD::Float> &v);
384 };
385 template<typename T>
386 struct PrintValue::Ty<Reference<T>>
387 {
fmtrr::PrintValue::Ty388 	static std::string fmt(const Reference<T> &v) { return PrintValue::Ty<T>::fmt(v); }
valrr::PrintValue::Ty389 	static std::vector<Value *> val(const Reference<T> &v) { return PrintValue::Ty<T>::val(v); }
390 };
391 template<typename T>
392 struct PrintValue::Ty<RValue<T>>
393 {
fmtrr::PrintValue::Ty394 	static std::string fmt(const RValue<T> &v) { return PrintValue::Ty<T>::fmt(v); }
valrr::PrintValue::Ty395 	static std::vector<Value *> val(const RValue<T> &v) { return PrintValue::Ty<T>::val(v); }
396 };
397 
398 // VPrintf emits a call to printf() using vals[0] as the format string,
399 // and vals[1..n] as the args.
400 void VPrintf(const std::vector<Value *> &vals);
401 
402 // Printv emits a call to printf() using the function, file and line,
403 // message and optional values.
404 // See Printv below.
405 void Printv(const char *function, const char *file, int line, const char *msg, std::initializer_list<PrintValue> vals);
406 
407 // Printv emits a call to printf() using the provided message and optional
408 // values.
409 // Printf replaces any bracketed indices in the message with string
410 // representations of the corresponding value in vals.
411 // For example:
412 //   Printv("{0} and {1}", "red", "green");
413 // Would print the string:
414 //   "red and green"
415 // Arguments can be indexed in any order.
416 // Invalid indices are not substituted.
Printv(const char * msg,std::initializer_list<PrintValue> vals)417 inline void Printv(const char *msg, std::initializer_list<PrintValue> vals)
418 {
419 	Printv(nullptr, nullptr, 0, msg, vals);
420 }
421 
422 // Print is a wrapper over Printv that wraps the variadic arguments into an
423 // initializer_list before calling Printv.
424 template<typename... ARGS>
Print(const char * msg,const ARGS &...vals)425 void Print(const char *msg, const ARGS &...vals)
426 {
427 	Printv(msg, { vals... });
428 }
429 
430 // Print is a wrapper over Printv that wraps the variadic arguments into an
431 // initializer_list before calling Printv.
432 template<typename... ARGS>
Print(const char * function,const char * file,int line,const char * msg,const ARGS &...vals)433 void Print(const char *function, const char *file, int line, const char *msg, const ARGS &...vals)
434 {
435 	Printv(function, file, line, msg, { vals... });
436 }
437 
438 // RR_LOG is a macro that calls Print(), automatically populating the
439 // function, file and line parameters and appending a newline to the string.
440 //
441 // RR_LOG() is intended to be used for debugging JIT compiled code, and is
442 // not intended for production use.
443 #	if defined(_WIN32)
444 #		define RR_LOG(msg, ...) Print(__FUNCSIG__, __FILE__, static_cast<int>(__LINE__), msg "\n", ##__VA_ARGS__)
445 #	else
446 #		define RR_LOG(msg, ...) Print(__PRETTY_FUNCTION__, __FILE__, static_cast<int>(__LINE__), msg "\n", ##__VA_ARGS__)
447 #	endif
448 
449 // Macro magic to perform variadic dispatch.
450 // See: https://renenyffenegger.ch/notes/development/languages/C-C-plus-plus/preprocessor/macros/__VA_ARGS__/count-arguments
451 // Note, this doesn't attempt to use the ##__VA_ARGS__ trick to handle 0
452 #	define RR_MSVC_EXPAND_BUG(X) X  // Helper macro to force expanding __VA_ARGS__ to satisfy MSVC compiler.
453 #	define RR_GET_NTH_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, N, ...) N
454 #	define RR_COUNT_ARGUMENTS(...) RR_MSVC_EXPAND_BUG(RR_GET_NTH_ARG(__VA_ARGS__, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0))
455 static_assert(1 == RR_COUNT_ARGUMENTS(a), "RR_COUNT_ARGUMENTS broken");  // Sanity checks.
456 static_assert(2 == RR_COUNT_ARGUMENTS(a, b), "RR_COUNT_ARGUMENTS broken");
457 static_assert(3 == RR_COUNT_ARGUMENTS(a, b, c), "RR_COUNT_ARGUMENTS broken");
458 
459 // RR_WATCH_FMT(...) resolves to a string literal that lists all the
460 // arguments by name. This string can be passed to LOG() to print each of
461 // the arguments with their name and value.
462 //
463 // RR_WATCH_FMT(...) uses the RR_COUNT_ARGUMENTS helper macro to delegate to a
464 // corresponding RR_WATCH_FMT_n specialization macro below.
465 #	define RR_WATCH_CONCAT(a, b) a##b
466 #	define RR_WATCH_CONCAT2(a, b) RR_WATCH_CONCAT(a, b)
467 #	define RR_WATCH_FMT(...) RR_MSVC_EXPAND_BUG(RR_WATCH_CONCAT2(RR_WATCH_FMT_, RR_COUNT_ARGUMENTS(__VA_ARGS__))(__VA_ARGS__))
468 #	define RR_WATCH_FMT_1(_1) "\n  " #    _1 ": {0}"
469 #	define RR_WATCH_FMT_2(_1, _2) \
470 		RR_WATCH_FMT_1(_1)         \
471 		"\n  " #_2 ": {1}"
472 #	define RR_WATCH_FMT_3(_1, _2, _3) \
473 		RR_WATCH_FMT_2(_1, _2)         \
474 		"\n  " #_3 ": {2}"
475 #	define RR_WATCH_FMT_4(_1, _2, _3, _4) \
476 		RR_WATCH_FMT_3(_1, _2, _3)         \
477 		"\n  " #_4 ": {3}"
478 #	define RR_WATCH_FMT_5(_1, _2, _3, _4, _5) \
479 		RR_WATCH_FMT_4(_1, _2, _3, _4)         \
480 		"\n  " #_5 ": {4}"
481 #	define RR_WATCH_FMT_6(_1, _2, _3, _4, _5, _6) \
482 		RR_WATCH_FMT_5(_1, _2, _3, _4, _5)         \
483 		"\n  " #_6 ": {5}"
484 #	define RR_WATCH_FMT_7(_1, _2, _3, _4, _5, _6, _7) \
485 		RR_WATCH_FMT_6(_1, _2, _3, _4, _5, _6)         \
486 		"\n  " #_7 ": {6}"
487 #	define RR_WATCH_FMT_8(_1, _2, _3, _4, _5, _6, _7, _8) \
488 		RR_WATCH_FMT_7(_1, _2, _3, _4, _5, _6, _7)         \
489 		"\n  " #_8 ": {7}"
490 #	define RR_WATCH_FMT_9(_1, _2, _3, _4, _5, _6, _7, _8, _9) \
491 		RR_WATCH_FMT_8(_1, _2, _3, _4, _5, _6, _7, _8)         \
492 		"\n  " #_9 ": {8}"
493 #	define RR_WATCH_FMT_10(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \
494 		RR_WATCH_FMT_9(_1, _2, _3, _4, _5, _6, _7, _8, _9)           \
495 		"\n  " #_10 ": {9}"
496 #	define RR_WATCH_FMT_11(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) \
497 		RR_WATCH_FMT_10(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10)          \
498 		"\n  " #_11 ": {10}"
499 #	define RR_WATCH_FMT_12(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) \
500 		RR_WATCH_FMT_11(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11)          \
501 		"\n  " #_12 ": {11}"
502 #	define RR_WATCH_FMT_13(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) \
503 		RR_WATCH_FMT_12(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12)          \
504 		"\n  " #_13 ": {12}"
505 #	define RR_WATCH_FMT_14(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) \
506 		RR_WATCH_FMT_13(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13)          \
507 		"\n  " #_14 ": {13}"
508 #	define RR_WATCH_FMT_15(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) \
509 		RR_WATCH_FMT_14(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14)          \
510 		"\n  " #_15 ": {14}"
511 #	define RR_WATCH_FMT_16(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) \
512 		RR_WATCH_FMT_15(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15)          \
513 		"\n  " #_16 ": {15}"
514 #	define RR_WATCH_FMT_17(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17) \
515 		RR_WATCH_FMT_16(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16)          \
516 		"\n  " #_17 ": {16}"
517 #	define RR_WATCH_FMT_18(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18) \
518 		RR_WATCH_FMT_17(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17)          \
519 		"\n  " #_18 ": {17}"
520 
521 // RR_WATCH() is a helper that prints the name and value of all the supplied
522 // arguments.
523 // For example, if you had the Int and bool variables 'foo' and 'bar' that
524 // you want to print, you can simply write:
525 //    RR_WATCH(foo, bar)
526 // When this JIT compiled code is executed, it will print the string
527 // "foo: 1, bar: true" to stdout.
528 //
529 // RR_WATCH() is intended to be used for debugging JIT compiled code, and
530 // is not intended for production use.
531 #	define RR_WATCH(...) RR_LOG(RR_WATCH_FMT(__VA_ARGS__), __VA_ARGS__)
532 
533 }  // namespace rr
534 
535 #	define RR_PRINT_ONLY(x) x
536 #else
537 #	define RR_PRINT_ONLY(x)
538 #endif  // ENABLE_RR_PRINT
539 
540 #endif  // rr_Print_hpp
541