/* * Souffle - A Datalog Compiler * Copyright (c) 2021, The Souffle Developers. All rights reserved * Licensed under the Universal Permissive License v 1.0 as shown at: * - https://opensource.org/licenses/UPL * - /licenses/SOUFFLE-UPL.txt */ /************************************************************************ * * @file MiscUtil.h * * @brief Datalog project utilities * ***********************************************************************/ #pragma once #include "souffle/utility/General.h" #include "souffle/utility/Iteration.h" #include "souffle/utility/Types.h" #include "tinyformat.h" #include #include #include #include #include #include #include #include #ifdef _WIN32 #define NOMINMAX #define NOGDI #include #include #include #include /** * On windows, the following gcc builtins are missing. * * In the case of popcountll, __popcnt64 is the windows equivalent. * * For ctz and ctzll, BitScanForward and BitScanForward64 are the respective * windows equivalents. However ctz is used in a constexpr context, and we can't * use BitScanForward, so we implement it ourselves. */ #define __builtin_popcountll __popcnt64 #if defined(_MSC_VER) // return the number of trailing zeroes in value, or 32 if value is zero. inline constexpr unsigned long __builtin_ctz(unsigned long value) { unsigned long trailing_zeroes = 0; if (value == 0) return 32; while ((value = value >> 1) ^ 1) { ++trailing_zeroes; } return trailing_zeroes; } // return the number of trailing zeroes in value, or 64 if value is zero. inline constexpr int __builtin_ctzll_constexpr(unsigned long long value) { int trailing_zeroes = 0; if (value == 0) return 64; while ((value = value >> 1) ^ 1) { ++trailing_zeroes; } return trailing_zeroes; } inline int __builtin_ctzll(unsigned long long value) { unsigned long trailing_zeroes = 0; if (_BitScanForward64(&trailing_zeroes, value)) { return static_cast(trailing_zeroes); } else { return 64; // return 64 like GCC would when value == 0 } } #endif // _MSC_VER #endif // _WIN32 // ------------------------------------------------------------------------------- // Timing Utils // ------------------------------------------------------------------------------- namespace souffle { // a type def for a time point using time_point = std::chrono::high_resolution_clock::time_point; using std::chrono::microseconds; // a shortcut for taking the current time inline time_point now() { return std::chrono::high_resolution_clock::now(); } // a shortcut for obtaining the time difference in milliseconds inline long duration_in_us(const time_point& start, const time_point& end) { return static_cast(std::chrono::duration_cast(end - start).count()); } // a shortcut for obtaining the time difference in nanoseconds inline long duration_in_ns(const time_point& start, const time_point& end) { return static_cast(std::chrono::duration_cast(end - start).count()); } // ------------------------------------------------------------------------------- // Cloning Utilities // ------------------------------------------------------------------------------- namespace detail { // TODO: This function is still used by ram::Node::clone() because it hasn't been // converted to return Own<>. Once converted, remove this. template Own downCast(B* ptr) { // ensure the clone operation casts to appropriate pointer static_assert(std::is_base_of_v, std::remove_const_t>, "Needs to be able to downcast"); return Own(ptr); } template Own downCast(Own ptr) { // ensure the clone operation casts to appropriate pointer static_assert(std::is_base_of_v, std::remove_const_t>, "Needs to be able to downcast"); return Own(static_cast(ptr.release())); } } // namespace detail template std::enable_if_t && !is_range_v, Own> clone(const A& node) { return detail::downCast(node.cloneImpl()); } template Own clone(const A* node) { return node ? clone(*node) : nullptr; } template Own clone(const Own& node) { return clone(node.get()); } template auto clone(const std::map& xs) { std::map()))> ys; for (auto&& [k, v] : xs) ys.insert({k, clone(v)}); return ys; } /** * Clone a range */ template auto cloneRange(R const& range) { return makeTransformRange(std::begin(range), std::end(range), [](auto const& x) { return clone(x); }); } /** * Clone a range, optionally allowing up-casting the result to D */ template , void*> = nullptr> auto clone(R const& range) { auto rn = cloneRange(range); using ValueType = remove_cvref_t; using ResType = std::conditional_t, ValueType, D>; return VecOwn(rn.begin(), rn.end()); } template auto clone(const std::pair& p) { return std::make_pair(clone(p.first), clone(p.second)); } // ------------------------------------------------------------------------------- // Comparison Utilities // ------------------------------------------------------------------------------- /** * Compares two values referenced by a pointer where the case where both * pointers are null is also considered equivalent. */ template bool equal_ptr(const T* a, const T* b) { if (a == nullptr && b == nullptr) { return true; } if (a != nullptr && b != nullptr) { return *a == *b; } return false; } /** * Compares two values referenced by a pointer where the case where both * pointers are null is also considered equivalent. */ template bool equal_ptr(const Own& a, const Own& b) { return equal_ptr(a.get(), b.get()); } // ------------------------------------------------------------------------------- // Error Utilities // ------------------------------------------------------------------------------- template [[noreturn]] void fatal(const char* format, const Args&... args) { tfm::format(std::cerr, format, args...); std::cerr << "\n"; assert(false && "fatal error; see std err"); abort(); } // HACK: Workaround to suppress spurious reachability warnings. #define UNREACHABLE_BAD_CASE_ANALYSIS fatal("unhandled switch branch"); // ------------------------------------------------------------------------------- // Other Utilities // ------------------------------------------------------------------------------- template auto lazy(F f) { using A = decltype(f()); return [cache = std::optional{}, f = std::move(f)]() mutable -> A& { if (!cache) cache = f(); return *cache; }; } } // namespace souffle