blob: a6f04c1f274979249a7a0718ad04375d9d45581a [file] [log] [blame]
//===-- include/flang/Common/visit.h ----------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// common::visit() is a drop-in replacement for std::visit() that reduces both
// compiler build time and compiler execution time modestly, and reduces
// compiler build memory requirements significantly (overall & maximum).
// It does not require redefinition of std::variant<>.
//
// The C++ standard mandates that std::visit be O(1), but most variants are
// small and O(logN) is faster in practice to compile and execute, avoiding
// the need to build a dispatch table.
//
// Define FLANG_USE_STD_VISIT to avoid this code and make common::visit() an
// alias for ::std::visit().
//
//
// With GCC 9.3.0 on a Haswell x86 Ubuntu system, doing out-of-tree builds:
// Before:
// build:
// 6948.53user 212.48system 27:32.92elapsed 433%CPU
// (0avgtext+0avgdata 6429568maxresident)k
// 36181912inputs+8943720outputs (3613684major+97908699minor)pagefaults 0swaps
// execution of tests:
// 205.99user 26.05system 1:08.87elapsed 336%CPU
// (0avgtext+0avgdata 2671452maxresident)k
// 244432inputs+355464outputs (422major+8746468minor)pagefaults 0swaps
// After:
// build:
// 6651.91user 182.57system 25:15.73elapsed 450%CPU
// (0avgtext+0avgdata 6209296maxresident)k
// 17413480inputs+6376360outputs (1567210major+93068230minor)pagefaults 0swaps
// execution of tests:
// 201.42user 25.91system 1:04.68elapsed 351%CPU
// (0avgtext+0avgdata 2661424maxresident)k
// 238840inputs+295912outputs (428major+8489300minor)pagefaults 0swaps
#ifndef FORTRAN_COMMON_VISIT_H_
#define FORTRAN_COMMON_VISIT_H_
#include "flang/Runtime/api-attrs.h"
#include <type_traits>
#include <variant>
namespace Fortran::common {
namespace log2visit {
template <std::size_t LOW, std::size_t HIGH, typename RESULT, typename VISITOR,
typename... VARIANT>
inline RT_API_ATTRS RESULT Log2VisitHelper(
VISITOR &&visitor, std::size_t which, VARIANT &&...u) {
if constexpr (LOW == HIGH) {
return visitor(std::get<LOW>(std::forward<VARIANT>(u))...);
} else {
static constexpr std::size_t mid{(HIGH + LOW) / 2};
if (which <= mid) {
return Log2VisitHelper<LOW, mid, RESULT>(
std::forward<VISITOR>(visitor), which, std::forward<VARIANT>(u)...);
} else {
return Log2VisitHelper<(mid + 1), HIGH, RESULT>(
std::forward<VISITOR>(visitor), which, std::forward<VARIANT>(u)...);
}
}
}
template <typename VISITOR, typename... VARIANT>
inline RT_API_ATTRS auto visit(VISITOR &&visitor, VARIANT &&...u)
-> decltype(visitor(std::get<0>(std::forward<VARIANT>(u))...)) {
using Result = decltype(visitor(std::get<0>(std::forward<VARIANT>(u))...));
if constexpr (sizeof...(u) == 1) {
static constexpr std::size_t high{
(std::variant_size_v<std::decay_t<decltype(u)>> * ...) - 1};
return Log2VisitHelper<0, high, Result>(std::forward<VISITOR>(visitor),
u.index()..., std::forward<VARIANT>(u)...);
} else {
// TODO: figure out how to do multiple variant arguments
return ::std::visit(
std::forward<VISITOR>(visitor), std::forward<VARIANT>(u)...);
}
}
} // namespace log2visit
// Some versions of clang have bugs that cause compilation to hang
// on these templates. MSVC and older GCC versions may work but are
// not well tested. So enable only for GCC 9 and better.
#if __GNUC__ < 9
#define FLANG_USE_STD_VISIT
#endif
#ifdef FLANG_USE_STD_VISIT
using ::std::visit;
#else
using Fortran::common::log2visit::visit;
#endif
} // namespace Fortran::common
#endif // FORTRAN_COMMON_VISIT_H_