// Copyright 2019 The SwiftShader Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef rr_Print_hpp #define rr_Print_hpp #ifdef ENABLE_RR_PRINT # include "Reactor.hpp" # include # include namespace rr { // PrintValue holds the printf format and value(s) for a single argument // to Print(). A single argument can be expanded into multiple printf // values - for example a Float4 will expand to "%f %f %f %f" and four // scalar values. // The PrintValue constructor accepts the following: // * Reactor LValues, RValues, Pointers. // * Standard Plain-Old-Value types (int, float, bool, etc) // * Custom types that specialize the PrintValue::Ty template struct. // * Static arrays in the form T[N] where T can be any of the above. class PrintValue { // Ty is a template that can be specialized for printing type T. // Each specialization must expose: // * A 'static std::string fmt(const T& v)' method that provides the // printf format specifier. // * A 'static std::vector val(const T& v)' method that // returns all the printf format values. template struct Ty { // static std::string fmt(const T& v); // static std::vector val(const T& v); }; // returns the printf values for all the values in the given array. template static std::vector val(const T *list, int count) { std::vector values; values.reserve(count); for(int i = 0; i < count; i++) { auto v = val(list[i]); values.insert(values.end(), v.begin(), v.end()); } return values; } // fmt returns the comma-delimited list of printf format strings for // every element in the provided list, all enclosed in square brackets. template static std::string fmt(const T *list, int count) { std::string out = "["; for(int i = 0; i < count; i++) { if(i > 0) { out += ", "; } out += fmt(list[i]); } return out + "]"; } static std::string addr(const void *ptr) { char buf[32]; snprintf(buf, sizeof(buf), "%p", ptr); return buf; } public: const std::string format; const std::vector values; // Constructs a PrintValue for the given value. template PrintValue(const T &v) : format(fmt(v)) , values(val(v)) {} // Constructs a PrintValue for the given static array. template PrintValue(const T (&v)[N]) : format(fmt(&v[0], N)) , values(val(&v[0], N)) {} // Constructs a PrintValue for the given array starting at arr of length // len. template PrintValue(const T *arr, int len) : format(fmt(arr, len)) , values(val(arr, len)) {} // PrintValue constructors for plain-old-data values. PrintValue(bool v) : format(v ? "true" : "false") {} PrintValue(int8_t v) : format(std::to_string(v)) {} PrintValue(uint8_t v) : format(std::to_string(v)) {} PrintValue(int16_t v) : format(std::to_string(v)) {} PrintValue(uint16_t v) : format(std::to_string(v)) {} PrintValue(int32_t v) : format(std::to_string(v)) {} PrintValue(uint32_t v) : format(std::to_string(v)) {} PrintValue(int64_t v) : format(std::to_string(v)) {} PrintValue(uint64_t v) : format(std::to_string(v)) {} PrintValue(float v) : format(std::to_string(v)) {} PrintValue(double v) : format(std::to_string(v)) {} PrintValue(const char *v) : format(v) {} template PrintValue(T *v) : format(addr(v)) {} // vals is a helper to build composite value lists. // vals returns the full, sequential list of printf argument values used // to print all the provided variadic values. // vals() is intended to be used by implementations of // PrintValue::Ty<>::vals() to help declare aggregate types. // For example, if you were declaring a PrintValue::Ty<> specialization // for a custom Mat4x4 matrix formed from four Vector4 values, you'd // write: // // namespace rr // { // template <> struct PrintValue::Ty // { // static std::string fmt(const Mat4x4& v) // { // return "[a: <%f, %f, %f, %f>," // " b: <%f, %f, %f, %f>," // " c: <%f, %f, %f, %f>," // " d: <%f, %f, %f, %f>]"; // } // static std::vector val(const Mat4x4& v) // { // return PrintValue::vals(v.a, v.b, v.c, v.d); // } // }; // } template static std::vector vals(ARGS... v) { std::vector> lists = { val(v)... }; std::vector joined; for(const auto &list : lists) { joined.insert(joined.end(), list.begin(), list.end()); } return joined; } // returns the printf format specifier for the given type via the // PrintValue::Ty specialization. template static std::string fmt(const T &v) { return Ty::fmt(v); } // returns the printf value for the given type with a // PrintValue::Ty specialization. template static std::vector val(const T &v) { return Ty::val(v); } }; // PrintValue::Ty specializations for basic types. template<> struct PrintValue::Ty { static std::string fmt(const char *v) { return "%s"; } static std::vector val(const char *v); }; template<> struct PrintValue::Ty { static std::string fmt(const std::string &v) { return PrintValue::Ty::fmt(v.c_str()); } static std::vector val(const std::string &v) { return PrintValue::Ty::val(v.c_str()); } }; // PrintValue::Ty specializations for standard Reactor types. template<> struct PrintValue::Ty { static std::string fmt(const RValue &v) { return "%s"; } static std::vector val(const RValue &v); }; template<> struct PrintValue::Ty { static std::string fmt(const RValue &v) { return "%d"; } static std::vector val(const RValue &v); }; template<> struct PrintValue::Ty { static std::string fmt(const RValue &v) { return "[%d, %d, %d, %d]"; } static std::vector val(const RValue &v); }; template<> struct PrintValue::Ty { static std::string fmt(const RValue &v) { return "%d"; } static std::vector val(const RValue &v); }; template<> struct PrintValue::Ty { static std::string fmt(const RValue &v) { return "[%d, %d]"; } static std::vector val(const RValue &v); }; template<> struct PrintValue::Ty { static std::string fmt(const RValue &v) { return "[%d, %d, %d, %d]"; } static std::vector val(const RValue &v); }; template<> struct PrintValue::Ty { static std::string fmt(const RValue &v) { return "%u"; } static std::vector val(const RValue &v); }; template<> struct PrintValue::Ty { static std::string fmt(const RValue &v) { return "[%u, %u]"; } static std::vector val(const RValue &v); }; template<> struct PrintValue::Ty { static std::string fmt(const RValue &v) { return "[%u, %u, %u, %u]"; } static std::vector val(const RValue &v); }; template<> struct PrintValue::Ty { static std::string fmt(const RValue &v) { return "%d"; } static std::vector val(const RValue &v); }; template<> struct PrintValue::Ty { static std::string fmt(const RValue &v) { return "[%d, %d, %d, %d]"; } static std::vector val(const RValue &v); }; template<> struct PrintValue::Ty { static std::string fmt(const RValue &v) { return "%u"; } static std::vector val(const RValue &v); }; template<> struct PrintValue::Ty { static std::string fmt(const RValue &v) { return "[%u, %u, %u, %u]"; } static std::vector val(const RValue &v); }; template<> struct PrintValue::Ty { static std::string fmt(const RValue &v) { return "%f"; } static std::vector val(const RValue &v); }; template<> struct PrintValue::Ty { static std::string fmt(const RValue &v) { return "[%f, %f, %f, %f]"; } static std::vector val(const RValue &v); }; template<> struct PrintValue::Ty { static std::string fmt(const RValue &v) { return "%lld"; } static std::vector val(const RValue &v) { return { v.value }; } }; template struct PrintValue::Ty> { static std::string fmt(const RValue> &v) { return "%p"; } static std::vector val(const RValue> &v) { return { v.value }; } }; template struct PrintValue::Ty> { static std::string fmt(const Reference &v) { return PrintValue::Ty::fmt(v); } static std::vector val(const Reference &v) { return PrintValue::Ty::val(v); } }; template struct PrintValue::Ty> { static std::string fmt(const RValue &v) { return PrintValue::Ty::fmt(v); } static std::vector val(const RValue &v) { return PrintValue::Ty::val(v); } }; // VPrintf emits a call to printf() using vals[0] as the format string, // and vals[1..n] as the args. void VPrintf(const std::vector &vals); // Printv emits a call to printf() using the function, file and line, // message and optional values. // See Printv below. void Printv(const char *function, const char *file, int line, const char *msg, std::initializer_list vals); // Printv emits a call to printf() using the provided message and optional // values. // Printf replaces any bracketed indices in the message with string // representations of the corresponding value in vals. // For example: // Printv("{0} and {1}", "red", "green"); // Would print the string: // "red and green" // Arguments can be indexed in any order. // Invalid indices are not substituted. inline void Printv(const char *msg, std::initializer_list vals) { Printv(nullptr, nullptr, 0, msg, vals); } // Print is a wrapper over Printv that wraps the variadic arguments into an // initializer_list before calling Printv. template void Print(const char *msg, const ARGS &... vals) { Printv(msg, { vals... }); } // Print is a wrapper over Printv that wraps the variadic arguments into an // initializer_list before calling Printv. template void Print(const char *function, const char *file, int line, const char *msg, const ARGS &... vals) { Printv(function, file, line, msg, { vals... }); } // RR_LOG is a macro that calls Print(), automatically populating the // function, file and line parameters and appending a newline to the string. // // RR_LOG() is intended to be used for debugging JIT compiled code, and is // not intended for production use. # if defined(_WIN32) # define RR_LOG(msg, ...) Print(__FUNCSIG__, __FILE__, static_cast(__LINE__), msg "\n", ##__VA_ARGS__) # else # define RR_LOG(msg, ...) Print(__PRETTY_FUNCTION__, __FILE__, static_cast(__LINE__), msg "\n", ##__VA_ARGS__) # endif // Macro magic to perform variadic dispatch. // See: https://renenyffenegger.ch/notes/development/languages/C-C-plus-plus/preprocessor/macros/__VA_ARGS__/count-arguments // Note, this doesn't attempt to use the ##__VA_ARGS__ trick to handle 0 # define RR_MSVC_EXPAND_BUG(X) X // Helper macro to force expanding __VA_ARGS__ to satisfy MSVC compiler. # define RR_GET_NTH_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, N, ...) N # 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)) static_assert(1 == RR_COUNT_ARGUMENTS(a), "RR_COUNT_ARGUMENTS broken"); // Sanity checks. static_assert(2 == RR_COUNT_ARGUMENTS(a, b), "RR_COUNT_ARGUMENTS broken"); static_assert(3 == RR_COUNT_ARGUMENTS(a, b, c), "RR_COUNT_ARGUMENTS broken"); // RR_WATCH_FMT(...) resolves to a string literal that lists all the // arguments by name. This string can be passed to LOG() to print each of // the arguments with their name and value. // // RR_WATCH_FMT(...) uses the RR_COUNT_ARGUMENTS helper macro to delegate to a // corresponding RR_WATCH_FMT_n specialization macro below. # define RR_WATCH_CONCAT(a, b) a##b # define RR_WATCH_CONCAT2(a, b) RR_WATCH_CONCAT(a, b) # define RR_WATCH_FMT(...) RR_MSVC_EXPAND_BUG(RR_WATCH_CONCAT2(RR_WATCH_FMT_, RR_COUNT_ARGUMENTS(__VA_ARGS__))(__VA_ARGS__)) # define RR_WATCH_FMT_1(_1) "\n " # _1 ": {0}" # define RR_WATCH_FMT_2(_1, _2) \ RR_WATCH_FMT_1(_1) \ "\n " #_2 ": {1}" # define RR_WATCH_FMT_3(_1, _2, _3) \ RR_WATCH_FMT_2(_1, _2) \ "\n " #_3 ": {2}" # define RR_WATCH_FMT_4(_1, _2, _3, _4) \ RR_WATCH_FMT_3(_1, _2, _3) \ "\n " #_4 ": {3}" # define RR_WATCH_FMT_5(_1, _2, _3, _4, _5) \ RR_WATCH_FMT_4(_1, _2, _3, _4) \ "\n " #_5 ": {4}" # define RR_WATCH_FMT_6(_1, _2, _3, _4, _5, _6) \ RR_WATCH_FMT_5(_1, _2, _3, _4, _5) \ "\n " #_6 ": {5}" # define RR_WATCH_FMT_7(_1, _2, _3, _4, _5, _6, _7) \ RR_WATCH_FMT_6(_1, _2, _3, _4, _5, _6) \ "\n " #_7 ": {6}" # define RR_WATCH_FMT_8(_1, _2, _3, _4, _5, _6, _7, _8) \ RR_WATCH_FMT_7(_1, _2, _3, _4, _5, _6, _7) \ "\n " #_8 ": {7}" # define RR_WATCH_FMT_9(_1, _2, _3, _4, _5, _6, _7, _8, _9) \ RR_WATCH_FMT_8(_1, _2, _3, _4, _5, _6, _7, _8) \ "\n " #_9 ": {8}" # define RR_WATCH_FMT_10(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \ RR_WATCH_FMT_9(_1, _2, _3, _4, _5, _6, _7, _8, _9) \ "\n " #_10 ": {9}" # define RR_WATCH_FMT_11(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) \ RR_WATCH_FMT_10(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \ "\n " #_11 ": {10}" # define RR_WATCH_FMT_12(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) \ RR_WATCH_FMT_11(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) \ "\n " #_12 ": {11}" // RR_WATCH() is a helper that prints the name and value of all the supplied // arguments. // For example, if you had the Int and bool variables 'foo' and 'bar' that // you want to print, you can simply write: // RR_WATCH(foo, bar) // When this JIT compiled code is executed, it will print the string // "foo: 1, bar: true" to stdout. // // RR_WATCH() is intended to be used for debugging JIT compiled code, and // is not intended for production use. # define RR_WATCH(...) RR_LOG(RR_WATCH_FMT(__VA_ARGS__), __VA_ARGS__) } // namespace rr #endif // ENABLE_RR_PRINT #endif // rr_Print_hpp